diff --git a/.gitattributes b/.gitattributes index d10c448..b7986da 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,10 +2,14 @@ * text=auto eol=lf *.sh text eol=lf -terminal-run text eol=lf -config_txt_tips text eol=lf +*.mjs text eol=lf +*.json text eol=lf *.md text eol=lf +# Stored with LF even though cmd.exe needs CRLF: install-wor.sh appends the CR when it +# writes the file into boot.wim, and a CRLF copy here would produce CRCRLF +*.cmd text eol=lf + # Binary assets *.png binary diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..5bf77e1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [Botspot, blackoutsecure] diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 5f8beae..3eb6c18 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,11 +1,20 @@ blank_issues_enabled: true contact_links: + - name: Report it upstream instead + url: https://github.com/Botspot/wor-flasher/issues/new/choose + about: If the bug is not specific to macOS support, the native windows, or the test suite, upstream needs it more than this fork does - name: Botspot Software Discord url: https://discord.gg/RXSTvaUvuu about: Questions and help with WoR-flasher itself + - name: Windows on Raspberry Discord + url: https://discord.gg/jQCpfVK + about: Real-time help with Windows on Raspberry, the operating system - name: Windows on Raspberry support url: https://worproject.com/contact about: Problems with Windows, the drivers, or the UEFI firmware rather than the flasher - name: Windows on Raspberry FAQ url: https://worproject.com/faq about: Covers the 3 GB RAM limit, supported Windows versions, and driver status + - name: Report a security issue privately + url: https://github.com/blackoutsecure/wor-flasher/security/advisories/new + about: Anything that should not be discussed in public. See SECURITY.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 096236b..a22b242 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -6,8 +6,9 @@ -- [ ] `bash -n install-wor.sh install-wor-gui.sh terminal-run tests/*.sh` passes +- [ ] `bash -n install-wor.sh install-wor-gui.sh install-wor-hook.sh tests/*.sh` passes - [ ] `./tests/run-tests.sh` passes, skips only expected host/display/container checks, or the reason is explained +- [ ] GUI walkthrough checked with `./tests/run-tests-gui.sh` on the intended host (`yad` on Linux or `osascript` plus a removable drive on macOS) - [ ] Tested with `DRY_RUN=1`, or flashed a real drive - [ ] Raspberry Pi model tested: - [ ] Windows build tested: @@ -15,5 +16,8 @@ ## Checklist - [ ] The GUI and CLI still agree, if shared logic changed +- [ ] Shared logic lives in `install-wor.sh`, not duplicated into `install-wor-gui.sh` - [ ] README updated, if behaviour or variables changed -- [ ] No new ShellCheck errors (`shellcheck --severity=error install-wor.sh install-wor-gui.sh terminal-run tests/*.sh`) +- [ ] Version history at the top of `install-wor.sh` and the Versions section of the README updated, if behaviour changed +- [ ] Sent upstream to Botspot/wor-flasher too, if the fix applies there +- [ ] No new ShellCheck errors (`shellcheck --severity=error install-wor.sh install-wor-gui.sh install-wor-hook.sh tests/*.sh`) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..806b8ac --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,267 @@ +name: Publish Release + +on: + push: + tags: + - "v*" + workflow_dispatch: + inputs: + tag_name: + description: "auto for the next patch, or an explicit vX.Y.Z tag" + default: "auto" + required: false + type: string + skip_project_version_update: + description: "Do not update project version files before validating a new manual tag" + default: false + required: false + type: boolean + +permissions: + contents: read + +concurrency: + group: release-${{ github.event_name == 'workflow_dispatch' && format('refs/tags/{0}', inputs.tag_name) || github.ref }} + cancel-in-progress: false + +jobs: + prepare: + name: Resolve release tag + runs-on: ubuntu-latest + permissions: + contents: write + outputs: + create_tag: ${{ steps.release.outputs.create_tag }} + ref: ${{ steps.release.outputs.ref }} + tag: ${{ steps.release.outputs.tag }} + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + fetch-depth: 0 + + - id: release + env: + EVENT_NAME: ${{ github.event_name }} + INPUT_TAG: ${{ inputs.tag_name }} + REF: ${{ github.ref }} + REF_NAME: ${{ github.ref_name }} + SKIP_PROJECT_VERSION_UPDATE: ${{ inputs.skip_project_version_update }} + SOURCE_SHA: ${{ github.sha }} + run: | + set -euo pipefail + if [ "$EVENT_NAME" = workflow_dispatch ];then + #Metadata is JSON-backed; the shell module no longer has a literal version assignment. + version="$(node -p 'require("./src/config/metadata.json").product.version')" + [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { + echo "Invalid version: $version" >&2 + exit 1 + } + if [ "$INPUT_TAG" = auto ];then + latest_tag='' + while IFS= read -r candidate ;do + if [[ "$candidate" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]];then + latest_tag="$candidate" + break + fi + done < <(git tag --list 'v*' --sort=-version:refname) + if [ -n "$latest_tag" ];then + IFS=. read -r major minor patch <<< "${latest_tag#v}" + tag="v$major.$minor.$((10#$patch + 1))" + else + tag="v$version" + fi + else + tag="$INPUT_TAG" + fi + if git rev-parse --verify --quiet "refs/tags/$tag" >/dev/null ;then + ref="refs/tags/$tag" + create_tag=false + else + if [ "$SKIP_PROJECT_VERSION_UPDATE" != true ];then + node src/set-version.mjs "${tag#v}" + if [ -n "$(git status --porcelain -- src/config/metadata.json src/lib/metadata.sh src/macos-app/Contents/Info.plist README.md install-wor.sh package.json)" ];then + source_branch="${REF#refs/heads/}" + [ "$source_branch" != "$REF" ] || { + echo "Manual releases must run from a branch." >&2 + exit 1 + } + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add src/config/metadata.json src/lib/metadata.sh src/macos-app/Contents/Info.plist README.md install-wor.sh package.json + git commit -m "chore: prepare $tag" \ + -m "Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" + git push origin "HEAD:$source_branch" + fi + fi + SOURCE_SHA="$(git rev-parse HEAD)" + ref="$SOURCE_SHA" + create_tag=true + fi + else + tag="$REF_NAME" + ref="$REF" + create_tag=false + fi + [[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || { + echo "Release tag must be vX.Y.Z (got: $tag)." >&2 + exit 1 + } + { + echo "create_tag=$create_tag" + echo "ref=$ref" + echo "tag=$tag" + } >> "$GITHUB_OUTPUT" + + validate-linux: + name: Validate Linux release candidate + needs: prepare + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + fetch-depth: 0 + ref: ${{ needs.prepare.outputs.ref }} + + - name: Verify tag matches the release metadata + env: + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} + run: | + set -euo pipefail + version="$(node -p 'require("./src/config/metadata.json").product.version')" + [ -n "$version" ] || { echo 'WOR_FLASHER_VERSION is missing.' >&2; exit 1; } + [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || { echo "Invalid version: $version" >&2; exit 1; } + [ "$RELEASE_TAG" = "v$version" ] || { echo "Tag $RELEASE_TAG does not match v$version." >&2; exit 1; } + + - name: Install ShellCheck and integration test prerequisites + run: | + set -euo pipefail + sudo apt-get update -qq + sudo apt-get install -y -qq shellcheck pv + + - name: Check shell syntax + run: bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh + + - name: Check ShellCheck errors + run: shellcheck --severity=error src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh + + - name: Verify packaged macOS runtime + run: npm run build:macos && node src/package-macos-app.mjs --check + + - name: Verify release packaging plan + run: npm run check + + - name: Run Linux test suite + env: + TEST_MODELS: "4" + TEST_COMMAND_TIMEOUT: "120" + run: ./tests/run-tests.sh + + validate-macos: + name: Validate macOS release candidate + needs: prepare + runs-on: macos-latest + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + ref: ${{ needs.prepare.outputs.ref }} + + - name: Check shell syntax + run: /bin/bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh + + - name: Verify packaged macOS runtime + run: npm run build:macos && node src/package-macos-app.mjs --check + + - name: Verify release packaging plan + run: npm run check + + - name: Run macOS test suite + run: ./tests/run-tests.sh + + publish: + name: Package and publish release + needs: + - prepare + - validate-linux + - validate-macos + runs-on: macos-latest + permissions: + contents: write + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + with: + fetch-depth: 0 + ref: ${{ needs.prepare.outputs.ref }} + + - name: Package release artifacts + id: package + shell: bash + env: + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} + run: | + set -euo pipefail + version="$(node -p 'require("./src/config/metadata.json").product.version')" + archive_dir="$RUNNER_TEMP/release-artifacts" + release_commit="$(git rev-parse HEAD)" + mkdir -p "$archive_dir" + npm run package:all + ditto -c -k --keepParent release/linux/wor-flasher "$archive_dir/wor-flasher-$version-linux-rpi.zip" + ditto -c -k --keepParent release/macos/WoR-Flasher.app "$archive_dir/WoR-Flasher-$version-macos.zip" + tar -czf "$archive_dir/macos-runtime-$version.tar.gz" \ + -C release/macos/WoR-Flasher.app/Contents/Resources runtime runtime-manifest.json + runtime_digest="$(shasum -a 256 "$archive_dir/macos-runtime-$version.tar.gz" | awk '{print $1}')" + printf '{\n "version": "%s",\n "url": "https://github.com/%s/releases/download/%s/macos-runtime-%s.tar.gz",\n "sha256": "%s"\n}\n' \ + "$version" "$GITHUB_REPOSITORY" "$RELEASE_TAG" "$version" "$runtime_digest" > "$archive_dir/macos-runtime.json" + ( + cd "$archive_dir" + shasum -a 256 wor-flasher-"$version"-linux-rpi.zip WoR-Flasher-"$version"-macos.zip \ + macos-runtime-"$version".tar.gz macos-runtime.json > SHA256SUMS + ) + { + echo "directory=$archive_dir" + echo "commit=$release_commit" + } >> "$GITHUB_OUTPUT" + + - name: Create immutable release tag + if: needs.prepare.outputs.create_tag == 'true' + env: + RELEASE_TAG: ${{ needs.prepare.outputs.tag }} + run: | + set -euo pipefail + git ls-remote --exit-code --tags origin "refs/tags/$RELEASE_TAG" >/dev/null && { + echo "Tag already exists: $RELEASE_TAG" >&2 + exit 1 + } + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git tag -a "$RELEASE_TAG" -m "Release $RELEASE_TAG" HEAD + git push origin "refs/tags/$RELEASE_TAG" + + - name: Publish GitHub Release + uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + with: + tag_name: ${{ needs.prepare.outputs.tag }} + target_commitish: ${{ steps.package.outputs.commit }} + generate_release_notes: true + body: | + `wor-flasher-*-linux-rpi.zip` is the portable Bash distribution for Debian, Ubuntu, + Raspberry Pi OS, and other supported Linux hosts. It includes the runtime source, + README, LICENSE, and NOTICE. + + The `WoR-Flasher-*-macos.zip` application bundle is unsigned and unnotarized. + `macos-runtime.json` and its versioned runtime archive are consumed only by the macOS + launcher's verified runtime updater. + + Verify downloaded artifacts against `SHA256SUMS` before use. + + macOS GUI disk writes use one administrator authentication and an already-authorized + finalizer. Automatic Ignore handles only the exact unreadable-disk system alert + during active writes; Accessibility and Automation permission may be required. + Physical-media completion and Raspberry Pi boot compatibility are not established + by the automated tests. See README and NOTICE for limitations and attribution. + files: | + ${{ steps.package.outputs.directory }}/wor-flasher-*-linux-rpi.zip + ${{ steps.package.outputs.directory }}/WoR-Flasher-*-macos.zip + ${{ steps.package.outputs.directory }}/macos-runtime-*.tar.gz + ${{ steps.package.outputs.directory }}/macos-runtime.json + ${{ steps.package.outputs.directory }}/SHA256SUMS + fail_on_unmatched_files: true diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index 8eea0e5..3698271 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -4,13 +4,31 @@ on: push: paths: - "**.sh" - - "terminal-run" + - "**.js" + - "**.mjs" + - "package.json" + - "src/config/**" + - "assets/**" + - "config-templates/**" + - "src/macos-app/**" + - "src/*.mjs" + - "src/lib/*.mjs" + - ".github/workflows/release.yml" - ".github/workflows/shellcheck.yml" - "README.md" pull_request: paths: - "**.sh" - - "terminal-run" + - "**.js" + - "**.mjs" + - "package.json" + - "src/config/**" + - "assets/**" + - "config-templates/**" + - "src/macos-app/**" + - "src/*.mjs" + - "src/lib/*.mjs" + - ".github/workflows/release.yml" - ".github/workflows/shellcheck.yml" - "README.md" workflow_dispatch: @@ -39,14 +57,39 @@ jobs: sudo apt-get install -y -qq shellcheck - name: Check shell syntax - run: bash -n install-wor.sh install-wor-gui.sh terminal-run tests/*.sh + run: bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh - name: Check for errors - run: shellcheck --severity=error install-wor.sh install-wor-gui.sh terminal-run tests/*.sh + run: shellcheck --severity=error src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh - name: Report warnings continue-on-error: true - run: shellcheck --severity=warning install-wor.sh install-wor-gui.sh terminal-run tests/*.sh + run: shellcheck --severity=warning src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh + + - name: Verify packaged macOS runtime + run: npm run build:macos && node src/package-macos-app.mjs --check + + - name: Verify Node release tooling + run: npm run check + + macos-syntax: + runs-on: macos-latest + steps: + - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 + + - name: Check syntax with the macOS system Bash + run: /bin/bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh + + - name: Verify packaged macOS runtime + run: npm run build:macos && node src/package-macos-app.mjs --check + + - name: Verify Node release tooling + run: npm run check + + # bash -n accepts a mis-parsed heredoc, so the suite must also run here to catch + # structural breakage in the macOS-only JXA code paths + - name: Run the test suite on macOS + run: ./tests/run-tests.sh dry-run-integration: runs-on: ubuntu-latest @@ -56,6 +99,12 @@ jobs: with: fetch-depth: 0 + #The progress-helper test runs before the engine installs its own dependencies. + - name: Install integration test prerequisites + run: | + sudo apt-get update -qq + sudo apt-get install -y -qq pv + - name: Run one-model dry-run integration suite env: TEST_MODELS: ${{ inputs.test_models || '4' }} diff --git a/.gitignore b/.gitignore index 584feef..9211817 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,21 @@ cache/ wget-log* +# Run logs. WOR_LOG_FILE defaults under DL_DIR/logs, but that can be pointed anywhere +*.log + # Working directory for tests/run-tests.sh: loopback disk images and downloads .test-workspace/ +# Generated release staging output +release/ + +# User local configuration +config.json +*.config.json +!config-templates/*.config.json +!config-templates/*.json + # Downloaded components, if DL_DIR is pointed inside the repo peinstaller/ driverpackage/ diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9e74369 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,239 @@ +# AGENTS.md + +## What this is + +WoR-Flasher is a Bash-based tool for creating bootable Windows 10 or Windows 11 +ARM64 media for Raspberry Pi from Debian-based Linux or macOS. It automates the +Windows on Raspberry installation process: obtain or import Windows media, add +UEFI firmware and drivers, partition and write a selected drive, verify the +result, and eject it. + +Botspot created WoR-Flasher and also created Pi-Apps. Preserve the project's +original priorities: a straightforward interactive workflow, readable Bash, +community compatibility, and one implementation of the flashing logic. The +Blackout Secure maintained source extends that direction with macOS support, +native GUI paths, stronger validation and verification, caching, automation +interfaces, and broader tests. Do not erase the original authorship or replace +the established design with a new framework. + +Read `README.md` for supported hosts, Raspberry Pi models, user-facing behavior, +and parameters. Read `CONTRIBUTING.md` before changing code, `SECURITY.md` before +changing trust or validation behavior, and `NOTICE` before changing licensing or +attribution text. + +## Commands + +Run the narrowest applicable check first. The repository has no compilation step. + +```bash +# Syntax and ShellCheck for all maintained Bash entry points, libraries, and tests. +# These match the targets validated by CI (.github/workflows/shellcheck.yml). +bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh +shellcheck --severity=error src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh + +# Release tooling validation and artifact staging cleanup +npm run check +npm run clean + +# Full automated suite; does not write physical media +./tests/run-tests.sh + +# GUI flow in DRY_RUN mode, or Linux loop-device coverage from macOS via Docker +./tests/run-tests.sh --gui +./tests/run-tests-gui.sh +./tests/run-linux-integration.sh + +# Remove loop devices and test state left by an interrupted run +./tests/run-tests.sh --clean +``` + +On a non-Linux host the suite prints three summaries: the Docker container's nested run, +the integration wrapper, then this host's own run. All three must report `failed 0`, and +the process exit status must be `0`. Read the summaries rather than the exit status alone +when you change the harness itself. + +Never test a code change by flashing a physical device without the user's explicit +approval and selected target. For partitioning, firmware, driver, boot, or model +compatibility changes, record real hardware coverage separately when available. + +## Architecture + +- `install-wor.sh` is the engine and the single source of truth. Drive discovery, + host and target validation, downloads, caching, ISO handling, partitioning, + writing, settings summaries, and post-flash verification belong here. +- `install-wor-gui.sh` is presentation only. It sources the engine and presents + native AppKit/JXA dialogs on macOS or `yad` dialogs on Linux. Do not duplicate + engine functions or decisions in this file. +- Keep both GUI implementations behaviorally aligned. A setting added to one + platform's Advanced Options UI must be added to the other platform and to the + shared `settings_summary` in the same change. +- `install-wor-hook.sh` is the stable, machine-readable process adapter for + external front-ends and automation. Keep commands small, preserve exit-status + behavior, and cover interface changes with tests. It may bootstrap a complete + checkout because the engine depends on repository assets. +- `src/lib/` holds the shared libraries every entry point loads explicitly: + `metadata.sh` owns the canonical name, version, and asset filenames; `dependencies.sh`, + `paths.sh`, and `cleanup.sh` own the dependency, path, and cleanup helpers. These files + must stay safe to source and must not source one another. +- `assets/` holds the shared UI artwork used by the README, both GUIs, and the macOS app + icon. Reference it through `WOR_ASSETS_DIR` rather than rebuilding the path. +- Both front-ends run the engine in-process and report progress through a native + progress window. Do not reintroduce a spawned terminal emulator or launcher + detection; the GUI must not depend on a visible terminal on either platform. +- `src/macos-app/` holds the canonical macOS application bundle template (`Info.plist`, + launcher executable, icons, and logo). The checked-in root `WoR-Flasher.app` is deleted; + staged bundles are written to `release/macos/WoR-Flasher.app` by Node release tooling + (`npm run build`). +- Node release tooling (`package.json`, `src/build-release.mjs`, `src/lib/node-runtime.mjs`) + stages deterministic release artifacts into `release/` (ignored by Git and cleaned with + `npm run clean`). `src/config/metadata.json` owns the explicit `runtimePaths` list of files + shipped in release artifacts, plus the runtime/system defaults loaded by `src/lib/metadata.sh`. +- `config-templates/` contains required boot, setup inputs, and JSON configuration schema/template files (`config.schema.json`, `config.json`). Keep these files + tracked and validate changes carefully; a missing or empty template can produce + unbootable media. +- `tests/` contains static, shared-engine, dry-run, loop-device, GUI, and Docker + integration coverage. Tests should call real engine functions through the test + helpers rather than reimplement product logic. + +## Safety invariants + +This project erases storage devices. Treat target selection and verification as +safety-critical code. + +- Preserve the final host, whole-disk, boot-drive, capacity, and installation-mode + checks in the engine. Discovery output is a snapshot, not authorization to write. +- Never weaken target-drive exclusions or make an unsafe device selectable merely + to simplify a UI or integration. +- Keep destructive operations behind explicit user confirmation unless the caller + has deliberately supplied the documented non-interactive inputs. +- Preserve TLS and payload integrity checks, cache manifests, source-image checks, + and post-flash partition, file, and image verification. Any bypass must remain + explicit, documented, and disabled by default. +- Do not log passwords, tokens, unattended-setup credentials, or other secrets. + Keep credential-bearing files limited to the explicitly enabled setup flow. +- Preserve registered cleanup for mounts, loop devices, temporary files, and + interrupted runs. Do not replace an existing `EXIT` trap in a way that drops + earlier cleanup handlers. +- The tool must never rewrite its own installation. The engine and the launcher perform a + read-only release check and nothing more: no `git fetch`, `merge`, `pull`, or self-replacing + update, on any platform. A half-updated disk flasher is more dangerous than an out-of-date + one. The single exception is the macOS app's verified runtime updater, which installs only + a digest-verified release archive into a staged runtime store with rollback. +- WSL is deliberately rejected because its visible disks are not safe flashing + targets. Do not broaden host support without a tested device-safety design. +- Never run a real flash against physical media during automated validation. Use + `DRY_RUN=1`, sparse files, loop devices, mocks, or the Docker integration suite. + A real-device test requires the user's explicit approval and device selection. + +## Platform rules + +- Keep Linux and macOS behavior separate only where host tooling requires it; put + shared decisions in the engine. +- Use the existing host helpers instead of inferring the platform from `DISPLAY` + or duplicating `uname` checks. The front-end is selected explicitly. +- Linux support targets Debian-family hosts and uses Linux block-device and mount + tooling. Loop-device tests are expected to be unavailable on macOS. +- macOS support targets macOS 13 or newer with Homebrew. Preserve `diskutil`/ + `hdiutil` discovery and mounting behavior and `sgdisk` GPT partitioning. The + `WOR_BOOT` EFI partition must remain partition 1. +- The macOS GUI uses JXA inside quoted heredocs and shell command substitutions. + Do not put apostrophes in those JXA heredoc bodies; an unmatched quote can hide + later shell definitions while `bash -n` still succeeds. Use wording such as + "does not" instead of "doesn't". +- The macOS application bundle is a launcher for the same maintained scripts, not + a separate implementation. Preserve the complete-checkout execution model and + its bounded preflight: repair only missing tracked files from the local revision, + and install only missing Homebrew formulae. It must not update a source checkout + from any remote. Never add `git fetch`, `git merge`, `git reset`, `git clean`, + `brew upgrade`, or a remote script piped into a shell. +- Model and firmware compatibility changes require authoritative upstream evidence + plus tests. Keep known pins and model restrictions unless the relevant upstream + issue is resolved and the replacement is tested on applicable hardware. + +## Bash conventions + +Follow the established Botspot style in nearby code rather than applying a generic +formatter: + +- Use `#comment` with no space after `#`. +- Use the existing conditional form, for example `if [ "$value" == expected ];then`. +- Put the concise `#Input: ... Output: ...` description on a function's opening + line where that pattern is used. +- Comment why a non-obvious choice exists; do not narrate obvious commands. +- Quote paths and expansions unless intentional word splitting is required. +- Reuse existing helpers for errors, progress, cleanup, downloads, platform + behavior, and device handling before adding another abstraction. +- Keep changes focused. Do not perform broad style rewrites alongside behavioral + work. + +Use the established local pattern rather than introducing a generic shell style: + +```bash +get_partition() { #Input: device & partition number. Output: partition /dev entry + [ -z "$1" ] && error "get_partition(): no /dev device specified as"' $1' + [ -z "$2" ] && error "get_partition(): no partition number specified as"' $2' + [ ! -b "$1" ] && error "get_partition(): $1 is not a valid block device!" + if is_macos ;then + [ "$2" == all ] && error "get_partition(): macOS does not support listing all partitions through this helper." + darwin_plist_json diskutil list -plist "$1" | jq -er ".AllDisksAndPartitions[0].Partitions[$(($2 - 1))].DeviceIdentifier" | sed 's+^+/dev/+' + return + fi + command -v lsblk >/dev/null || error "get_partition(): lsblk is required." +} +``` + +Two Bash-specific hazards are documented in `CONTRIBUTING.md`: do not edit a script +while it is running, because Bash reads incrementally by byte offset, and do not +try to `wait` for a sibling process from a subshell. + +## Git workflow + +This checkout is the Blackout Secure maintained fork. `origin` is +`blackoutsecure/wor-flasher`, whose default branch is `main`; `upstream` is +Botspot's original `Botspot/wor-flasher`, tracked so original work can be merged +in and changes can be offered back. The working branch is `patch-1`. + +- Land changes through a pull request against `blackoutsecure/wor-flasher`, as + `CONTRIBUTING.md` directs. Do not push straight to `main`. +- One logical change per pull request, with original attribution preserved. +- Say what you tested it on, and be honest about what you could not test. +- Keep the README, the `install-wor.sh` version history, and any release metadata + in the same commit as the behavior change they describe. + +## Boundaries + +### Always + +- Keep `install-wor.sh` as the shared decision-making engine and test behavior + through its real functions rather than reimplementing it in tests. +- Run the relevant commands above, mutation-test new coverage, and report what + was tested and what could not be tested. +- Update `README.md` and the version history at the top of `install-wor.sh` with + user-visible behavior changes. For a release, update `WOR_FLASHER_VERSION` in + `src/lib/metadata.sh` and both version fields in + `src/macos-app/Contents/Info.plist`; the suite checks all release metadata. +- Preserve Botspot's original authorship, contributor credit, project links, and + community support paths. + +### Ask first + +- Changing target-device safety checks, integrity verification, default bypasses, + supported hosts, or Raspberry Pi and firmware compatibility. +- Adding or changing a public hook command, its output, or its exit-status contract. +- Adding dependencies, modifying the macOS app bundle's update/repair behavior, + or changing where canonical metadata is sourced. +- Editing licensing, attribution, third-party component notices, or generated + configuration templates. + +### Never + +- Remove, bypass, or weaken boot-drive protection, final validation, integrity + checks, credential redaction, cleanup registration, or the deliberate WSL block. +- Duplicate engine logic in a GUI or make the Linux and macOS Advanced Options + flows diverge. +- Add apostrophes inside the quoted JXA heredocs, edit a running Bash script, or + use destructive repair/update commands such as `git reset`, `git clean`, or + `brew upgrade` in the macOS launcher. +- Commit generated Windows media, downloaded firmware or drivers, caches, logs, + credentials, mounted-image contents, or test workspaces. +- State that the upstream licensing position is settled; follow `NOTICE` instead. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..9d320cf --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Code of Conduct + +## The short version + +This project exists because a college student wanted to run Windows on a Raspberry Pi +and decided to share how. Behave like someone worth sharing with. + +## Our pledge + +We as members, contributors and maintainers pledge to make participation in this +project a harassment-free experience for everyone, regardless of age, body size, +visible or invisible disability, ethnicity, sex characteristics, gender identity and +expression, level of experience, education, socio-economic status, nationality, +personal appearance, race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, +inclusive and healthy community. + +## What we expect + +- **Assume good faith.** Most people asking an "obvious" question are not lazy; they + are new. The Raspberry Pi community is largely how people get started with Linux. +- **Be patient with beginners.** Someone flashing their first SD card is exactly who + this tool is for. +- **Keep criticism on the code.** "This function does not handle X" is useful. + "This is garbage" is not. +- **Respect the upstream author.** Botspot wrote and gave away the thing you are using. + Disagree with a design decision all you like; do so civilly. +- **Accept a "no".** Maintainers get to decide what goes in. Not every patch lands, and + that is not a personal slight. + +## What is not acceptable + +- Sexualised language or imagery, and sexual attention or advances of any kind. +- Trolling, insulting or derogatory comments, and personal or political attacks. +- Public or private harassment. +- Publishing others' private information, such as a physical or email address, without + their explicit permission. +- Demanding free support as though it were owed to you. Nobody here is paid. +- Other conduct which could reasonably be considered inappropriate in a professional + setting. + +## Scope + +This Code of Conduct applies within all project spaces — the repository, issues, pull +requests and discussions — and also when an individual is officially representing the +project in public spaces. + +Conversations that happen on the [Botspot Software Discord][botspot-discord] or the +[WoR project Discord][wor-discord] are governed by those servers' own rules. This +document does not override them. + +## Enforcement + +Report unacceptable behaviour to the maintainers of this fork at +[blackoutsecure.app][bos] or by opening a confidential report through +[GitHub's private reporting][report]. All complaints will be reviewed and investigated +promptly and fairly. Maintainers are obligated to respect the privacy and security of +the reporter. + +Maintainers may take any action they deem appropriate, including a warning, a temporary +ban, or a permanent ban from the project's spaces. + +If the report concerns conduct in the upstream project, please raise it with +[Botspot][upstream] as well; this fork has no authority there. + +## Attribution + +Adapted from the [Contributor Covenant][covenant], version 2.1. + +[covenant]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[botspot-discord]: https://discord.gg/RXSTvaUvuu +[wor-discord]: https://discord.gg/jQCpfVK +[upstream]: https://github.com/Botspot/wor-flasher +[bos]: https://blackoutsecure.app +[report]: https://github.com/blackoutsecure/wor-flasher/security/advisories/new diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..bb62ca7 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,130 @@ +# Contributing + +Thanks for wanting to help. Blackout Secure directly maintains this source while +preserving Botspot's original authorship and the project's community connections. + +## Table of contents + +- [Where to send your change](#where-to-send-your-change) +- [Getting set up](#getting-set-up) +- [Running the tests](#running-the-tests) +- [House style](#house-style) +- [Traps that have bitten us](#traps-that-have-bitten-us) +- [Opening a pull request](#opening-a-pull-request) +- [Reporting a bug](#reporting-a-bug) + +## Where to send your change + +Open bug fixes, features and documentation changes at [Botspot/wor-flasher][issues]. +Keep changes focused, preserve original attribution and explain user-visible behavior +in the pull request. + +## Getting set up + +```bash +git clone https://github.com/Botspot/wor-flasher +cd wor-flasher +``` + +You need `bash`, `shellcheck`, and Node.js 20 or newer for release packaging. For the +Linux integration suite on a non-Linux host, you also need Docker. Everything else the +scripts install for themselves. + +## Running the tests + +```bash +./tests/run-tests.sh # static checks, plus Linux integration where available +./tests/run-tests.sh --gui # walk through the GUI in DRY_RUN mode +./tests/run-linux-integration.sh # force the Dockerised Linux suite +npm run check # shell syntax and an isolated release-package build +``` + +The suite must be green and ShellCheck must be error-clean before a pull request can +be merged. CI runs the same commands on Ubuntu and macOS. + +Two things to know about the harness: + +- On macOS only the **Static checks** and **Shared engine functions** sections run. + Everything else is gated behind `command -v losetup`. A test added elsewhere will + not execute on macOS — validate it with `run-linux-integration.sh`. +- On a non-Linux host the run prints three summaries: the Docker container's nested + run, the integration wrapper, then the host's own run. Every one must report + `failed 0`. A failing host-side check is reported in its own summary, so do not + read the container summary alone. +- Tests call the real functions out of `install-wor.sh` through the `run_in_engine` + helper. Please do not restate product logic inside a test; a test that reimplements + what it is checking can pass against code that no longer works. + +**Mutation-test anything you add.** Break the behaviour on purpose, confirm your new +test fails, then put it back. A test that never fails is not a test. + +## House style + +The scripts follow Botspot's existing conventions, not a general shell style guide: + +- `#comment` with no space after the hash. +- `if [ "$x" == y ];then` — no space before `;then`. +- Functions carry a one-line `#Input: ... Output: ...` comment on the same line as the + opening brace. +- Comments explain _why_, not _what_. If the code already says it, do not repeat it. + +Two structural rules matter more than any of that: + +1. **`install-wor.sh` is the engine. `install-wor-gui.sh` is only windows.** Anything + that decides _what gets written to the drive_ — validation, drive discovery, + downloads, partitioning, the settings summary — belongs in `install-wor.sh` and is + used by both front-ends. The GUI sources it. A function defined in both files is a + bug, and there is a test that fails on it. +2. **The two front-ends must not diverge.** If you add a setting to the macOS Advanced + Options window, add it to the Linux yad form and to `settings_summary` in the same + commit. +3. **`install-wor-hook.sh` is the external process adapter, not an internal GUI layer.** + Keep the built-in GUI on the shared engine functions it needs to construct its forms. + Keep hook commands small, machine-readable and behaviorally covered by the test suite. + +## Traps that have bitten us + +Please read these before touching the relevant code. Each one cost real debugging time. + +- **No apostrophes in the JXA heredocs.** The `<<'JXA'` heredocs live inside a + `"$( ... )"` command substitution, so bash still tracks quote state through the body. + One unpaired `'` — a comment saying "doesn't" — silently swallows the next several + hundred lines and four function definitions. `bash -n` still passes. Write "does not". +- **`bash -n` is not enough.** Because of the above, the test suite runs on macOS in CI + specifically to catch structural breakage in the JXA paths. +- **Never edit a script while it is running.** Bash reads scripts incrementally by byte + offset. Editing `tests/run-tests.sh` mid-run corrupts execution and produces nonsense + failures at valid lines. +- **`config-templates/` must stay tracked.** A missing template used to produce a blank + `config.txt` and an unbootable Pi, with only a warning in a GUI that has no terminal. +- **A subshell cannot `wait` on a sibling.** Let the background job record its own exit + status; do not wrap it in `( wait "$pid"; ... ) &`. + +## Opening a pull request + +- One logical change per pull request. +- Say what you tested it on. "Flashed a Pi 4 from Debian Bookworm to a 64 GB SanDisk" + is worth more than any amount of review. +- If you changed behaviour, update the README and the version history at the top of + `install-wor.sh` in the same commit. +- For a release, update `WOR_FLASHER_VERSION` in `src/lib/metadata.sh` and both version + fields in `src/macos-app/Contents/Info.plist`; the test suite checks all release metadata. +- Be honest about what you did not test. Nobody has every Pi model. + +## Reporting a bug + +Use the [issue templates][issues]. Include: + +- Host OS and version, and Raspberry Pi model. +- The exact command or GUI choices you used. +- The timestamped log shown in the failure dialog, or `$DL_DIR/last-run.log` as the latest-run + shortcut. Set `WOR_LOG_FILE` to put it somewhere else. +- What you expected, and what happened instead. + +For real-time help, the [Botspot Software Discord][botspot-discord] is the best place +for WoR-Flasher itself, and the [WoR project Discord][wor-discord] is the place for +questions about Windows on Raspberry as an operating system. + +[issues]: https://github.com/Botspot/wor-flasher/issues/new/choose +[botspot-discord]: https://discord.gg/RXSTvaUvuu +[wor-discord]: https://discord.gg/jQCpfVK diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e62ec04 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..b7a9d9c --- /dev/null +++ b/NOTICE @@ -0,0 +1,50 @@ +WoR-Flasher +Copyright (C) 2021-2026 Botspot and the WoR-Flasher contributors +Copyright (C) 2026 Blackout Secure + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation, either version 3 of the License, or (at your option) any later +version. The full text is in the LICENSE file. + +## Upstream licensing status + +As of 2026-09-04, the upstream project at https://github.com/Botspot/wor-flasher +ships no LICENSE file and the GitHub API reports its license as null. Botspot's +companion project BVM (https://github.com/Botspot/bvm) is released under +GPL-3.0, so GPL-3.0 is what this fork applies, on the understanding that it +matches the author's intent for his other work in this space. + +Read that as a statement of intent, not as settled fact. Only Botspot can +license the upstream code he wrote. Until he adds a LICENSE file upstream, or +confirms GPL-3.0 in writing: + +- The Blackout Secure additions listed in the version history at the top of + install-wor.sh are offered under GPL-3.0 without reservation. +- The pre-existing upstream code carries no explicit grant. Anyone planning to + redistribute this fork commercially, or to relicense it, should get that + confirmation first. + +If you are Botspot and GPL-3.0 is not what you want here, please open an issue +on https://github.com/Botspot/wor-flasher and it will be changed. + +## Third-party components + +This project downloads, but does not redistribute, the following: + +- Windows 10/11 ARM64 installation media, retrieved directly from Microsoft's + own update servers via uupdump (https://uupdump.net). No proprietary + Microsoft material is included in this repository. The resulting install is + unlicensed, exactly like a retail Windows ISO, and must be activated with a + key or a pre-licensed Microsoft account. +- Windows on Raspberry (WoR) PE-based installer, by the WoR project + (https://worproject.com). +- Raspberry Pi UEFI firmware, by the Pi Firmware Task Force + (https://github.com/pftf). +- Windows ARM64 drivers for the Raspberry Pi, by the WoR project + (https://github.com/worproject/RPi-Windows-Drivers). + +Each of those is covered by its own license, held by its own authors. + +The config.txt bodies in config-templates/ are derived from the Pi Firmware Task +Force reference configuration (https://github.com/pftf/RPi4/blob/master/config.txt). diff --git a/README.md b/README.md index c66374a..a5c8abb 100644 --- a/README.md +++ b/README.md @@ -1,795 +1,732 @@ -# ![app icon](https://github.com/Botspot/wor-flasher/blob/main/logo.png?raw=true) WoR-flasher +Available on Pi-Apps -**Use a Linux machine to install Windows 10 or Windows 11 on a Raspberry Pi SD card.** +# ![WoR-Flasher logo](assets/logo-full.png) WoR-Flasher -> [!IMPORTANT] -> **This repository is looking for a maintainer.** -> I don't use wor-flasher personally, and I don't have the time to maintain it. If you use wor-flasher, and you can read and understand the script, please contact me somehow and I can grant you write privileges to the repository. +![Maintainer partnership banner](assets/partnership.png) + +[![Version](https://img.shields.io/badge/version-2.0.0-0a7ea4?style=for-the-badge&labelColor=555555&logo=semanticrelease&logoColor=ffffff)](#versions) +[![CI](https://img.shields.io/github/actions/workflow/status/blackoutsecure/wor-flasher/shellcheck.yml?style=for-the-badge&labelColor=555555&logo=githubactions&logoColor=ffffff&color=0a7ea4&label=CI)](https://github.com/blackoutsecure/wor-flasher/actions/workflows/shellcheck.yml) +[![License](https://img.shields.io/badge/license-GPL--3.0-0a7ea4?style=for-the-badge&labelColor=555555&logo=gnu&logoColor=ffffff)](LICENSE) +[![Platform](https://img.shields.io/badge/host-Linux%20%7C%20macOS-0a7ea4?style=for-the-badge&labelColor=555555&logo=linux&logoColor=ffffff)](#requirements) +[![Shell](https://img.shields.io/badge/written%20in-bash-0a7ea4?style=for-the-badge&labelColor=555555&logo=gnubash&logoColor=ffffff)](install-wor.sh) -> [!TIP] -> **Consider [the BVM project](https://github.com/Botspot/bvm) instead, especially on a Pi 4 or older.** -> -> BVM runs Windows 11 ARM64 in a KVM virtual machine alongside Linux instead of replacing it. Because networking, audio, and USB are passed through from the host, Wi-Fi and sound work out of the box - neither of which bare-metal Windows on a Raspberry Pi supports today. Microsoft's Prism emulator also lets x86 and x64 applications run. -> -> It does **not** lift the Windows version ceiling. On hardware without ARMv8.1, such as the Pi 4 and older, BVM pins Windows 11 to build `22631.2861` for exactly the same CPU reason described in [The ARMv8.1 limitation](#the-armv81-limitation). No tool can work around that, since it is a property of the processor. BVM requires an ARM64 Linux with the `kvm` kernel module. +[![Discord](https://img.shields.io/badge/Discord-Botspot%20Software-5865F2?style=for-the-badge&labelColor=555555&logo=discord&logoColor=ffffff)](https://discord.gg/RXSTvaUvuu) +[![Sponsor](https://img.shields.io/badge/sponsor-Botspot-EA4AAA?style=for-the-badge&labelColor=555555&logo=githubsponsors&logoColor=ffffff)](https://github.com/sponsors/Botspot) +[![Sponsor](https://img.shields.io/badge/sponsor-Blackout%20Secure-EA4AAA?style=for-the-badge&labelColor=555555&logo=githubsponsors&logoColor=ffffff)](https://github.com/sponsors/blackoutsecure) +[![Blackout Secure](https://img.shields.io/badge/maintained%20by-Blackout%20Secure-0a7ea4?style=for-the-badge&labelColor=555555&logo=shieldsdotio&logoColor=ffffff)](https://blackoutsecure.app) + +> [!NOTE] +> [Blackout Secure](https://blackoutsecure.app/) is proud to partner with [Botspot](https://github.com/Botspot) and the [Windows on R](https://worproject.com/) community as part of this endeavour. Together, we are carrying WoR-Flasher forward while keeping Botspot's original authorship, project direction, and community connections visible. -In 2020, this was flat-out impossible. -In 2021, this required following [a complicated tutorial](https://worproject.com/guides/how-to-install/from-other-os). -Now, using the new WoR-flasher, it's a _piece of cake_. +Create a bootable Windows 10 or Windows 11 ARM64 drive for a Raspberry Pi from Linux or macOS. + +WoR-Flasher downloads or imports Windows, adds the required UEFI firmware and available drivers, writes the target drive, and verifies the finished result. It automates the manual process described in worproject's [How to install from other OSes](https://worproject.com/guides/how-to-install/from-other-os) guide. + +> [!WARNING] +> Flashing erases the selected drive. Check the device carefully, keep the computer powered, and do not remove the drive until verification and ejection finish. -**[Get started](#getting-started)** · **[Find your Pi](#supported-devices)** · **[Troubleshooting](#troubleshooting)** · **[Get help](#getting-help)** +--- ## Table of contents -- [ WoR-flasher](#-wor-flasher) - - [Table of contents](#table-of-contents) - - [Features](#features) +- [Table of contents](#table-of-contents) + - [Compatibility](#compatibility) - [Requirements](#requirements) - - [Windows compatibility](#windows-compatibility) - - [The ARMv8.1 limitation](#the-armv81-limitation) - - [End of support](#end-of-support) - - [Preinstalled apps](#preinstalled-apps) - - [Supported devices](#supported-devices) - - [Raspberry Pi 3 / Pi 2 v1.2](#raspberry-pi-3--pi-2-v12) - - [Raspberry Pi 4 / Pi 400](#raspberry-pi-4--pi-400) - - [The 3 GB RAM limit](#the-3-gb-ram-limit) - - [Raspberry Pi 5](#raspberry-pi-5) - - [If you need Wi-Fi or graphics acceleration](#if-you-need-wi-fi-or-graphics-acceleration) - - [Getting started](#getting-started) - - [Choosing a drive](#choosing-a-drive) - - [Install from Pi-Apps](#install-from-pi-apps) - - [Install manually](#install-manually) - - [Download a single script](#download-a-single-script) + - [Install](#install) + - [Pi-Apps](#pi-apps) + - [Usage](#usage) - [Graphical interface](#graphical-interface) + - [macOS walkthrough](#macos-walkthrough) - [Terminal interface](#terminal-interface) + - [Non-interactive use](#non-interactive-use--json-configuration) + - [Integration adapter](#integration-adapter) + - [Parameters](#parameters) + - [Application setup](#application-setup) + - [Existing Windows ISO](#existing-windows-iso) + - [Pi 4 RAM unlock](#pi-4-ram-unlock) + - [Offline Windows setup](#offline-windows-setup) + - [Customization templates](#customization-templates) + - [WoR-PE options](#wor-pe-options) + - [Download cache](#download-cache) + - [What to expect](#what-to-expect) + - [Updating](#updating) - [Troubleshooting](#troubleshooting) - - [The Pi is stuck on the rainbow screen](#the-pi-is-stuck-on-the-rainbow-screen) - - [The Pi reaches the UEFI splash screen, then freezes](#the-pi-reaches-the-uefi-splash-screen-then-freezes) - - [The keyboard does not work in UEFI](#the-keyboard-does-not-work-in-uefi) - - [The Pi boots straight to "Starting PXE over IPv4" / the Boot Manager only lists network options](#the-pi-boots-straight-to-starting-pxe-over-ipv4--the-boot-manager-only-lists-network-options) - - [Only 3 GB of RAM is available](#only-3-gb-of-ram-is-available) - - [Getting more detail](#getting-more-detail) - - [Scripting reference](#scripting-reference) - - [Environment variable options](#environment-variable-options) - - [Advanced tuning variables](#advanced-tuning-variables) - - [Functions](#functions) - - [Example function and variable usage](#example-function-and-variable-usage) + - [Development](#development) + - [Repository layout](#repository-layout) + - [Support](#support) + - [Maintainer partnership](#maintainer-partnership) + - [What this maintained source adds](#what-this-maintained-source-adds) + - [Related resources](#related-resources) + - [Versions](#versions) - [Contributing](#contributing) - - [Is this legal?](#is-this-legal) + - [Contributors](#contributors) - [License](#license) - - [Getting help](#getting-help) - - [Credits](#credits) - - [Sources](#sources) -## Features +--- -**Windows images** +## Compatibility -- Downloads Windows 10 and Windows 11 ARM64 directly from Microsoft's update servers, so no copyrighted files are redistributed. See [Is this legal?](#is-this-legal). -- Pick the newest release automatically, or choose an exact build number. -- Supports [37 languages](https://worproject.com/), selectable at flash time. -- Import your own ARM64 ISO instead, via the `SOURCE_FILE` variable. -- Extracted images are cached per build and language, so repeat flashes skip the lengthy download and image-generation step. +| Raspberry Pi | Newest usable Windows 11 | Limitations | +| -------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pi 2 v1.2, Pi 3, CM3 | 23H2 (`22631.x`) | No Wi-Fi or graphics acceleration; Windows 10 is usually faster | +| Pi 4, Pi 400 | 23H2 (`22631.x`) | No Wi-Fi or graphics acceleration; RAM is limited to 3 GB by default | +| CM4 | 23H2 (`22631.x`) | Known to freeze at the UEFI boot screen on some units ([pftf/RPi4#146](https://github.com/pftf/RPi4/issues/146)); USB requires the RAM limit set to 1 GB, and PCIe does not work | +| Pi 5 | 25H2 and newer | Community/unofficial support only ([worproject FAQ](https://worproject.com/faq#is-raspberry-pi-5-or-newer-supported)); no native Pi hardware drivers; USB Ethernet is required for networking | -**Hardware awareness** +Pi 3 and Pi 4 cannot run builds newer than `25163`, because those builds require ARMv8.1 atomics. WoR-Flasher rejects incompatible builds. Windows versions that run on these models are past end of support and should be treated as experimental or offline systems. -- Detects which Windows builds the target Pi can actually boot and hides the rest. See [The ARMv8.1 limitation](#the-armv81-limitation). -- Downloads the matching [UEFI firmware](https://github.com/pftf/RPi4/releases) and [ARM64 drivers](https://github.com/worproject/RPi-Windows-Drivers/releases) for the chosen model, newest release by default with pinned fallbacks. -- Injects the drivers into the Windows PE boot image automatically. -- Verifies every download against an upstream SHA1 or SHA256 hash. +## Requirements -**Flashing** +| Requirement | Detail | +| ------------ | ------------------------------------------------------------------------------------------------------------------------- | +| Host OS | Raspberry Pi OS, Debian, Ubuntu, Linux Mint or another Debian-based Linux; or macOS 13+ with [Homebrew](https://brew.sh/) | +| Privileges | Administrator or `sudo` access | +| Network | Internet access, unless every required file is already cached | +| Free space | About 10 GB in the download directory | +| Target drive | At least 8 GB | +| Display | A desktop session, for the graphical interface only | -- Creates either an installation drive that installs Windows onto itself, or a recovery drive that installs onto other disks. See [Choosing a drive](#choosing-a-drive). -- Customizes `config.txt` for overclocking or display tweaks via the `CONFIG_TXT` variable. -- Optionally downloads everything to a RAM disk to spare your SD card, using [More RAM](https://pi-apps.io/install-app/install-more-ram-on-raspberry-pi/) from Pi-Apps. +Dependencies are installed automatically on supported hosts. -**Interfaces** +Windows, WSL and non-Debian Linux distributions are **not** supported. WoR-Flasher deliberately refuses to run under WSL: WSL cannot reach USB drives directly, and the drives it does list are WSL's own virtual disks, so erasing one would damage the WSL installation. On Windows, use the official [Windows on Raspberry Imager](https://worproject.com/downloads) instead. -- Graphical wizard (`install-wor-gui.sh`) and terminal interface (`install-wor.sh`). -- Fully scriptable through [environment variables](#environment-variable-options), including a `DRY_RUN` mode. -- Sourceable as a library of [shell functions](#functions) for use in larger scripts. -- Self-updates from GitHub on each run, unless `NO_UPDATE=1` is set. +## Install -## Requirements +```bash +git clone https://github.com/blackoutsecure/wor-flasher +cd wor-flasher +./install-wor-gui.sh +``` -WoR-flasher runs on a Linux machine and flashes a drive that you then move to a Raspberry Pi. The table below is about the **computer running the flasher**, not the Pi. +On macOS, Finder users can instead double-click the generated **WoR-Flasher.app** from a release download, or run `npm run build:macos` in a checkout and open `release/macos/WoR-Flasher.app`. The app opens the same native GUI without leaving a Terminal window open. Keep the entire `.app` bundle together when moving it. -| Host operating system | CLI | GUI | Notes | -| --------------------------------------------------------- | --- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Raspberry Pi OS (32 or 64-bit) | Yes | Yes | The only system upstream tests against | -| Debian, Ubuntu, Linux Mint and other Debian-based distros | Yes | Yes | ARM or x86_64. The CLI works over a terminal or SSH; the GUI works on desktop sessions with `yad` and a supported terminal emulator such as GNOME Terminal | -| Fedora, Arch, openSUSE and other non-Debian Linux | No | No | Dependencies are installed with `apt`, and installed packages are detected by reading the dpkg database | -| macOS | No | No | The CLI and GUI now fail early with a clear message, but the flashing backend still requires Linux block-device and filesystem tools such as `lsblk`, `findmnt`, `parted`, `mkfs.fat`, `mkfs.exfat`, `mount.exfat-fuse`, `modprobe` and `/sys` | -| Windows | No | No | Requires Linux block-device and filesystem tooling | -| Windows with WSL2 | No | No | WSL2 does not expose removable drives by default, so assume it does not work | +WoR-Flasher runs one GUI session per signed-in user, even if more than one checkout or version is present. Opening the app again brings the current macOS window forward instead of starting another installer workflow. -You also need: +For command-line use, use the complete repository. Neither script is designed to be downloaded on its own or piped into Bash. The standalone macOS application carries a generated, integrity-checked copy of that same repository runtime rather than a second implementation. -- **`sudo` access**, for partitioning and mounting the target drive -- **About 10 GB of free space** in the download directory, for the Windows image and the files extracted from it -- **A target drive of at least 8 GB.** See [Choosing a drive](#choosing-a-drive) -- **A desktop session** if you want the graphical interface, since it needs `yad` and a terminal emulator. Ubuntu Desktop's default GNOME Terminal is supported. The terminal interface works over SSH -- **A Raspberry Pi 2 v1.2, 3, 4, 400, or 5** to run Windows on +## Pi-Apps -> [!WARNING] -> Flashing erases the target drive completely. Botspot (the developer of this tool) cannot be held responsible for data loss. +[Pi-Apps](https://github.com/Botspot/pi-apps) provides a graphical installation and removal path for the Linux version of WoR-Flasher. Install the **Windows Flasher** app from Pi-Apps, then open **Accessories -> WoR-Flasher** or run: -## Windows compatibility +```bash +~/wor-flasher/install-wor-gui.sh +``` -| Pi model | CPU | Architecture | Newest Windows 11 that boots | End of support | Notes | -| ---------------------- | ---------- | ------------ | ---------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -| Pi 2 v1.2 / Pi 3 / CM3 | Cortex-A53 | ARMv8.0 | 23H2 (`22631.x`) | [November 11, 2025](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro) | Windows 10 recommended | -| Pi 4 / Pi 400 | Cortex-A72 | ARMv8.0 | 23H2 (`22631.x`) | [November 11, 2025](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro) | [RAM limited to 3 GB](#the-3-gb-ram-limit) by default | -| Pi 5 | Cortex-A76 | ARMv8.2 | 25H2 and newer | [October 12, 2027](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro) | No Windows drivers yet | +Pi-Apps installs the application to `~/wor-flasher` from the maintained Blackout Secure `patch-1` branch and manages its launcher and removal. See [Maintainer partnership](#maintainer-partnership) for the project history and support links. -CPU and architecture data from the [Raspberry Pi processor documentation](https://www.raspberrypi.com/documentation/computers/processors.html). Support dates from [Microsoft Lifecycle](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro). +## Usage -### The ARMv8.1 limitation +There are two built-in front-ends over one engine: -> [!IMPORTANT] -> **Windows 11 builds newer than 25163 cannot run on the Pi 3 or Pi 4.** Those builds make extensive use of the atomic instructions introduced in ARMv8.1, which the Cortex-A53 and Cortex-A72 do not implement. +- **`install-wor.sh`** holds all of the logic — drive detection, download and cache handling, ISO validation, partitioning, flashing and verification. +- **`install-wor-gui.sh`** sources it and adds only the windows. It collects your choices in native dialogs, then runs `install-wor.sh` and shows a native progress window (AppKit on macOS, `yad` on Linux) instead of a visible terminal. -The symptom is distinctive: the Pi reaches the UEFI splash screen normally, then hangs with no error message the moment the bootloader hands off to Windows. In practice this means 23H2 (`22631.x`) is the newest retail release that works, since 24H2 is build 26100. The Pi 5's Cortex-A76 is ARMv8.2 and is unaffected. +Both therefore write identical media from identical settings. The built-in GUI intentionally uses the engine directly because it needs shared functions and state while constructing its forms. The separate integration adapter below is a process-level contract for external tools, not an extra layer inside the GUI. -WoR-flasher enforces this automatically. When the target is a Pi 3 or Pi 4, incompatible builds are hidden from the version menus, `get_bid` returns the newest build that will actually boot, and flashing aborts with an explanation if an incompatible build is supplied via the `BID` variable. The cutoff is controlled by the [`ARMV80_MAX_BUILD`](#advanced-tuning-variables) variable. +### Graphical interface -Source: [Windows on Raspberry FAQ](https://worproject.com/faq), "Does Windows 11 work?" - build 25163 is documented there as the last one that boots on the Pi 4 and older. +```bash +./install-wor-gui.sh +# or, equivalently: +./install-wor.sh --gui +``` -### End of support +![WoR-Flasher shared graphical interface overview](assets/overview.png) -End-of-support dates in the table are for the newest bootable build, Home and Pro editions. Enterprise and Education editions are supported longer. If you run Windows 10 instead, its support ended [October 14, 2025](https://learn.microsoft.com/en-us/lifecycle/products/windows-10-home-and-pro). +The overview image shows the shared installation workflow. On macOS, the same choices are presented in native AppKit windows rather than Linux `yad` dialogs. -Source: [Windows 11](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro) and [Windows 10](https://learn.microsoft.com/en-us/lifecycle/products/windows-10-home-and-pro) lifecycle pages, and the [Enterprise and Education](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-enterprise-and-education-version-21h2) equivalent. +Both front-ends size windows from the active screen. The Linux GUI detects its desktop geometry with `xrandr`, `xdpyinfo` or `xwininfo`, clamps every requested width and height inside fixed screen margins, and falls back to the narrow product logo when a full-size illustration cannot fit. A conservative `1024x768` fallback is used when the display server exposes no geometry command. -> [!WARNING] -> Every Windows version a Pi 3 or Pi 4 can run is now past end of support and no longer receives security updates. This is fine for an experimental or offline machine, but worth knowing before putting one on a network you care about. +Linux follows the same staged route as macOS: partnership announcement, Windows version, Raspberry Pi model, target drive, installation mode, overview, Advanced Options, progress, and completion. Its overview and Advanced Options scroll inside their bounded windows, and `config.txt` opens in a separate Save/Back editor. Progress-window Abort and close both stop the installer tree before reporting failure. -### Preinstalled apps +Window chrome remains native to each desktop. On Ubuntu, GNOME/Mutter draws the title bar and its controls; WoR-Flasher marks yad dialogs fixed-size so they support native minimize, restore, and close without allowing resize or maximize. Replacing those controls with imitation macOS traffic lights would remove native accessibility and window-manager behavior. -WoR-Flasher cannot debloat the OS. Performance is about the same either way, but there will be extra preinstalled apps you must remove manually if you want them gone. +The front-end is never chosen automatically. `DISPLAY` is also set over SSH and in CI, and a tool that erases a drive should do exactly what it was asked to do. -## Supported devices +An **Advanced Options** window is reachable from the confirmation screen on both platforms. It exposes every configuration-only option as a checkbox, plus an editable `config.txt`: [offline Windows setup](#offline-windows-setup), the [Pi 4 RAM unlock](#pi-4-ram-unlock), whether to use the latest UEFI firmware or drivers instead of the tested pinned versions (the pinned version is shown in each label), whether to skip the final written-image verification, and dry run. `APPLY_CUSTOM_CONFIG_TXT` controls whether the editable `config.txt` is applied at all; unchecking it dims the editor and leaves the UEFI firmware package's own default in place. A **Downloaded files** menu selects the [cache mode](#download-cache), since it has three settings rather than two. -Windows on a Raspberry Pi relies on a community driver package rather than vendor drivers, so a fair amount of hardware is unavailable or degraded. Find your model below. +Administrator access is requested through a native password dialog on both platforms — there is no terminal to type into. -> [!NOTE] -> The [RPi-Windows-Drivers](https://github.com/worproject/RPi-Windows-Drivers) project was archived in February 2025 and is read-only. Version 0.17 is the final release, so these tables are final too. See the [status page](https://github.com/worproject/RPi-Windows-Drivers#status) for the full per-device breakdown. +#### macOS walkthrough -### Raspberry Pi 3 / Pi 2 v1.2 +1. Double-click **WoR-Flasher.app**, or launch `./install-wor-gui.sh` or `./install-wor.sh --gui`, from macOS 13 or newer. +2. Review the partnership announcement. The **Botspot** and **Blackout Secure** names open their respective websites, and the Proceed button continues automatically after the countdown. +3. Choose the Windows version, language, Raspberry Pi model and target drive in native AppKit windows. The target drive is clearly identified before any erase operation. +4. Review the shared Installation Overview, then use **Advanced Options** for cache mode, firmware and driver choices, Pi 4 RAM handling, offline OOBE and `config.txt` customization. +5. Confirm the flash. A native progress window reports each shared installer phase, supports aborting, and retains a failure log when something stops unexpectedly. +6. After successful verification, the completion dialog provides the log controls and the next-steps guidance for moving the drive to the Raspberry Pi. -**Cortex-A53, ARMv8.0.** Newest bootable Windows: 23H2 (`22631.x`), though Windows 10 is recommended on this hardware. +The repository does not currently include desktop captures of the macOS windows because the GUI requires an interactive macOS display session. The workflow and shared overview artwork are kept here so the documented behavior stays accurate across both front-ends. -| Hardware | Status | Notes | -| ------------------------- | ----------- | ------------------------------------------------------------------------------------------- | -| Onboard Ethernet | Working | LAN9514 on the Pi 3 B, LAN7515 on the Pi 3 B+ | -| Wi-Fi | Not working | No driver exists for the CYW43438 or CYW43455 chips | -| Bluetooth | Partial | Bus speed is limited because hardware flow control is not exposed, and the driver may crash | -| USB 2.0 | Working | | -| SD card | Working | | -| Display | Working | Basic frame buffer only, no acceleration | -| GPU / 3D acceleration | Not working | The driver loads but is unfinished, so 3D and WebGL do not work | -| HDMI audio | Not working | No driver available | -| Analog audio jack | Working | | -| GPIO, SPI, I2C, PWM, UART | Working | | -| CSI camera module | Not working | No driver available | -| 7-inch DSI touch screen | Partial | The display works but the resolution may be wrong, and touch input does not | -| 3-pin case fan | Partial | The UEFI can switch it on, but it never switches off | +### Terminal interface -Source: [RPi-Windows-Drivers status page](https://github.com/worproject/RPi-Windows-Drivers#status), Raspberry Pi 3 (ARM64) section. +```bash +./install-wor.sh +``` -### Raspberry Pi 4 / Pi 400 +```text +Usage: install-wor.sh [OPTIONS] -**Cortex-A72, ARMv8.0.** Newest bootable Windows: 23H2 (`22631.x`). + (no arguments) run the interactive text-mode installer + --gui run the graphical front-end instead + --config load configuration settings from a JSON file + --version print the version and exit + --help show this message +``` -| Hardware | Status | Notes | -| ------------------------- | ----------- | ---------------------------------------------------------------------------------------- | -| Onboard Ethernet | Working | Broadcom GENET gigabit controller | -| Wi-Fi | Not working | No driver exists for the CYW43455 chip | -| Bluetooth | Working | | -| USB 2.0 | Partial | The OTG controller requires RAM limited to 1 GB | -| USB 3.0 | Partial | UASP is disabled so USB 3.0 drives can boot, which significantly reduces transfer speeds | -| SD card | Partial | The eMMC2 controller lacks DMA, HS200/HS400 and UHS-I | -| Display | Working | Basic frame buffer only, no acceleration | -| GPU / 3D acceleration | Not working | The driver loads but is unfinished, so 3D and WebGL do not work | -| HDMI audio | Partial | The HDMI0 port only, which is the one next to the USB-C connector | -| Analog audio jack | Working | | -| GPIO, SPI, I2C, PWM, UART | Working | | -| CSI camera module | Not working | No driver available | -| 7-inch DSI touch screen | Partial | The display works but the resolution may be wrong, and touch input does not | -| 3-pin case fan | Partial | The UEFI can switch it on, but it never switches off | +The selected drive is erased. Drives from 8 GB to under 25 GB can create recovery media for another drive. Drives of 25 GB or more can also install Windows onto themselves. The host's current boot drive is always excluded. -Source: [RPi-Windows-Drivers status page](https://github.com/worproject/RPi-Windows-Drivers#status), Raspberry Pi 4 / 400 (ARM64) section. Firmware from [pftf/RPi4](https://github.com/pftf/RPi4). +### Non-interactive use & JSON Configuration -#### The 3 GB RAM limit +`install-wor.sh` is designed to be driven non-interactively via environment variables or a `config.json` configuration file. -The UEFI firmware limits the Pi 4 to 3 GB of usable RAM by default, regardless of whether your board has 4 GB or 8 GB. This is a conservative default, not a permanent restriction. +Settings follow a 3-tier precedence cascade: **Environment Variables / CLI Options** > **JSON Configuration (`config.json` or `--config`)** > **Script Defaults**. -You can change it whenever you like - before installing Windows, or at any point after setup has finished. The setting lives in the UEFI firmware rather than in Windows, so it persists across reboots and changing it never requires reinstalling. +```bash +# Using environment variables +RPI_MODEL=4 WIN_LANG=en-us BID=22631.2861 DEVICE=/dev/sda CAN_INSTALL_ON_SAME_DRIVE=1 ./install-wor.sh -To use the full amount of RAM: +# Using a JSON configuration file +./install-wor.sh --config config-templates/config.json +``` -1. Keep pressing ESC after plugging in the power cord, until you see the UEFI setup screen. -2. Go to `Device Manager` → `Raspberry Pi Configuration` → `Advanced Configuration`. -3. Change `Limit RAM to 3 GB` to `Disabled`. -4. Press ESC several times to go back, then Y to save when prompted, and reboot. +Refer to [`config-templates/config.json`](config-templates/config.json) for the default run configuration and [`config-templates/config.schema.json`](config-templates/config.schema.json) for the full JSON configuration schema. Project pins such as the WoR-PE package URL, SHA-256 digest, firmware versions, driver version, Windows build guardrails and update target live in [`src/config/metadata.json`](src/config/metadata.json), are loaded by [`src/lib/metadata.sh`](src/lib/metadata.sh), and remain overrideable through environment variables or a custom config file. -See the [Windows on Raspberry FAQ](https://worproject.com/faq) for details, under "Only 3 GB of RAM are available. How can I fix this?". +Sourcing with the `source` argument makes the engine's functions available without running a flash. Useful ones include `list_devs`, `list_dev_paths`, `drive_capability`, `describe_device`, `get_bid`, `get_os_name`, `list_langs`, `validate_iso_file`, `list_cached_winfiles`, `settings_summary` and `install_packages`. -> [!NOTE] -> On the Compute Module 4, USB support requires a RAM limit, so leave this setting enabled there. +## Integration adapter -### Raspberry Pi 5 +`install-wor-hook.sh` is the stable command-line adapter for external front-ends and automation. When it is kept next to `install-wor.sh`, it uses that engine directly. When distributed by itself, it automatically obtains a shallow copy of the complete WoR-Flasher repository in `${XDG_CACHE_HOME:-$HOME/.cache}/wor-flasher-hook`; fetching the complete checkout ensures required assets such as `config-templates/` are present. It sources the selected engine for discovery and summaries, then executes it directly for a flash. -**Cortex-A76, ARMv8.2.** The only model that can run Windows 11 24H2 and newer. +| Command | Output or behavior | +| ------------------------ | ----------------------------------------------------------------------------------- | +| `list-devices` | Safe whole-disk candidate paths, one per line, excluding the current boot drive | +| `describe-device DEVICE` | The supplied path with its size and model when available | +| `summary` | The current settings as tab-separated `labelvalue` lines | +| `run [ARGS...]` | Runs `install-wor.sh` with the caller's environment and any supplied engine options | -There are **no drivers at all**. WoR-flasher injects a placeholder file purely so the installer will boot, so assume nothing in the tables above applies. +```bash +./install-wor-hook.sh list-devices +./install-wor-hook.sh describe-device /dev/sda -| Hardware | Status | Notes | -| ----------------------- | ----------- | ---------------------------------------- | -| Everything | Not working | No driver package exists for the Pi 5 | -| USB to Ethernet adapter | Working | The only practical way to get networking | +./install-wor-hook.sh --set DEVICE=/dev/sda --set RPI_MODEL=4 \ + --set BID=22631.2861 --set WIN_LANG=en-us \ + --set CAN_INSTALL_ON_SAME_DRIVE=1 summary -SD card boot seems more reliable than USB on this model. The WoR developers have [stated](https://worproject.com/faq) they no longer offer support for Raspberry Pi boards, so this is unlikely to change. +./install-wor-hook.sh --progress-file /tmp/wor.progress \ + --set DEVICE=/dev/sda --set RPI_MODEL=4 --set BID=22631.2861 \ + --set WIN_LANG=en-us --set CAN_INSTALL_ON_SAME_DRIVE=1 run +``` -Source: [worproject/rpi5-uefi](https://github.com/worproject/rpi5-uefi), which provides the Pi 5 firmware, and the [Windows on Raspberry FAQ](https://worproject.com/faq). +Standalone bootstrap requires `git`. These variables control where the hook obtains the engine; only point them at a repository and ref you trust: -### If you need Wi-Fi or graphics acceleration +| Variable | Default | Purpose | +| ---------------------- | -------------------------------------------------- | -------------------------------------------- | +| `WOR_HOOK_REPOSITORY` | `https://github.com/blackoutsecure/wor-flasher.git` | Git repository containing the complete tool | +| `WOR_HOOK_REF` | `main` | Branch or tag cloned by the hook | +| `WOR_HOOK_INSTALL_DIR` | `${XDG_CACHE_HOME:-$HOME/.cache}/wor-flasher-hook` | Persistent checkout used by standalone hooks | -Neither works on any model and neither is coming. [BVM](https://github.com/Botspot/bvm) runs Windows in a VM where the Linux host owns the hardware, so Wi-Fi, audio and USB are passed through and work normally. +Discovery is a snapshot, not authorization to erase a path later. `list-devices` rejects unsupported hosts and excludes the current boot drive, but device state can change. `describe-device` formats any supplied path and `summary` previews settings; neither validates that a device is currently safe. Call `list-devices` again before `run`, and let `run` perform the engine's final host, device, capacity and installation-mode checks. For unattended operation, provide all required values from [Parameters](#parameters) through environment variables or repeated `--set NAME=VALUE` options; otherwise the engine can prompt for missing choices. -Source: [BVM](https://github.com/Botspot/bvm). Note that BVM pins the Pi 4 to build `22631.2861` for the same CPU reason described above, so it does not raise the Windows version ceiling. +The adapter is transport-neutral. A GUI, desktop launcher, test harness or another local imaging application can present its own choices and invoke the same engine without copying its flashing logic. Pass `--progress-file FILE` or set `WOR_GUI_PROGRESS_FILE` to a writable path to receive line-oriented, tab-separated events while `run` is active: -## Getting started +```text +STATUSmessage +STEPcurrenttotalmessage +SUBSTEPpercent +TASKpercentlabel +``` -### Choosing a drive +`STEP` reports the major workflow stage. `SUBSTEP` reports the current stage's numeric progress. `TASK` carries the friendly current operation, such as `install.wim`, paired with the same percentage so an external progress bar can display `install.wim: 84%` without parsing terminal output. -The size of the drive you flash determines what it can do: +The adapter returns the underlying command's exit status. Usage errors, including an unknown command or a missing `DEVICE` for `describe-device`, return `2`. -| Drive size | What it can do | -| --------------- | --------------------------------------------------------------------------------- | -| 25 GB or larger | Installation drive - can install Windows onto itself | -| 8 GB to 25 GB | Recovery drive - can only install Windows onto **other drives larger than 16 GB** | -| Under 8 GB | Too small to be usable | +Raspberry Pi Imager supports a custom image repository through `--repo`, which is useful for publishing image metadata and downloads. It does not by itself turn an arbitrary shell flasher into an Imager write target. A future Imager integration should therefore be a deliberate adapter on the Imager side that calls this contract, rather than embedding or forking the flashing logic. See the [Raspberry Pi Imager repository](https://github.com/raspberrypi/rpi-imager) for its current repository and application integration model. -WoR-flasher detects this from the selected drive. If the drive is 25 GB or larger, the terminal and graphical interfaces ask whether to make a self-installing drive or a recovery drive. Smaller usable drives automatically use recovery mode. +## Release tooling -### Install from Pi-Apps +The flashing engine remains `install-wor.sh`. Node.js is used only for release packaging and validation, where it is a better fit for deterministic file copying, checksum generation and future platform manifests. The tooling has no runtime dependencies. -The fastest way to get WoR-flasher running on a RPi is by using the [Pi-Apps app store for Raspberry Pi](https://github.com/Botspot/pi-apps): -[![badge](https://github.com/Botspot/pi-apps/blob/master/icons/badge.png?raw=true)](https://github.com/Botspot/pi-apps) -Installing WoR-flasher from Pi-Apps has several advantages: it creates a convenient button in the Start menu, uninstalling takes one click, and updates are handled seamlessly. +```bash +npm run check # shell syntax, macOS runtime freshness and release-plan validation +npm run build # stage fresh macOS, Linux and Windows-placeholder release folders +npm run build:macos # stage release/macos/WoR-Flasher.app and SHA256SUMS +npm run build:linux # stage release/linux/wor-flasher, WoR-Flasher--linux.tar.gz and SHA256SUMS +npm run package:all # refresh the embedded .app runtime, then stage every release folder +npm run pe:check # download the pinned WoR-PE package and verify the recorded SHA-256 +npm run pe:update # resolve the latest WoR-PE package, hash it and update project metadata +npm run metadata:check # verify package.json matches src/config/metadata.json +npm run metadata:write # rewrite package.json to match src/config/metadata.json +npm run clean # remove generated release output +``` -### Install manually +Generated release output is written under `release/` and is intentionally ignored by Git. It is rebuilt from the source checkout and the embedded macOS runtime each time, so the release folder is not another maintained copy of the project. Publish `release/linux/WoR-Flasher--linux.tar.gz` for Linux users; it contains the `wor-flasher/` payload including `install-wor.sh`, `install-wor-gui.sh`, and `install-wor-hook.sh`. Review the staged app or Linux payload and the matching `SHA256SUMS` before publishing. + +`pe:check` and `pe:update` are maintainer commands because they download release assets. Keep `pe:check` out of routine CI unless network access is expected; use `pe:update` only when deliberately refreshing the pinned WoR-PE package URL and digest in [`src/config/metadata.json`](src/config/metadata.json). + +[`src/config/metadata.json`](src/config/metadata.json) is the source of truth for `package.json`'s `version`, `description`, `license`, `homepage`, `repository`, `bugs`, `funding`, and `keywords`; edit `product.*` there, then run `npm run metadata:write` (or `npm run version:set`, which calls it automatically). `npm run build` and `npm run package:all` refuse to stage a release while `package.json` is out of sync, and `npm run check` runs `metadata:check` too. + +Windows packaging is intentionally only a placeholder today. A Windows UI should drive the same `install-wor-hook.sh` / engine contract only after a separate device-safety design exists for Windows disks, elevation and removable media. Until then, Windows users should use the official Windows on Raspberry Imager. + +## Parameters + +Every prompt has a matching environment variable. + +| Variable | Default | Function | +| --------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | +| `DL_DIR` | `~/wor-flasher-files` | Where components are downloaded and Windows images are extracted | +| `RPI_MODEL` | _ask_ | Target Raspberry Pi: `3`, `4` or `5` | +| `BID` | _ask_ | Exact Windows build ID, e.g. `22631.2861` | +| `WIN_LANG` | _ask_ | Windows language code, e.g. `en-us` | +| `DEVICE` | _ask_ | Target drive, e.g. `/dev/sda` or `/dev/disk4` | +| `CAN_INSTALL_ON_SAME_DRIVE` | _ask_ | `1` to install Windows onto the target itself, `0` to make recovery media for another drive | +| `SOURCE_FILE` | unset | Path to an existing Windows ARM64 ISO, instead of downloading | +| `CONFIG_TXT` | shipped template | Body of `config.txt` written to the boot partition | +| `APPLY_CUSTOM_CONFIG_TXT` | `1` | `0` leaves the UEFI firmware package's own `config.txt` in place | +| `OOBE_NETWORK_BYPASS` | `1` | `0` requires the standard network-connected Windows setup flow | +| `WINDOWS_ACCOUNT_SETUP` | `0` | `1` creates the optional local Windows administrator configured in Advanced Options | +| `WINDOWS_ACCOUNT_USERNAME` | unset | Username for the optional local Windows account | +| `WINDOWS_ACCOUNT_PASSWORD` | unset | Password for the optional account; written to unattended setup only when enabled | +| `WINDOWS_LOCALE_SETUP` | `1` | `1` applies `WINDOWS_LOCALE` to Windows keyboard and regional settings | +| `WINDOWS_LOCALE` | `en-US` | Locale such as `en-US` or `en-GB` used when locale setup is enabled | +| `PI4_AUTO_DISABLE_3GB` | `1` | Pi 4 only. `0` keeps the 3 GB RAM limit | +| `PI4_UEFI_SHELL_UNLOCK` | `0` | Pi 4 only. `1` stages a one-time verified UEFI Shell handoff and restores the EFI loader after setting the RAM variable | +| `UEFI_USE_LATEST` | `0` | `1` queries GitHub for the newest UEFI firmware instead of the pinned version | +| `DRIVERS_USE_LATEST` | `1` | `0` uses the pinned driver package version | +| `SKIP_IMAGE_VERIFICATION` | `0` | `1` skips post-flash verification. Not recommended | +| `CHECK_FOR_UPDATES` | `1` | `0` disables the read-only release check | +| `NO_UPDATE` | `0` | Legacy inverse of `CHECK_FOR_UPDATES`; `1` disables update checks | +| `HIDE_EMPTY_DRIVES` | `1` | `0` shows empty card-reader slots as selectable drives in WoR-PE | +| `USE_CACHE` | `1` | See [Download cache](#download-cache) | +| `DRY_RUN` | `0` | `1` runs every step except writing to the drive | +| `WOR_LOG_FILE` | `$DL_DIR/logs/wor-flasher-.log` | Where a failed run's primary log is kept; `last-run.log` is refreshed for support | +| `VERIFY_TLS` | `1` | `0` skips TLS certificate verification, for hosts with an outdated CA bundle | +| `RUN_MODE` | `cli` | `gui` makes the engine show graphical error dialogs | +| `SKIP_PACKAGE_INSTALL` | unset | `1` assumes dependencies are already present | + +Example: ```bash -git clone https://github.com/Botspot/wor-flasher +DL_DIR=/media/pi/big-drive DEVICE=/dev/sdg RPI_MODEL=4 WIN_LANG=en-us DRY_RUN=1 ./install-wor.sh ``` -This will download the scripts to a new directory named `wor-flasher`. -**Dependencies:** No need to install packages manually on supported Debian-based systems. Running the script will automatically install these: `yad` `aria2` `cabextract` `wimtools` `chntpw` `genisoimage` `exfat-fuse` `exfatprogs` or `exfat-utils`, `wget`, `udftools`, `bc`, `parted`, `dosfstools`, `unzip`, and `git`. +## Application setup -### Download a single script +### Existing Windows ISO -`install-wor.sh` is self-contained, so you can fetch just that file and run it: +Use an official Windows ARM64 ISO containing `sources/install.wim` or `sources/install.esd`: ```bash -mkdir -p ~/wor-flasher && cd ~/wor-flasher -wget https://raw.githubusercontent.com/Botspot/wor-flasher/main/install-wor.sh -chmod +x install-wor.sh -./install-wor.sh +SOURCE_FILE=/path/to/windows-arm64.iso ./install-wor.sh ``` -> [!IMPORTANT] -> This only works for the terminal interface. **`install-wor-gui.sh` cannot be downloaded on its own** - it needs `install-wor.sh`, `terminal-run` and several images beside it, and exits with _"No script found named install-wor.sh"_ if they are missing. Use `git clone` for the graphical interface. -> -> **Do not pipe either script into bash.** `curl ... | bash` fails, because the script needs to know its own directory and exits with _"Failed to determine the directory that contains this script"_. It also means nobody reads the code before it runs `parted`, `mkfs` and `dd` on a drive. - -When importing an ISO, use an official Windows ARM64 image. The script accepts media containing either `sources/install.wim` or `sources/install.esd`; customized Windows images are not supported. +The build number and language are read from the filename where possible, and you are asked for them if not. Customized Windows images are not supported. -Automatic source-checkout updates are disabled by default so a run always uses the revision you reviewed. To opt in for a clean Git checkout, run it with `NO_UPDATE=0`; otherwise update deliberately with `git pull`. A single-file copy has no Git repository, so re-download it when you want a newer version. +### Pi 4 RAM unlock -### Graphical interface +On Raspberry Pi 4 only, the 3 GB RAM limit is disabled automatically after WoR-PE installs Windows and reboots. This setting is ignored for every other model. To keep the limit enabled: ```bash -~/wor-flasher/install-wor-gui.sh +PI4_AUTO_DISABLE_3GB=0 ./install-wor.sh ``` -- Choose a Windows version and choose which Raspberry Pi model will be running it. - ![page1](https://user-images.githubusercontent.com/54716352/131228226-5d5b8456-b273-48a5-b4c3-5e90790cf21e.png) -- Choose a language for Windows. - ![page2](https://user-images.githubusercontent.com/54716352/131228261-e7e1a989-4151-4df7-8aa2-eff95704df41.png) -- Plug in a writable storage device to flash Windows to. - ![page3](https://user-images.githubusercontent.com/54716352/131228296-fb61f216-9a12-412a-b7b5-0bcd185891a0.png) - - See [Choosing a drive](#choosing-a-drive) for what each size can do. -- Double-check that everything looks correct before clicking the Flash button. - ![page4](https://user-images.githubusercontent.com/54716352/131921620-7ca69a5c-13fe-4236-8e0e-27ff4cfffa10.png) -- A terminal will launch and run the `install-wor.sh` script: - ![terminal3](https://user-images.githubusercontent.com/54716352/131228381-11dc3a4e-96da-40ec-8f46-8b28ade5ee52.png) - Note: this can take a lot of time to download individual files from Microsoft, compress them, and generate a Windows image. Fortunately, subsequent runs can skip the lengthy image-generating step if the ISO file exists. -- If all goes well, the terminal will close and you will be told what to do next. - ![next steps](https://user-images.githubusercontent.com/54716352/131228409-f84ede9b-a1fc-43f9-a79c-5b1853513960.png) +Windows Setup attempts to change the pftf `RamLimitTo3GB` firmware variable during the `specialize` pass, after the injected drivers are installed, and reboots once before OOBE. It also clears any BCD-level `truncatememory` cap, a separate Windows Boot Manager memory limit noted in worproject's [imager customization guide](https://worproject.com/guides/wor-imager-customization#configuration-file). The Windows log can report that the runtime variable was changed successfully, but Raspberry Pi UEFI emulates NVRAM in `RPI_EFI.fd` and changes made from an operating system may not persist after reboot. If the limit returns, disable `RamLimitTo3GB` from UEFI Device Manager -> Raspberry Pi Configuration -> Advanced Configuration, or pre-edit the UEFI variable in `RPI_EFI.fd` before flashing. -### Terminal interface +The `specialize` answer-file command invokes the staged `Pi4Disable3GB.ps1` file instead of embedding the PowerShell program. Windows limits `RunSynchronousCommand/Path` to 259 characters and rejects the entire answer file when that limit is exceeded. The script logs its result to `%WINDIR%\Temp\Pi4Disable3GB.log` and always returns success so a firmware-setting failure cannot abort Windows Setup. -```bash -~/wor-flasher/install-wor.sh -``` +> [!IMPORTANT] +> **Set `PI4_AUTO_DISABLE_3GB=0` on Compute Module 4.** Per the [worproject FAQ](https://worproject.com/faq#does-it-work-on-the-compute-module-cm), CM4 requires the RAM limit set to 1 GB — not simply left at 3 GB — for USB to work at all, and PCIe does not work regardless. WoR-Flasher cannot distinguish a CM4 from a Pi 4/400, so do not rely on the automatic default for CM4 hardware. + +### Offline Windows setup + +Enabled by default. WoR-Flasher writes a minimal Microsoft unattended-setup answer file that hides the OOBE network and online-account screens, so setup can continue with a local account when Pi networking is not ready yet. It does not automate accounts, licenses, partitions or privacy choices by default. -
Example terminal walkthrough (click to expand) - -```console -$ ~/wor-flasher/install-wor.sh -Choose Windows version: -1) Windows 11 -2) Windows 10 -3) More options... -Enter 1, 2 or 3: 1 - -Choose language: en-us - -Choose Raspberry Pi model to deploy Windows on: -1) Raspberry Pi 5 -2) Raspberry Pi 4 / 400 -3) Raspberry Pi 3 or Pi2 v1.2 -Enter 1, 2, or 3: 2 - -Available devices: -/dev/sdb - 59.5GB - USB Storage -Choose a device to flash the Windows setup files to: /dev/sdb - -1) Create an installation drive (minimum 25 GB) capable of installing Windows to itself -2) Create a recovery drive (minimum 8 GB) to install Windows on other >16 GB drives -Choose the installation mode (1 or 2): 1 - -Input configuration: -DL_DIR: /home/pi/wor-flasher-files -RUN_MODE: cli -RPI_MODEL: 4 -DEVICE: /dev/sdb -CAN_INSTALL_ON_SAME_DRIVE: 1 -BID: 22631.2861 -WIN_LANG: en-us - -Using UEFI firmware: https://github.com/pftf/RPi4/releases/download/v1.52/RPi4_UEFI_Firmware_v1.52.zip -Formatting /dev/sdb -Generating partitions -Generating filesystems -# script output continues... It generates a Windows image legally, downloads all necessary drivers, the BIOS, the bootloader, and the modified kernel. Once done it ejects the drive. +Advanced Options can optionally configure a Windows local administrator account and a locale profile before the first boot. The account username and password are written to `Autounattend.xml` only when explicitly enabled; the password is never shown in summaries or logs, but Windows setup necessarily stores it in plaintext on the prepared media temporarily. Remove `Autounattend.xml` after setup completes. Regional settings are enabled by default and initially use the current host locale when it matches a Windows locale, otherwise `en-US`; a selection made during the current GUI run is retained when returning to Advanced Options. The locale profile applies one value such as `en-US` or `en-GB` to the Windows keyboard/input, system, user and UI locale settings. + +```bash +OOBE_NETWORK_BYPASS=0 ./install-wor.sh # require network ``` -
-This script is actually what does the flashing: the GUI script is a front-end that launches dialog windows and finally runs install-wor.sh in a terminal. +### Customization templates -## Troubleshooting +[`config-templates/`](config-templates) holds the files injected onto the media or used for configuration validation: -### The Pi is stuck on the rainbow screen +| File | Purpose | +| ------------------------------- | ------------------------------------------------------------ | +| `config.json` | Shipped default configuration parameter file | +| `config.schema.json` | JSON Schema definition for `config.json` parameters | +| `pi3.config.txt` | `config.txt` body for Pi 2 v1.2 / Pi 3 | +| `pi4.config.txt` | `config.txt` body for Pi 4 / Pi 400 | +| `pi5.config.txt` | `config.txt` body for Pi 5 | +| `pi4-ram-unlock.ps1` | PowerShell action that clears the Pi 4 3 GB limit | +| `pi4-ram-unlock-specialize.xml` | Answer-file fragment that runs the above during `specialize` | +| `oobe-network-bypass.xml` | Answer-file fragment for offline OOBE | -The GPU firmware never handed off to the UEFI firmware. Usually one of: +Edit these directly to customize what gets written. Both the CLI and the GUI start from the same template, so they produce identical media. Updating your checkout picks up any changes to them. -- **Mismatched firmware files.** `RPI_EFI.fd`, `start4.elf`, `fixup4.dat`, the `.dtb` files, and the `overlays/` folder must all come from the same UEFI release. Hand-copying only some of them causes this. Re-flash rather than patching files individually. -- **Outdated firmware.** Newer Pi 4 board revisions need a recent UEFI release. WoR-flasher downloads the latest release by default. -- **Outdated bootloader EEPROM.** Update it with Raspberry Pi Imager (`Misc Utility Images` → `Bootloader`). +### WoR-PE options -The green ACT LED blink pattern narrows it down: 3 blinks means `start4.elf` was not found, 4 blinks means it failed to launch, and 7 blinks means `RPI_EFI.fd` was not found. +`HIDE_EMPTY_DRIVES` (default `1`) writes `HideEmptyDrives=1` into the cached WoR-PE `settings.ini` before each run, matching worproject's [WoR-PE package option](https://worproject.com/guides/wor-imager-customization#configuration-file) of the same name, so empty card-reader slots do not show up as selectable drives during setup. -### The Pi reaches the UEFI splash screen, then freezes +### Download cache -If ESC works at the splash but the system hangs once the countdown finishes, the firmware is healthy and the hang is in the handoff to Windows. Check in this order: +Downloads are stored in `~/wor-flasher-files` by default. -1. **The Windows build is too new.** This is by far the most common cause on a Pi 3 or Pi 4. See [The ARMv8.1 limitation](#the-armv81-limitation). -2. **System Table Selection is not ACPI.** Go to `Device Manager` → `Raspberry Pi Configuration` → `Advanced Configuration` and make sure it is set to `ACPI`. Windows cannot boot from a Device Tree handoff. -3. **Secure Boot is enabled.** Check `Device Manager` → `Secure Boot Configuration` and disable it. +| `USE_CACHE` | Behaviour | +| ----------- | ----------------------------------------------------------------------------- | +| `0` | Remove cached components and download them again | +| `1` | Reuse cache only when its source and SHA-256 payload manifest match (default) | +| `2` | Trust the existing cache without update or integrity checks | -### The keyboard does not work in UEFI +```bash +USE_CACHE=0 ./install-wor-gui.sh +``` -UEFI stops accepting keystrokes once the boot countdown expires, so a keyboard that seems dead is often just a system that has already moved on. Start tapping ESC the instant power is applied. +Mode `1` refreshes changed, missing, extra or outdated cached content. Delete `~/wor-flasher-files` when you no longer need the downloads or extracted Windows images. -If it genuinely does not respond, use a wired keyboard plugged directly into a USB 2.0 port with no hub, and unplug all other USB devices. Wireless dongles and keyboards with built-in hubs frequently fail to enumerate in UEFI. +## What to expect -### The Pi boots straight to "Starting PXE over IPv4" / the Boot Manager only lists network options +1. Downloads and verifies the PE installer, UEFI firmware, drivers and Windows image. +2. Extracts or imports the Windows image. +3. Creates FAT32 `WOR_BOOT` and ExFAT `WOR_INSTALL` partitions. +4. Copies the startup and installation files and updates `boot.wim`. +5. Verifies the partition layout, filesystems, boot files, WIM images and the copied `install.wim` checksum. +6. Unmounts and ejects the drive. -There are two independent boot-order mechanisms in play, and it's important to know which one this is: +Downloads and final verification take a long time, especially on slow SD cards. Progress is shown for long operations. **Do not remove the drive until WoR-Flasher reports success.** -1. **The Raspberry Pi bootloader's `BOOT_ORDER`** - lives in the Pi board's own SPI EEPROM, not on the SD card. It just decides which media (SD, USB, network) to search for `start4.elf`/`RPI_EFI.fd` on, and is unaffected by flashing or swapping SD cards. -2. **The UEFI firmware's own Boot Manager `BootOrder`/`Boot####` NVRAM** - this is the "Boot Manager Menu" screen with `UEFI PXEv4`, `UEFI Shell`, etc. It is emulated by the `pftf/RPi4` (or Pi 3/5 equivalent) firmware in a variable-store file that lives **on the SD card's boot partition itself**, so it travels with the card. This is what decides whether "Windows Boot Manager" or the network options get tried first, and it's the one that matters here. +![Next steps after flashing](assets/next-steps.png) -`install-wor.sh`/`install-wor-gui.sh` never touch either of these - they only copy files (Windows install files, drivers, and the UEFI firmware binaries) onto the drive. Neither script calls `efibootmgr`, `bcfg`, or anything else that edits NVRAM. The "Windows Boot Manager" entry is added automatically by **Windows Setup itself**, the first time it actually runs on the Pi (via `bcdboot`, during the WinPE stage), not by anything on the flashing machine. +Move the completed drive to the Pi and connect a display, a wired keyboard and a wired mouse. Windows Setup may restart several times; do not remove power or the drive until setup completes. -**About ejecting the freshly-flashed card and moving it into the Pi:** that's the normal, expected flow and is safe by itself - the script already `sync`s and unmounts/ejects before it exits. The part that actually matters is what happens _after_ that, on the Pi itself: +## Updating -- First boot from the card starts the WinPE-based installer, which partitions/applies the Windows image, then **automatically reboots several times** (WinPE → Windows Setup → specialize → OOBE) before Windows is actually installed and registers its own boot entry. -- If the SD card is removed, swapped for a different card, or the Pi is powered off partway through that automatic reboot sequence, the "Windows Boot Manager" entry never gets written to that card's variable store, and you're left with only the firmware defaults (network/shell) - exactly the symptom described here. -- Let the whole process run to completion (watch it over HDMI, or serially per [Getting more detail](#getting-more-detail)) before assuming the card is done and swapping anything. +WoR-Flasher never rewrites its own installation. Nothing in the tool runs `git pull`, `git merge`, +or replaces its own files on a source checkout, because a partly-updated disk flasher is far more +dangerous than an out-of-date one. Updating is always something you choose to do. -If Windows is confirmed already installed on the drive (`bootmgfw.efi` exists at `EFI/Microsoft/Boot/bootmgfw.efi` on the boot partition) but the entry is still missing, check in this order: +If you installed from a git checkout, update it yourself: -1. **The drive is not connected, or not connected at boot time.** Make sure it is plugged in and powered before applying power to the Pi, not after. -2. **The wrong drive was flashed, or the last flash failed partway through.** Re-run `install-wor.sh` against the correct `/dev/sdX` device and let it finish completely; check its output for errors instead of assuming it succeeded. -3. **The ESP (first partition, `bootpart`) is missing or unreadable.** It must contain `EFI/BOOT/BOOTAA64.EFI`. Mount the drive's first partition on another Linux machine and confirm the file is present - if not, re-flash. -4. **The firmware's own `BootOrder`/`Boot####` NVRAM entries were edited or lost**, e.g. in `Boot Maintenance Manager`, or because Windows Setup was interrupted as above. Enter the UEFI setup (keep tapping ESC at power-on) → `Boot Maintenance Manager` → `Boot Options` → `Add Boot Option`, point it at `EFI\Microsoft\Boot\bootmgfw.efi` on the boot partition, then move it above the PXE/HTTP entries in `Change Boot Order` and save. -5. **Outdated bootloader EEPROM**, same as the rainbow-screen cause above - update it with Raspberry Pi Imager. +```bash +cd wor-flasher +git pull +``` -### Only 3 GB of RAM is available +If you installed from a release archive, download the newer archive from the +[releases page](https://github.com/blackoutsecure/wor-flasher/releases) and verify it against the published +`SHA256SUMS` before use. -Expected on a Pi 4. See [The 3 GB RAM limit](#the-3-gb-ram-limit). +To help you notice a new version, the engine performs a **read-only** release check before setup, +downloads, or flashing begin, and prints a one-line notice when a newer release exists. It makes a +single HTTPS request to the GitHub releases API, writes nothing, and changes nothing. The check +needs Node.js; on a host without Node.js it is silently skipped and the flash proceeds normally. -### Getting more detail +```bash +CHECK_FOR_UPDATES=0 ./install-wor.sh # skip the release check +``` -The generated `config.txt` already sets `enable_uart=1` and `uart_2ndstage=1`, so serial debugging needs no changes. Connect a USB-TTL adapter to GPIO 14 (TX), GPIO 15 (RX), and GND, at **115200 baud**, to see exactly where boot stops. +Legacy callers can still set `NO_UPDATE=1` to disable update checks. You can run the same check on +its own with `npm run update-check`. -## Scripting reference +The macOS app is the one component that can install an update, and only when it has been copied away +from a checkout. It never modifies its own bundle. On first launch it validates the embedded runtime +and copies it to `~/Library/Application Support/WoR-Flasher/runtimes//runtime`. Detached +updates are staged there from release metadata over HTTPS, and are accepted only after the archive +SHA-256, every extracted file digest, and every recorded file mode match the signed package manifest. +Unsafe archive entries, incomplete payloads, equal versions, and downgrades are rejected. Runtime +selection falls back in this order: active, previous, then the immutable embedded copy. Launched from +a source checkout, the combined startup update-and-repair check updates nothing remotely. It restores +only missing tracked runtime files, with your confirmation, from the revision already in your local +checkout. -### Environment variable options +Check what you are running with `./install-wor.sh --version`. -The `install-wor.sh` script is designed to be used within other, larger bash scripts. For automation and customization, `install-wor.sh` will detect and obey certain environment variables. +### Launch repair on macOS -Setting `BID`, `WIN_LANG`, `RPI_MODEL`, `DEVICE` or `CAN_INSTALL_ON_SAME_DRIVE` suppresses the matching interactive prompt, which is what makes unattended runs possible. Interactive runs auto-detect the selected drive's capacity: drives under 8 GB are refused, 8-25 GB drives use recovery mode, and 25 GB+ drives let you choose between self-install and recovery mode. +The app performs a bounded preflight rather than a destructive general-purpose "self-heal": -| Variable | Default | Description | -| --------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `BID` | _prompts_ | An exact Windows version ID. Example: `22631.2861` | -| `WIN_LANG` | _prompts_ | Language for the Windows image. Example: `en-us` | -| `RPI_MODEL` | _prompts_ | Target Raspberry Pi model. Allowed values: `3`, `4`, `5` | -| `DEVICE` | _prompts_ | The device to flash. Example: `/dev/sda` | -| `CAN_INSTALL_ON_SAME_DRIVE` | _prompts_ | Optional automation override. Set to `1` to install Windows onto the selected 25 GB+ drive, or `0` to make a recovery drive for another >16 GB drive | -| `DL_DIR` | `~/wor-flasher-files` | Where components and Windows images are downloaded | -| `SOURCE_FILE` | none | Path to an existing Windows ARM64 ISO to use instead of downloading one. Must be at least 3 GB and end in `.iso` or `.ISO` | -| `CONFIG_TXT` | firmware default | Replaces `config.txt` on the resulting drive, commonly for overclocking or HDMI settings. [This is the firmware's own default.](https://github.com/pftf/RPi4/blob/master/config.txt) The GUI supplies its own per-model version | -| `RUN_MODE` | `cli` | Set to `gui` to display graphical error messages | -| `USE_CACHE` | `0` | Controls reuse of downloaded components. `0` deletes them and downloads again every run, `1` reuses them only while they are still the newest version, `2` reuses them without checking for updates | -| `DRY_RUN` | unset | Set to `1` to run the whole setup but exit after downloading, without flashing | -| `SKIP_PACKAGE_INSTALL` | `0` | Internal test-runner override. Set to `1` only after dependencies have already been installed by the test container | +- If a tracked runtime script, configuration file, template or image is absent, the app offers to restore only that missing file from the local Git `HEAD`. +- If a required Homebrew formula is absent, the app lists the exact formulae and asks before installing only those dependencies. It never runs `brew upgrade`. +- If Homebrew itself is absent, the app offers to open the official [Homebrew website](https://brew.sh/). It does not run a remote installer automatically. +- Existing modified files, untracked files, downloaded Windows content and user settings are never reset or replaced. Download recovery remains controlled by the selected [cache mode](#download-cache). -### Customizing config.txt +Source-file repair requires a complete Git checkout. A detached app instead validates its installed runtime and falls back to the previous or embedded runtime when the active copy is damaged; it does not attempt Git repair. If no runtime validates, the app stops with a native error instead of guessing or overwriting local work. -`CONFIG_TXT` replaces the UEFI `config.txt` on the resulting drive. The GUI starts with a model-specific default, including the settings needed for UEFI and serial diagnostics. Keep those settings unless you know why they need to change. Refer to the official [Raspberry Pi config.txt documentation](https://www.raspberrypi.com/documentation/computers/config_txt.html) for supported options. +## Troubleshooting -An unstable overclock can prevent the Pi from booting. Power it off, put the drive back in a Linux host, then reduce or remove the overclock entries from its `config.txt`. The GPU is not used by Windows on Raspberry Pi, so GPU overclocking has no meaningful benefit. +If a flash fails from the GUI, the full log is kept under `$DL_DIR/logs/` with a UTC timestamp in the filename, or wherever `WOR_LOG_FILE` points. `$DL_DIR/last-run.log` is also refreshed as a stable support shortcut. The primary path is shown in the error dialog and listed on the confirmation screen before you start. Attach that log to any bug report. -These Pi 4 examples are starting points, not guarantees. Board cooling, power supply quality, and silicon variation determine the stable limit. Add only the lines from one example below to the generated Pi 4 configuration. +On macOS, `Written image verified successfully` confirms the copied data, but partition finalization must still finish. If the finalizer fails, the media is not confirmed ready to boot. The installer requires worker readiness before disk preparation and pre-creates user-owned result files. -```ini -# Conservative Pi 4 overclock: 2.147 GHz CPU -over_voltage=6 -arm_freq=2147 -gpu_freq=300 -``` +GUI launches normally have no terminal, so the default `sudo` policy scopes cached authorization to the parent process. The finalizer launches the external `sudo` command directly from the authenticated shell, rather than running the GUI wrapper inside another background shell. This avoids the immediate `Administrator authentication is no longer reusable` startup failure without allowing a second password prompt. The worker script is passed as a fixed command argument instead of relying on detached stdin. Parent-scoped authorization and startup failures are covered by mock-only regression tests; an end-to-end flash with the correction still needs confirmation. Keep the diagnostic log rather than repeatedly reflashing after a startup error. -```ini -# Higher Pi 4 overclock: 2.3 GHz CPU -arm_freq=2300 -gpu_freq=300 -over_voltage=14 -force_turbo=1 -``` +The same worker applies the Pi 3 GPT patch at its original point before written-image verification, then waits for the final retag request. These late writes do not depend on a cached `sudo` timestamp remaining valid during long copies. macOS therefore does not start the ineffective background timestamp refresher. ISO images attached by the current user are also detached without `sudo`, so ISO cleanup does not consume the disk-write password prompt. -Do not treat extreme settings as a general recommendation. Start conservatively, test for stability, and ensure adequate cooling before raising voltage or frequency. - -#### Advanced tuning variables - -These have working defaults and rarely need changing. - -| Variable | Default | Description | -| --------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `UEFI_USE_LATEST` | `1` | Download the newest UEFI firmware release from GitHub. Set to `0` to use the pinned version instead. Pre-releases are never selected, since upstream uses them to mark known-bad builds | -| `UEFI_VER_PI3` | `v1.39` | Pinned UEFI version, used when `UEFI_USE_LATEST=0` or GitHub is unreachable | -| `UEFI_VER_PI4` | `v1.52` | Pinned UEFI version, used when `UEFI_USE_LATEST=0` or GitHub is unreachable | -| `UEFI_VER_PI5` | `v0.3` | Pinned UEFI version, used when `UEFI_USE_LATEST=0` or GitHub is unreachable | -| `DRIVERS_USE_LATEST` | `1` | Download the newest ARM64 driver release from GitHub. Set to `0` to use the pinned version instead | -| `DRIVER_VER` | `v0.17` | Pinned driver package version, used when `DRIVERS_USE_LATEST=0` or GitHub is unreachable. The upstream project is archived, so this is the final release | -| `PE_USE_LATEST` | `1` | Download the newest WoR PE-based installer from worproject.com. Set to `0` to use the pinned package instead | -| `PE_INSTALLER_URL` | v1.1.0 asset | Pinned PE installer URL, used when `PE_USE_LATEST=0` or worproject.com is unreachable | -| `PE_INSTALLER_SHA256` | v1.1.0 hash | Expected SHA256 of the pinned PE installer. The download is always verified, either against this value or against the live hash when using the latest package | -| `ARMV80_MAX_BUILD` | `25163` | The last Windows build that boots on an ARMv8.0 Pi. Higher builds are hidden and rejected for the Pi 3 and Pi 4 | -| `ARMV80_SAFE_BID` | `22631.2861` | The build suggested when a user picks an incompatible one for a Pi 3 or Pi 4 | -| `WIN11_MIN_BUILD` | `22000` | The build number at which a release counts as Windows 11 rather than Windows 10 | -| `WIN10_OLDEST_BUILD` | `17134.112` | Marks the end of the Windows 10 section of worproject.com's version list | -| `EXAMPLE_BID` | `22621.525` | The example build number shown in prompts | -| `VERIFY_TLS` | `1` | Verify TLS certificates when downloading. Set to `0` only if your system has an outdated CA bundle and downloads fail with certificate errors | -| `NO_UPDATE` | `0` | Set to `1` to skip the self-updater | -| `UPDATE_REPO_URL` | `https://github.com/Botspot/wor-flasher` | Repo the self-updater compares against to decide if an update exists. Only affects the check itself - `git pull` still uses the local checkout's own configured remote, typically `origin` | -| `UPDATE_REF` | `HEAD` | Branch/ref on `UPDATE_REPO_URL` to compare the local commit against | - -Example usage: +The disk can temporarily appear unformatted immediately after administrator authentication. During an active macOS GUI flash, WoR-Flasher automatically chooses **Ignore** for the exact **The disk you attached was not readable by this computer** system alert. It never chooses **Initialize** or **Eject**, never dismisses other alert types, and does not disable Disk Arbitration or change global disk settings. The alert may appear briefly before dismissal. Automation starts only after authentication and finalizer readiness, and stops when the flash completes, fails, or is aborted. -```bash -DL_DIR=/media/pi/my-big-flash-drive DEVICE=/dev/sdg DRY_RUN=1 BID=22631.2861 RPI_MODEL=4 WIN_LANG=en-us ~/wor-flasher/install-wor-gui.sh -``` +This requires macOS **Accessibility** permission for WoR-Flasher and permission to control **System Events** under **Privacy & Security > Automation**. WoR-Flasher does not grant these permissions itself. If access is denied, the progress window shows a warning and flashing continues normally; choose **Ignore** manually in that case. Because the system alert does not identify its disk, automatic dismissal is limited to the active-write window and a single matching system alert; multiple matching alerts are left for manual handling. -### Functions +The developer-only `src/macos-disk-claim.c` prototype explores a per-disk claim held by a control pipe. It is not packaged or invoked by the app: compilation succeeded, but claim acquisition timed out on a disposable disk image on the development host. Prompt suppression and formatter compatibility remain unverified. Do not use this prototype on physical media until its lifecycle and formatting interactions have been validated on disposable images. -The `install-wor.sh` script is designed to be used within other, larger bash scripts. For improved integration, `install-wor.sh` is equipped with a variety of useful functions that frontend scripts like `install-wor-gui.sh` can use. -**To source the script** so the functions are available: +
+macOS: "Operation not permitted" formatting the drive -```bash -source ~/wor-flasher/install-wor.sh source -``` +An older WoR-Flasher runtime may report `newfs_msdos`, `newfs_exfat`, or `sgdisk` failing with `Operation not permitted` even though the script already has `sudo`. Since macOS Catalina, writing directly to a raw disk device (`/dev/rdiskN`) needs **Full Disk Access**, which `sudo` does not grant on its own — and this is a deliberate macOS security boundary, so no app (including WoR-Flasher) can turn the toggle on for you; only a person clicking it in System Settings satisfies it. Current macOS formatting uses `diskutil eraseVolume` for the created partitions and retains `sgdisk` only for partition layout and EFI attributes. -Question: why does that command say "`source`" twice? Answer: The first "`source`" is a command, and the second "`source`" is a command-line flag that is passed to the script to let it know you are sourcing it. - -Once the script is sourced, these functions become available: - -| Function | Purpose | -| ---------------------------------- | ----------------------------------------------------------------- | -| `error` | Print an error message and exit with code 1 | -| `status`, `echo_green`, `echo_red` | Print colored progress, success and failure messages | -| `resolve_path` | Resolve a path using GNU or BSD-compatible tools | -| `require_linux_host` | Stop early on unsupported non-Linux hosts | -| `get_file_size` | File size in bytes | -| `sha1_file`, `sha256_file` | Hash a file with GNU or BSD-compatible tools | -| `package_available` | Test whether an apt package exists in the repositories | -| `install_packages` | Install a space-separated list of apt packages | -| `download_from_gdrive` | Download a large publicly shared file from Google Drive | -| `get_partition` | Resolve a partition device node from a drive and partition number | -| `get_device_name` | Human-readable manufacturer and model for a drive | -| `get_size_raw` | Drive size in bytes | -| `drive_capability` | Classify a drive as too small, recovery-only, or install-capable | -| `get_space_free` | Free space in a folder, in bytes | -| `cache_is_current`, `mark_cache` | Check and stamp downloaded component caches | -| `list_devs` | Colored list of drives that can be flashed | -| `detect_root_dev` | Detect the Linux block device backing the current root filesystem | -| `list_langs` | Supported Windows language codes and names | -| `list_bids` | Available Windows build IDs from the catalog | -| `get_bid` | Newest Windows build ID the target Pi can actually boot | -| `cpu_supports_bid` | Whether the target Pi's CPU can run a given build | -| `list_bids_supported` | Build list filtered to what the target Pi can boot | -| `get_os_name` | Human-readable OS name from a build ID | -| `setup` | Run host checks and install Linux dependencies | - -The most commonly reused functions are documented in detail below. - -- `error` - a simple function that Botspot uses in bash scripts to warn the user that something failed and to exit the script with a failure code. (1) - Input: string containing the error message - Usage: +If it worked before and fails now with no other change, the most likely cause is that `WoR-Flasher.app` was rebuilt or reinstalled since it was last granted access — see below. -```bash -command-that-downloads-windows || error "Windows failed to download! Check your internet connection and try again." -``` +WoR-Flasher detects this specific failure and opens `System Settings > Privacy & Security > Full Disk Access` for you automatically, naming the exact app that needs the toggle: `WoR-Flasher.app` and its bundle path for the packaged app, or `Terminal.app`/`iTerm.app` for a CLI run. In GUI mode, the failure dialog also shows an **Open Settings** button. For a mounted removable-volume denial, it opens **Files and Folders** first: enable the narrower **Removable Volumes** permission for `bash` when macOS lists it. If that permission is unavailable or still denied, open **Full Disk Access**, click `+`, press `Shift`+`Command`+`G`, enter `/bin/bash`, click **Open**, and enable its toggle. Quit the app completely (not just the window) and try again. -- `echo_green` and `echo_red` - announce the success or failure of an action in colored text. `status` prints blue progress text. - Input: string containing message - Usage: +If the exact `WoR-Flasher.app` is already enabled and macOS still blocks the write, also grant Full Disk Access to the app you launched it from, such as `Visual Studio Code.app`, `Terminal.app`, or `iTerm.app`. WoR-Flasher is a shell-script app bundle, and macOS can attribute protected disk access to the launcher or interpreter chain instead of the displayed app bundle. -```bash -status "Now, downloading windows... please wait" -echo_green "Done" -echo_red "That did not work, but it is not fatal" -``` +If you rebuild or move `WoR-Flasher.app` (for example after re-running the packaging script), macOS treats it as a new app and its previously granted Full Disk Access is revoked, so you will need to re-add and re-enable it once. -- `package_available` - Determines if a package is possible to install from the apt repositories - Input: one name of a package - Usage: +
-```bash -if package_available yad ;then - echo "yad can be installed" -fi -``` +
+Rainbow screen -- `install_packages` - Checks for and installs a quoted list of packages. - Input: string containing a space-separated list of packages - Usage: +The Raspberry Pi firmware did not start UEFI. Reflash the drive and wait for verification to finish. Also update the Pi EEPROM bootloader, and avoid `UEFI_USE_LATEST=1` unless you are intentionally testing firmware. -```bash -install_packages 'yad aria2 cabextract wimtools chntpw genisoimage exfat-fuse exfatprogs wget parted dosfstools unzip git' -``` +On Pi 4, three ACT LED blinks indicate that `start4.elf` is missing, four indicate that it failed to launch, and seven indicate that `RPI_EFI.fd` is missing. -- `download_from_gdrive` - Downloads a publicly shared large-file from Google Drive. [Here's the tutorial](https://medium.com/@acpanjan/download-google-drive-files-using-wget-3c2c025a8b99) I adapted it from. - Inputs: File ID, output filename - Usage: +
-```bash -download_from_gdrive 1WHyHFYjM4WPAAGH2PICGEhT4R5TlxlJC WoR-PE_Package.zip -``` +
+UEFI splash, then freeze -- `get_partition` - A clean, reliable way to determine the block-device of a partition. - Input: block device of drive, partition number - Usage: +On Pi 3 or Pi 4, use Windows 11 build `22631.2861` or another compatible `22631.x` release. In UEFI, verify that `System Table Selection` is `ACPI` and that Secure Boot is disabled. -```bash -get_partition /dev/sda 2 -#Assuming partition 2 exists, the above command returns "/dev/sda2" +
-get_partition /dev/mmcblk0 2 -#Assuming partition 2 exists, the above command returns "/dev/mmcblk0p2" +
+PXE boot, or no local boot option -get_partition /dev/mmcblk0 all -#Returns every partition within the drive, each one on a line -``` +Reflash with the default pinned UEFI firmware and wait for `Written image verified successfully`. WoR-Flasher pins Pi 4 UEFI to v1.50, because v1.52 and v1.53 do not boot from microSD ([pftf/RPi4#285](https://github.com/pftf/RPi4/issues/285)). Avoid `UEFI_USE_LATEST=1` on a Pi 4 for the same reason. -- `get_device_name` - Determine a human-readable name for the given storage drive. - Input: block device of drive - Usage: +During first boot, Windows Setup creates Windows Boot Manager. If an installed system has lost that entry, open `Boot Maintenance Manager > Boot Options > Add Boot Option`, select `EFI\Microsoft\Boot\bootmgfw.efi`, and place it above the network boot entries. -```bash -get_device_name /dev/sda -``` +
-- `get_size_raw` - Determines the size of a drive in bytes. - Input: block device of drive - Usage: +
+Ethernet does not work, and the MAC address is all zeros -```bash -get_size_raw /dev/sda -``` +In Windows, `ipconfig /all` shows the Broadcom GENET adapter with a physical address of `00-00-00-00-00-00` and only an APIPA address (`169.254.x.x`). The driver is fine; the UEFI firmware never gave it a MAC. -- `list_devs` - list available storage drives in a human-readable, colored format. - Usage: +This affects Pi 4 UEFI **v1.51 and v1.52** ([pftf/RPi4#283](https://github.com/pftf/RPi4/issues/283)). WoR-Flasher now pins v1.50, which is unaffected, so reflashing with the default settings fixes it. Do not work around it with `UEFI_USE_LATEST=1`: v1.53 fixes the MAC but does not boot from microSD. -```bash -list_devs -``` +To fix an existing installation without reflashing, either update the firmware on the boot partition using the [boot partition mount utility](https://worproject.com/downloads#boot-partition-mount-utility), or set a MAC by hand in `Device Manager` > the adapter > `Advanced` > `Network Address`. -- `get_bid` - Get the latest Windows build ID for either Windows 10 or Windows 11 that the target Raspberry Pi can actually boot. When `RPI_MODEL` is "`3`" or "`4`", builds requiring ARMv8.1 are skipped. - Input: "`10`" or "`11`" - Usage:   +
-```bash -get_bid 11 -``` +
+Several "Unknown device" entries in Device Manager -- `cpu_supports_bid` - Exit 0 if the target Pi's CPU can run the given build, otherwise exit 1. Depends on `RPI_MODEL` being set. - Input: build ID - Usage: +Expected. No Windows drivers exist for some Pi hardware - the CYW43455 Wi-Fi, the camera interface, and VCHIQ among others. See the [driver status table](https://github.com/worproject/RPi-Windows-Drivers#status) for what is and is not supported. Wi-Fi in particular will not work; use Ethernet or a supported USB adapter. -```bash -if cpu_supports_bid 26100.1742 ;then - echo "this build will boot" -fi -``` +
-- `list_bids_supported` - Same as `list_bids`, but omits builds the target Pi cannot run. - Input: "`10`" or "`11`" - Usage: +
+WoR-PE says the initialization disk must be recreated -```bash -list_bids_supported 11 -``` +The installer cannot find unallocated space for the Windows target partition. Reflash with a current checkout and wait for final verification. Do not manually expand `WOR_INSTALL`; the unused space after that staging partition is required during installation. -- `get_os_name` - Get human-readable name of operating system. - Input: valid Windows build ID - Usage: +
-```bash -get_os_name 22631.2861 -``` +
+Keyboard does not work in UEFI -### Example function and variable usage +Press `Esc` repeatedly immediately after power-on. Connect a wired keyboard directly to a USB 2.0 port, and disconnect hubs and unnecessary USB devices. -This code will non-interactively flash Windows 11 to `/dev/sda` and add overclock settings. You can copy and paste the code into a terminal, or save this as a shell script. +
-```bash -#make all variables we set to be visible to the script (only necessary if you run this in a terminal) -set -a +
+Only 3 GB of RAM on Pi 4 -#First, source the script so its functions are available -source ~/wor-flasher/install-wor.sh source +This is disabled automatically by default; see [Pi 4 RAM unlock](#pi-4-ram-unlock). To do it manually instead, set `Device Manager > Raspberry Pi Configuration > Advanced Configuration > Limit RAM to 3 GB` to `Disabled` (see the [worproject FAQ](https://worproject.com/faq#only-3-gb-of-ram-are-available-how-can-i-fix-this)). On Compute Module 4, set the limit to 1 GB instead; leaving it fully disabled or at 3 GB breaks USB. -#Determine the latest Windows 11 update ID using a function -BID="$(get_bid 11)" +
-#set destination RPi model -RPI_MODEL=4 +## Development -#choose language -WIN_LANG=en-us +```bash +./tests/run-tests.sh # static checks, plus Linux integration where available +./tests/run-tests.sh --macos-auth # mocked finalizer authorization; no sudo or disks +./tests/run-tests.sh --gui # walk the GUI in DRY_RUN mode +./tests/run-tests.sh --walkthrough # fake drives, then the CLI interactively +./tests/run-linux-integration.sh # force the Dockerised Linux suite +LINUX_TEST_IMAGE=node:22-bookworm-slim ./tests/run-linux-integration.sh # include Node-based Linux checks +npm run check # shell syntax, package-plan checks, and release-tool syntax +npm run build:macos # generate release/macos/WoR-Flasher.app +node src/package-macos-app.mjs --check # verify generated macOS runtime matches canonical sources +shellcheck --severity=error src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh +``` -#set the device to flash -DEVICE=/dev/sda +The suite creates loopback devices as stand-in drives, so nothing can be written to physical storage. Tests call the real functions out of `install-wor.sh` rather than restating their logic, which means a test cannot pass against behaviour the shipped script no longer has. -#set a custom config.txt -CONFIG_TXT="over_voltage=6 -arm_freq=2147 -gpu_freq=750 +The Linux wrapper installs its test dependencies, including Python for answer-file XML validation, only inside its disposable container. Its default Ubuntu image does not include Node.js; use the Node image above to exercise the Node-based checks on Linux as well. Platform-specific macOS tests still run on the macOS host. -# don't change anything below this point # -arm_64bit=1 -enable_uart=1 -uart_2ndstage=1 -enable_gic=1 -armstub=RPI_EFI.fd -disable_commandline_tags=1 -disable_overscan=1 -device_tree_address=0x1f0000 -device_tree_end=0x200000 -dtoverlay=miniuart-bt" +On a non-Linux host the run prints three summaries — the Docker container's nested run, the integration wrapper, then the host's own run. All three must report `failed 0`. -#indicate that drive is large enough to install Windows to itself -CAN_INSTALL_ON_SAME_DRIVE=1 +CI runs ShellCheck plus the suite on Ubuntu and macOS, and a one-model dry-run integration pass. See [CONTRIBUTING.md](CONTRIBUTING.md) for house style and for the traps that have already caught us. -~/wor-flasher/install-wor.sh -``` +Pushing a new semantic version tag matching `product.version` in +[`src/config/metadata.json`](src/config/metadata.json) publishes a GitHub Release after those checks pass. +The shell metadata module reads that same canonical value. +The same workflow can be manually dispatched with `tag_name: auto` to select the next patch, or an +explicit `vX.Y.Z` tag once the workflow is available on the repository's default branch. +By default, manual releases update the shared version metadata, macOS app +bundle metadata, documentation histories, and embedded runtime before validation; select **Do not +update project version files before validating a new manual tag** only when those changes are +already committed. A new manual tag is created and published only after validation. Each release +includes a +portable `wor-flasher--linux-rpi.zip` for Linux and Raspberry Pi OS users, a +`WoR-Flasher--macos.zip` app bundle, the app launcher's verified runtime-update payload, +and `SHA256SUMS`. Both runtime distributions include this README, LICENSE, and NOTICE. The portable +ZIP contains the runtime source; GitHub additionally offers the full repository source archive for +each tag. The macOS app bundle is unsigned and unnotarized; verify +downloaded artifacts against `SHA256SUMS` before use. + +### Repository layout + +The root entry points remain stable for existing users and integrations: `install-wor.sh` is the engine and CLI, `install-wor-gui.sh` is the Linux/macOS front end, and `install-wor-hook.sh` is the automation adapter. The macOS app template lives under `src/macos-app`, while `npm run build:macos` generates `release/macos/WoR-Flasher.app` with an embedded runtime and manifest from the canonical files. Do not edit generated release output directly. + +Shared UI artwork lives in `assets/`, and boot and setup inputs live in `config-templates/`. + +Shared data and low-level helpers live under `src/lib/`. Entry points load these modules explicitly; the library files do not source one another: + +| Module | Responsibility | +| ----------------- | -------------------------------------------------------------------------------------------- | +| `metadata.sh` | Product identity, asset metadata and the named macOS AppleScript host | +| `dependencies.sh` | Homebrew and Linux package declarations shared by launcher preflight and engine installation | +| `paths.sh` | Platform-neutral path resolution used by engine bootstrap | +| `cleanup.sh` | Shared mount, device and temporary-file cleanup registration | + +## Support + +| Where | For | +| ------------------------------------------------------------- | ------------------------------------------------------------- | +| [Botspot/wor-flasher](https://github.com/Botspot/wor-flasher) | Report issues, share feedback, request features or contribute | +| [Botspot Software Discord](https://discord.gg/RXSTvaUvuu) | Real-time help with WoR-Flasher | +| [WoR project Discord](https://discord.gg/jQCpfVK) | Windows on Raspberry, the operating system | +| [worproject.com contact](https://worproject.com/contact) | The WoR developers directly | +| [Security policy](SECURITY.md) | Anything that should not be public | + +## Maintainer partnership + +WoR-Flasher is a community project created by **[Botspot](https://github.com/Botspot)** and directly maintained by **[Blackout Secure](https://blackoutsecure.app/)**. This partnership improves its documentation, testing and cross-platform experience while keeping Botspot's original authorship and project direction visible. + +Report issues, share feedback, request features or contribute through the [Botspot/wor-flasher repository](https://github.com/Botspot/wor-flasher). + +Support continued development by [sponsoring Botspot](https://github.com/sponsors/Botspot) or [buying Blackout Secure a coffee](https://github.com/sponsors/blackoutsecure?frequency=one-time&amount=8) through GitHub Sponsors. + +Blackout Secure is a cybersecurity, secure application development, cloud and AI security consultancy. Its open-source work focuses on practical automation, privacy-conscious tooling and dependable developer workflows. Learn more at [blackoutsecure.app](https://blackoutsecure.app), browse the organization's projects at [github.com/blackoutsecure](https://github.com/blackoutsecure), or find Dr Bill McIlhargey through [Linktree](https://linktr.ee/billmcilhargey). + +This directly maintained source is intended to strengthen the wider community around [Botspot's projects](https://github.com/Botspot), [Windows on Raspberry](https://worproject.com/) and the people who use them. + +## What this maintained source adds + +Building on Botspot's original work, this maintained source adds: + +| Area | Added capability | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Hosts | macOS support alongside Debian-based Linux, including `diskutil`, `hdiutil`, `sgdisk`, native password handling and safe external-drive detection | +| Interfaces | Native AppKit/JXA windows on macOS, `yad` progress on Linux, Advanced Options on both, and an explicit `install-wor.sh --gui` entry point | +| Installer flow | One shared `install-wor.sh` engine with the GUI as a presentation layer, so validation, settings, downloads and flashing do not drift between front-ends | +| Progress and failures | File-backed progress reporting, real installer exit codes, abort handling, durable error markers and retained failure logs with a configurable `WOR_LOG_FILE` path | +| Safety | Boot-drive protection, free-space preflight, cached-payload SHA-256 manifests, written-image verification and clearer cache modes | +| Windows setup | Offline-OOBE support and the Pi 4 RAM-unlock action delivered to the installed OS through WoR-PE's `prefinalize.cmd` hook, rather than only copying files to media roots | +| Firmware and drivers | Tested Pi 4 UEFI pinning, including the v1.50 choice that avoids both the v1.51 zero-MAC bug and the v1.52/v1.53 microSD boot regression | +| Quality | Cross-platform static checks, ShellCheck, loopback-drive integration tests, XML validation, mutation-tested anti-drift checks and macOS/Linux CI | + +These additions are maintained directly by Blackout Secure in cooperation with Botspot and the wider Windows on Raspberry community. + +## Related resources + +- [worproject.com](https://worproject.com/) — the WoR-PE installer, UEFI firmware and drivers that WoR-Flasher assembles +- [Advanced customization guide](https://worproject.com/guides/wor-imager-customization) — the `scripts/prefinalize.cmd` hook and `settings.ini` options. Written for the official WoR imager and not verified against WoR-Flasher's headless media +- [How can I update the drivers?](https://worproject.com/faq#how-can-i-update-the-drivers) — updating drivers on an already-installed system +- [Boot partition mount utility](https://worproject.com/downloads#boot-partition-mount-utility) — mount the boot partition later to edit `config.txt` or firmware +- [PiMon](https://worproject.com/downloads#pimon) — hardware monitor (CPU temperature and so on) for Windows on Raspberry Pi +- [How to perform OS updates](https://worproject.com/guides/performing-os-updates) — using Windows Update on a WoR installation +- [BVM](https://github.com/Botspot/bvm) — Botspot's newer project: Windows 11 in a KVM virtual machine on ARM Linux, rather than on bare metal + +## Versions + +- **2.0.0** + - Modernized the cross-platform flashing workflow, release tooling and configuration. + - Added a native standalone macOS runtime with validated, rollback-capable updates. + - Improved macOS disk preparation, remount handling and post-write verification resilience. + - Reused one administrator authorization for late disk writes and automatically chose Ignore only for the matching unreadable-disk alert during active GUI writes. + - Added password-retry resume, configurable completion sounds and desktop notifications. + - Corrected CI progress-test prerequisites and privileged loop-device inspection, and aligned release version lookup with the canonical JSON metadata. + - Included README, LICENSE, and NOTICE in both packaged runtime distributions. + - Aligned bootstrap and update discovery with the publishing repository and enforced the documented macOS 13 minimum. + - Refreshed staged files on package writes, rejected linked or stale runtime manifests, and propagated version-build failures. + - Kept the optional Pi 4 UEFI Shell handoff independent of answer-file customization. + - Reworked Advanced Options with a compact, dedicated `config.txt` editor. + This maintained source uses its own version line. The product name, window title, current version, runtime file list and pinned system defaults are defined in [`src/config/metadata.json`](src/config/metadata.json), loaded by [`src/lib/metadata.sh`](src/lib/metadata.sh), and checked against [`package.json`](package.json) and the macOS app property list. The macOS launcher synchronizes those values into `CFBundleDisplayName`, `CFBundleExecutable`, `CFBundleName`, `CFBundleShortVersionString` and `CFBundleVersion`. The same release history is repeated at the top of [`install-wor.sh`](install-wor.sh). + +- **1.0.2** + - `WoR-Flasher.app` can run independently of a Git checkout using an immutable embedded runtime, validated writable runtime copies under Application Support, and active/previous/embedded fallback. + - Detached runtime updates reject downgrades and verify the archive digest, extracted file digests, file modes, and archive entry safety before atomic promotion. + - A standalone `install-wor-hook.sh` now obtains a complete trusted checkout automatically when no adjacent engine is available. + - The native macOS partnership announcement now has compatible attributed-text construction, dark-mode contrast and non-overlapping layout on current JXA runtimes. + - The partnership banner is now 800x533, so the Linux announcement window fits on screen. `yad` draws `--image` at its native size and cannot scale it down. + - Linux dialogs that size themselves to their content no longer log a `gtk_window_resize` assertion warning. + - A double-clickable macOS app now checks for clean fast-forward updates, installs missing Homebrew formulae with consent and offers non-destructive repair of missing tracked runtime files. + - Repeated GUI launches now activate the existing macOS window instead of opening concurrent workflows, including launches from another checkout or version. + - Partnership messaging and default update checks now use the directly maintained Blackout Secure source while preserving Botspot's original authorship. + - The engine, GUI, named macOS JXA host and app property list now share the canonical `WoR-Flasher` name and `1.0.2` version metadata. +- **1.0.1** + - **Pi 4 UEFI pinned to v1.50**, the only release where both the Ethernet MAC and microSD boot work. v1.51 (the previous pin) and v1.52 report a MAC of `00:00:00:00:00:00`, leaving Windows with no DHCP ([pftf/RPi4#283](https://github.com/pftf/RPi4/issues/283)); v1.53 fixes that but still does not boot from microSD ([pftf/RPi4#285](https://github.com/pftf/RPi4/issues/285)). + - The Pi 4 RAM unlock and the offline-OOBE answer file now reach the installed OS through WoR-PE's prefinalize hook. The media-root copies alone were never read, because WoR-PE applies `install.wim` with DISM rather than running Windows Setup's media flow. +- **1.0.0** — First versioned Blackout Secure release. + - **macOS host support**: `diskutil`/`hdiutil` drive discovery, and `sgdisk` GPT partitioning that keeps `WOR_BOOT` as partition 1. An extra ESP made the Pi 4 fall back to PXE boot. + - **A native macOS interface**: AppKit/JXA wizard, progress window, Advanced Options window and error dialogs. + - **No visible terminal in GUI mode**: the engine reports progress over a file, and each front-end renders it — AppKit on macOS, `yad` on Linux. Administrator access is requested through a native password dialog on both. + - **Post-flash verification** of partitions, filesystems, boot files, WIM images and the copied `install.wim` checksum. + - **Offline Windows OOBE** via a shipped `Autounattend.xml`, on by default. + - **Automatic Pi 4 3 GB RAM unlock** after the WoR-PE reboot, including the BCD `truncatememory` cap. + - **Pinned, overridable UEFI firmware and driver versions.** Pi 4 stays on UEFI v1.50, the only release where both the Ethernet MAC and microSD boot work. + - **Cache modes with SHA-256 payload manifests**, a free-space preflight, and `HideEmptyDrives` written into the cached WoR-PE `settings.ini`. + - **Editable `config.txt` from `config-templates/`**, applied by the CLI and the GUI alike. + - **One engine, two front-ends**: `install-wor-gui.sh` sources `install-wor.sh` and adds only windows. A function defined in both files now fails a test. + - **Explicit `--gui` entry point.** The front-end is never chosen by sniffing `DISPLAY`. + - **A test suite**, plus ShellCheck, macOS and Linux dry-run CI. +- **0.x** — Original Botspot development history. Highlights, oldest first: the initial WoR automation, the self-updater, the "next steps" window, a complete rewrite to use ESD releases, download-to-RAM support, Pi 5 support, a GitHub API fallback for UEFI firmware, empty block devices filtered out of the drive list, and SHA-256 hashed ESD image handling. ## Contributing -This repository is looking for a maintainer, so contributions are genuinely welcome. +Pull requests are welcome at [Botspot/wor-flasher](https://github.com/Botspot/wor-flasher) — please read [CONTRIBUTING.md](CONTRIBUTING.md) first, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md). -> [!IMPORTANT] -> `install-wor.sh` does not update its source checkout automatically. For a clean Git checkout only, you can opt in to the updater with `NO_UPDATE=0`; it skips the update when tracked files have uncommitted changes: -> -> ```bash -> NO_UPDATE=0 ~/wor-flasher/install-wor.sh -> ``` - -Useful when testing changes: +## Contributors -| Command | Purpose | -| ------------------------------------ | ----------------------------------------------------------------------------------------------- | -| `./tests/run-tests.sh` | Run the automated suite; on non-Linux hosts it uses Docker for Linux integration when available | -| `./tests/run-linux-integration.sh` | Run only the privileged Ubuntu-container integration path, useful for debugging Docker setup | -| `./tests/run-tests-gui.sh` | Run the GUI walkthrough test with Linux/display/yad preflight | -| `./tests/run-tests.sh --walkthrough` | Create fake drives, then step through the terminal interface by hand | -| `./tests/run-tests.sh --gui` | Same, but launch the graphical interface | -| `./tests/run-tests.sh --full` | Include the real multi-gigabyte Windows image download | -| `./tests/run-tests.sh --clean` | Remove the test workspace and detach its loop devices | -| `tests/test-lib.sh` | Shared test output helpers used by the test entrypoints | -| `bash -n install-wor.sh` | Check syntax without running anything | -| `DRY_RUN=1 ...` | Run the whole flow but stop before touching the drive | -| `USE_CACHE=1 ...` | Reuse downloaded components so iterations are fast | -| `DEBUG=1 ./terminal-run ...` | Print which terminal emulator was selected | +**Original author.** WoR-Flasher was created by **[Botspot](https://github.com/Botspot)**, who also created [Pi-Apps](https://github.com/Botspot/pi-apps) and [BVM](https://github.com/Botspot/bvm). This maintained source rests on five years of his work, given away for free. If you find WoR-Flasher useful, [consider sponsoring him](https://github.com/sponsors/Botspot). -The harness needs Linux loop devices and passwordless `sudo` for the integration tests. On Linux, `./tests/run-tests.sh` creates loopback drives directly. On non-Linux hosts, the same command tries Docker and runs the integration suite inside a privileged Ubuntu container; if Docker is missing or unavailable, it reports a skip instead of failing. The container uses `/tmp/wor-flasher-test-workspace`, so loop devices, downloads and caches disappear with the container unless you explicitly pass `--keep` to the inner harness. The GUI walkthrough has its own wrapper, `./tests/run-tests-gui.sh`, which skips cleanly without a Linux desktop display and `yad`. The harness detects the newest bootable build for each model from the catalog, so no build number is hardcoded. +**Project contributors** ([historical list](https://github.com/Botspot/wor-flasher/graphs/contributors)): -GitHub Actions runs ShellCheck on every shell entrypoint and a focused Ubuntu dry-run integration job with `TEST_MODELS=4`. That keeps pull requests covered without downloading the full Windows image or exercising every Raspberry Pi firmware package on every push. +| | | | | | | | +| :------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------: | +| [
Botspot](https://github.com/Botspot) | [
Blackout Secure](https://blackoutsecure.app) | [
NoozAbooz](https://github.com/NoozAbooz) | [
Itai-Nelken](https://github.com/Itai-Nelken) | [
larskanis](https://github.com/larskanis) | [
Marcinoo97](https://github.com/Marcinoo97) | [
ryanfortner](https://github.com/ryanfortner) | -Both installer scripts are plain Bash with no build step. `install-wor-gui.sh` sources `install-wor.sh` for its functions, so shared installer logic belongs in the latter. Test entrypoints share reporting helpers through `tests/test-lib.sh`. +**Maintainer partnership.** **[Blackout Secure](https://blackoutsecure.app)** — represented here by **Dr Bill McIlhargey** ([links](https://linktr.ee/billmcilhargey)) — is partnering with **[Botspot](https://github.com/Botspot)** to provide ongoing maintenance and support while helping improve this project. Blackout Secure's contributions include macOS host support, native progress and Advanced Options windows, post-flash verification, the shared-engine refactor, documentation, community health files, the expanded test suite, and continued community support. If WoR-Flasher helps you, [consider sending Blackout Secure a cup of coffee](https://github.com/sponsors/blackoutsecure?frequency=one-time&amount=8) to support that work. -## Is this legal? +**Projects this tool assembles**, each with its own authors and license: -Yes. All proprietary Windows components are downloaded straight from Microsoft's update servers using [uupdump](https://uupdump.net). Consider reading [this debate](https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=318599) that took place on the Raspberry Pi Forums. At the conclusion of the thread, Raspberry Pi **employees** [confirm](https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=318599#p1907313) that WoR is completely legal. The OS is unlicenced just like a regular Windows ISO, which can be activated via an activation key or by logging in with a pre-licensed Microsoft account. +- [Windows on Raspberry](https://worproject.com/) — the PE-based installer +- [RPi-Windows-Drivers](https://github.com/worproject/RPi-Windows-Drivers) — Windows ARM64 drivers for the Pi +- [pftf/RPi4](https://github.com/pftf/RPi4) and [pftf/RPi3](https://github.com/pftf/RPi3) — Raspberry Pi UEFI firmware +- [worproject/rpi5-uefi](https://github.com/worproject/rpi5-uefi) — Pi 5 UEFI firmware +- [UUP dump](https://uupdump.net/) — retrieves Windows directly from Microsoft's update servers ## License -This repository does not currently contain a `LICENSE` or `COPYING` file, which means the code defaults to exclusive copyright. Botspot's related projects, [Pi-Apps](https://github.com/Botspot/pi-apps) and [BVM](https://github.com/Botspot/bvm), are both GPL-3.0. Adding a license here is a decision for the copyright holder. - -## Getting help - -| Problem with | Where to go | -| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | -| The WoR-flasher tool | [Open an issue](https://github.com/Botspot/wor-flasher/issues/new/choose) or the [Botspot Software Discord](https://discord.gg/RXSTvaUvuu) | -| Windows on Raspberry (the OS itself) | [Email the WoR developers](https://worproject.com/contact) or [join their Discord](https://discord.gg/jQCpfVK) | - -## Credits - -WoR-flasher automates a process built by other people. It would not exist without: - -| Project | By | -| ---------------------------------------------------------------------------------------------------- | ------------------------------------- | -| [Windows on Raspberry](https://worproject.com/) and its PE-based installer | Mario Bălănică and contributors | -| [RPi-Windows-Drivers](https://github.com/worproject/RPi-Windows-Drivers) | worproject | -| [UEFI firmware for the Pi 3 and Pi 4](https://github.com/pftf/RPi4) | Pete Batard and the pftf project | -| [UEFI firmware for the Pi 5](https://github.com/worproject/rpi5-uefi) | worproject | -| [uupdump](https://uupdump.net) | The UUP dump team | -| WoR-flasher, [Pi-Apps](https://github.com/Botspot/pi-apps) and [BVM](https://github.com/Botspot/bvm) | [Botspot](https://github.com/Botspot) | - -## Sources - -Everything WoR-flasher downloads at runtime, and every external fact stated in this README, comes from the following. - -**Components downloaded during a flash** - -| Source | Used for | -| -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | -| [worproject.com ESD catalog](https://worproject.com/) | Windows 10 and 11 ARM64 images, served from Microsoft's update servers | -| [WoR PE-based installer](https://worproject.com/downloads#windows-on-raspberry-pe-based-installer) | The installer environment injected into `boot.wim` | -| [pftf/RPi4](https://github.com/pftf/RPi4/releases) | UEFI firmware for the Pi 4 and Pi 400 | -| [pftf/RPi3](https://github.com/pftf/RPi3/releases) | UEFI firmware for the Pi 3 and Pi 2 v1.2 | -| [worproject/rpi5-uefi](https://github.com/worproject/rpi5-uefi/releases) | UEFI firmware for the Pi 5 | -| [RPi-Windows-Drivers](https://github.com/worproject/RPi-Windows-Drivers/releases) | ARM64 device drivers, archived at v0.17 | - -**Reference material** - -| Source | Used for | -| ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------ | -| [Windows on Raspberry FAQ](https://worproject.com/faq) | The ARMv8.1 build cutoff, the 3 GB RAM limit, Pi 5 support status | -| [RPi-Windows-Drivers status page](https://github.com/worproject/RPi-Windows-Drivers#status) | Every per-model driver table | -| [Windows 11 Home and Pro lifecycle](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-home-and-pro) | Windows 11 end-of-support dates | -| [Windows 10 Home and Pro lifecycle](https://learn.microsoft.com/en-us/lifecycle/products/windows-10-home-and-pro) | Windows 10 end-of-support date | -| [Windows 11 Enterprise and Education lifecycle](https://learn.microsoft.com/en-us/lifecycle/products/windows-11-enterprise-and-education-version-21h2) | Extended support dates | -| [Windows Processor Requirements](https://learn.microsoft.com/en-us/windows-hardware/design/minimum/windows-processor-requirements) | Supported processor lists per Windows release | -| [Raspberry Pi processors](https://www.raspberrypi.com/documentation/computers/processors.html) | CPU cores and architecture per model | -| [Raspberry Pi config.txt](https://www.raspberrypi.com/documentation/computers/config_txt.html) | `config.txt` option reference | -| [uupdump](https://uupdump.net) | Windows image generation, referenced in [Is this legal?](#is-this-legal) | -| [Pi-Apps](https://github.com/Botspot/pi-apps) | Recommended install method and the More RAM add-on | -| [BVM](https://github.com/Botspot/bvm) | The recommended alternative for Wi-Fi and graphics acceleration | +Released under the [GNU General Public License v3.0](LICENSE), matching Botspot's [BVM](https://github.com/Botspot/bvm). + +> [!IMPORTANT] +> The pre-existing Botspot code shipped **without** a license file and therefore carries no explicit grant. The Blackout Secure additions are offered under GPL-3.0 without reservation. Read [NOTICE](NOTICE) before commercial redistribution or relicensing. + +WoR-Flasher does **not** redistribute Windows. Proprietary components are downloaded straight from Microsoft's own update servers via [UUP dump](https://uupdump.net/). This is legal — Raspberry Pi employees [confirmed as much](https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=318599#p1907313) on the Raspberry Pi Forums. The resulting installation is unlicensed, exactly like a retail Windows ISO, and needs a product key or a pre-licensed Microsoft account to activate. + +No warranty. Neither Botspot nor Blackout Secure can be held responsible for data loss. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..510cde7 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,90 @@ +# Security Policy + +## Supported versions + +This fork keeps a single supported line. Fixes land on the default branch and in the +next version; there are no long-term support branches. + +| Version | Supported | +| ------------------------------ | ------------------------------ | +| 2.0.x | Yes | +| 1.0.x | No | +| Upstream `Botspot/wor-flasher` | Report to [upstream][upstream] | + +Check what you are running with: + +```bash +./install-wor.sh --version +``` + +## Reporting a vulnerability + +**Please do not open a public issue for a security problem.** + +Use [GitHub's private vulnerability reporting][report] on this repository, or contact +Blackout Secure at [blackoutsecure.app][bos]. + +Please include: + +- The version (`./install-wor.sh --version`) and your host OS. +- What an attacker can do, and what they need in order to do it. +- Steps to reproduce, ideally with `DRY_RUN=1` so nothing is written to a drive. + +You can expect an acknowledgement within a few days. We will tell you what we intend to +do and roughly when, and we will credit you in the fix unless you would rather we did +not. + +If the problem also affects [Botspot/wor-flasher][upstream], please report it there too. +This fork cannot ship a fix for upstream's users. + +## What we consider a vulnerability + +This tool needs `sudo`, downloads several gigabytes over the network, and then erases a +whole disk. The interesting failure modes follow from that: + +- **Writing to the wrong drive.** Anything that lets a target other than the one the + user chose be erased, or that defeats `is_safe_target_device` and the host boot-disk + guard. +- **Tampered downloads being accepted.** The WoR-PE installer is pinned by SHA-256, ESD + images are checked against Microsoft's published SHA-1, and cached payloads carry a + SHA-256 manifest. A way to get unverified content past any of those is a + vulnerability. +- **Privilege escalation through the sudo helpers.** The askpass scripts and the + credential keep-alive run while the user's timestamp is live. +- **Command or argument injection** through a filename, drive label, environment + variable or `config.txt` body that reaches a shell, `osascript`, or `yad`. +- **Secrets in the logs.** Failures keep timestamped logs under `$DL_DIR/logs/`, refresh + `$DL_DIR/last-run.log`, or write wherever `WOR_LOG_FILE` points; they must never contain a + password. + +## What we do not consider a vulnerability + +- **Needing root.** Flashing a disk requires it. That is the tool's purpose. +- **Downloading Windows from Microsoft.** This is done over HTTPS from Microsoft's own + update servers via [uupdump][uupdump], and it is [legal][legality]. No proprietary + material is redistributed here. +- **Erasing the drive you selected.** You are warned twice. +- **`VERIFY_TLS=0`.** It exists for hosts with a broken CA bundle, is documented as a + downgrade, and is opt-in. +- **The release check.** It is enabled by default, makes one read-only HTTPS request to the + GitHub releases API, and only prints a notice. It never writes to disk, never executes + downloaded content, and never modifies the installation. It can be disabled with + `CHECK_FOR_UPDATES=0` or the legacy `NO_UPDATE=1`. Report it if you can make it do + anything beyond reporting a version. +- Vulnerabilities in Windows itself, in the WoR PE installer, or in the Pi UEFI + firmware. Report those to [worproject.com][wor] and [pftf][pftf] respectively. + +## Disclosure + +We ask for coordinated disclosure. Give us a reasonable window to ship a fix — 90 days +is the default, shorter if the issue is being exploited. We will not take legal action +against anyone who reports in good faith and does not exfiltrate data, degrade a service +or access an account that is not theirs. + +[upstream]: https://github.com/Botspot/wor-flasher/issues +[report]: https://github.com/blackoutsecure/wor-flasher/security/advisories/new +[bos]: https://blackoutsecure.app +[uupdump]: https://uupdump.net +[legality]: https://www.raspberrypi.org/forums/viewtopic.php?f=29&t=318599 +[wor]: https://worproject.com/contact +[pftf]: https://github.com/pftf diff --git a/announcement.png b/announcement.png deleted file mode 100644 index a44730f..0000000 Binary files a/announcement.png and /dev/null differ diff --git a/announcement.svg b/announcement.svg deleted file mode 100644 index fd2f999..0000000 --- a/announcement.svg +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - right smoke - - Hold up!! - Hi there. Botspot here - developer of WoR-Flasher.​The Windows on Raspberry project seems to be dying. Wi-Fi still does not work,​the Pi500, CM5, and Pi5 16GB won't boot it, and many of the devs have left.​​I started a new project to solve these issues.​It's called BVM, and it runs Windows in a virtual machine on Pi OS.​Wi-Fi works, audio playback works, everything that works on WoR works,​and you can pass-through USB devices for Windows to directly use.​As a bonus, you can use Raspberry Pi OS on the outside at the same time.​Or any ARM Linux really.​BVM often runs faster than WoR because it deletes Windows bloatware and ads.​​Note:​If you still want to proceed with WoR-Flasher and use a Pi4, you need to choose​an older Windows 11 version: 22631.2861. Newer versions don't boot on Pi4.​Thanks for reading! :-) - - fluent-2023 - - - - - - icon - - - logo-full - - \ No newline at end of file diff --git a/logo-full.png b/assets/logo-full.png similarity index 100% rename from logo-full.png rename to assets/logo-full.png diff --git a/next-steps.png b/assets/next-steps.png similarity index 100% rename from next-steps.png rename to assets/next-steps.png diff --git a/overview.png b/assets/overview.png similarity index 100% rename from overview.png rename to assets/overview.png diff --git a/assets/partnership.png b/assets/partnership.png new file mode 100644 index 0000000..94aa396 Binary files /dev/null and b/assets/partnership.png differ diff --git a/ram.png b/assets/ram.png similarity index 100% rename from ram.png rename to assets/ram.png diff --git a/config-templates/config.json b/config-templates/config.json new file mode 100644 index 0000000..b7231c9 --- /dev/null +++ b/config-templates/config.json @@ -0,0 +1,45 @@ +{ + "$schema": "./config.schema.json", + "target": { + "rpiModel": 4, + "device": "", + "canInstallOnSameDrive": true + }, + "media": { + "winLang": "en-us", + "bid": "22631.2861", + "sourceFile": "", + "downloadDir": "~/wor-flasher-files", + "useCache": 1 + }, + "customization": { + "applyCustomConfigTxt": true, + "oobeNetworkBypass": true, + "pi4AutoDisable3Gb": true, + "uefiUseLatest": false, + "driversUseLatest": true, + "hideEmptyDrives": true + }, + "userAccount": { + "setupAccount": false, + "username": "", + "password": "", + "localeSetup": true, + "locale": "" + }, + "execution": { + "dryRun": false, + "skipImageVerification": false, + "verifyTls": true, + "checkForUpdates": true + }, + "notifications": { + "playSound": true, + "showNotification": true, + "sounds": { + "macos": "Glass", + "linux": "complete", + "windows": "" + } + } +} diff --git a/config-templates/config.schema.json b/config-templates/config.schema.json new file mode 100644 index 0000000..0a114a3 --- /dev/null +++ b/config-templates/config.schema.json @@ -0,0 +1,238 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WoR-Flasher Configuration Schema", + "description": "JSON schema for WoR-Flasher configuration parameters", + "type": "object", + "properties": { + "target": { + "type": "object", + "properties": { + "rpiModel": { + "type": "integer", + "enum": [3, 4, 5], + "description": "Target Raspberry Pi model (3, 4, or 5)" + }, + "device": { + "type": "string", + "description": "Target block device path (e.g. /dev/sda or /dev/disk4)" + }, + "canInstallOnSameDrive": { + "type": "boolean", + "description": "Whether to install Windows directly onto target drive (true) or build recovery media (false)" + } + }, + "additionalProperties": false + }, + "media": { + "type": "object", + "properties": { + "winLang": { + "type": "string", + "description": "Windows language code (e.g. en-us)" + }, + "bid": { + "type": "string", + "description": "Windows build ID (e.g. 22631.2861)" + }, + "sourceFile": { + "type": "string", + "description": "Path to an existing local Windows ARM64 ISO file" + }, + "downloadDir": { + "type": "string", + "description": "Directory path for downloads and extracted media" + }, + "useCache": { + "type": "integer", + "enum": [0, 1, 2], + "description": "Cache mode: 0 (redownload), 1 (reuse with manifest check), 2 (trust cache)" + } + }, + "additionalProperties": false + }, + "customization": { + "type": "object", + "properties": { + "applyCustomConfigTxt": { + "type": "boolean", + "description": "Whether to apply custom config.txt to the boot partition" + }, + "configTxt": { + "type": "string", + "description": "Body of config.txt for the boot partition" + }, + "oobeNetworkBypass": { + "type": "boolean", + "description": "Whether to bypass Windows OOBE network requirement" + }, + "pi4AutoDisable3Gb": { + "type": "boolean", + "description": "Whether to disable 3GB RAM limit on Pi 4 after setup" + }, + "pi4UefiShellUnlock": { + "type": "boolean", + "description": "Whether to use a one-time UEFI Shell handoff to persistently disable the Pi 4 RAM limit" + }, + "uefiUseLatest": { + "type": "boolean", + "description": "Whether to query GitHub for latest UEFI firmware instead of pinned version" + }, + "driversUseLatest": { + "type": "boolean", + "description": "Whether to query GitHub for latest drivers instead of pinned version" + }, + "hideEmptyDrives": { + "type": "boolean", + "description": "Whether to hide empty card-reader slots in WoR-PE" + } + }, + "additionalProperties": false + }, + "userAccount": { + "type": "object", + "properties": { + "setupAccount": { + "type": "boolean", + "description": "Whether to setup a local administrator account during setup" + }, + "username": { + "type": "string", + "description": "Username for optional local account" + }, + "password": { + "type": "string", + "description": "Password for optional local account (omit or leave empty in committed configs)" + }, + "localeSetup": { + "type": "boolean", + "description": "Whether to configure Windows locale and regional settings (enabled by default)" + }, + "locale": { + "type": "string", + "description": "Windows locale code (e.g. en-US or en-GB); leave empty to use the host locale or en-US" + } + }, + "additionalProperties": false + }, + "execution": { + "type": "object", + "properties": { + "dryRun": { + "type": "boolean", + "description": "Whether to run in dry-run mode without writing to storage" + }, + "skipImageVerification": { + "type": "boolean", + "description": "Whether to skip post-flash verification" + }, + "verifyTls": { + "type": "boolean", + "description": "Whether to verify TLS certificates during downloads" + }, + "checkForUpdates": { + "type": "boolean", + "description": "Whether to report a newer published release before setup work starts. The check is read-only and never modifies this installation." + }, + "noUpdate": { + "type": "boolean", + "description": "Legacy inverse of checkForUpdates; true disables the release check" + }, + "logFile": { + "type": "string", + "description": "Optional custom path for installer output. Omit to use a timestamped log under the download directory's logs/ folder, with last-run.log refreshed for support." + } + }, + "additionalProperties": false + }, + "notifications": { + "type": "object", + "description": "How the GUI announces the end of a flash. Only the completion sound is selectable; a failure always uses the platform's own error sound.", + "properties": { + "playSound": { + "type": "boolean", + "description": "Whether to play a sound when the flash finishes" + }, + "showNotification": { + "type": "boolean", + "description": "Whether to post a desktop notification when the flash finishes (macOS Notification Center, or notify-send on Linux)" + }, + "sounds": { + "type": "object", + "description": "Completion sound per host platform. A name this host cannot play falls back to the platform default.", + "properties": { + "macos": { + "type": "string", + "description": "Name of a sound in /System/Library/Sounds (e.g. Glass, Hero, Ping, Submarine)" + }, + "linux": { + "type": "string", + "description": "freedesktop sound-theme event name (e.g. complete, bell, message)" + }, + "windows": { + "type": "string", + "description": "Reserved: Windows is not a supported host, so no sound is played there" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "system": { + "type": "object", + "description": "Advanced overrides for project runtime metadata. Defaults live in src/config/metadata.json and are loaded by src/lib/metadata.sh; ordinary configs should usually omit this block.", + "properties": { + "peInstallerUrl": { + "type": "string", + "description": "Download URL for WoR-PE installer package" + }, + "peInstallerSha256": { + "type": "string", + "description": "SHA-256 digest for WoR-PE installer package" + }, + "uefiVerPi3": { + "type": "string", + "description": "Pinned UEFI firmware version for Pi 3" + }, + "uefiVerPi4": { + "type": "string", + "description": "Pinned UEFI firmware version for Pi 4" + }, + "uefiVerPi5": { + "type": "string", + "description": "Pinned UEFI firmware version for Pi 5" + }, + "driverVer": { + "type": "string", + "description": "Pinned Windows driver package version" + }, + "armv80MaxBuild": { + "type": "integer", + "description": "Maximum Windows build supported on ARMv8.0 hardware" + }, + "win11MinBuild": { + "type": "integer", + "description": "Minimum build ID for Windows 11" + }, + "win10OldestBuild": { + "type": "string", + "description": "Oldest supported Windows 10 build ID" + }, + "exampleBid": { + "type": "string", + "description": "Example build ID shown in prompts" + }, + "armv80SafeBid": { + "type": "string", + "description": "Recommended safe build ID for ARMv8.0 hardware" + }, + "repoSlug": { + "type": "string", + "description": "GitHub repository owner/repo slug checked for newer releases" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false +} diff --git a/config-templates/oobe-network-bypass.xml b/config-templates/oobe-network-bypass.xml new file mode 100644 index 0000000..edacadd --- /dev/null +++ b/config-templates/oobe-network-bypass.xml @@ -0,0 +1,8 @@ + + + + true + true + + + \ No newline at end of file diff --git a/config-templates/pi3.config.txt b/config-templates/pi3.config.txt new file mode 100644 index 0000000..0514336 --- /dev/null +++ b/config-templates/pi3.config.txt @@ -0,0 +1,11 @@ +# don't change anything below this point # +arm_64bit=1 +disable_commandline_tags=2 +disable_overscan=1 +enable_uart=1 +uart_2ndstage=1 +armstub=RPI_EFI.fd +device_tree_address=0x1f0000 +device_tree_end=0x200000 +# Uncomment if you have trouble with the UART console during boot +#core_freq=250 diff --git a/config-templates/pi4-ram-unlock-specialize.xml b/config-templates/pi4-ram-unlock-specialize.xml new file mode 100644 index 0000000..0dc0492 --- /dev/null +++ b/config-templates/pi4-ram-unlock-specialize.xml @@ -0,0 +1,11 @@ + + + + + 1 + powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%WINDIR%\Setup\Scripts\Pi4Disable3GB.ps1" + Always + + + + diff --git a/config-templates/pi4-ram-unlock.ps1 b/config-templates/pi4-ram-unlock.ps1 new file mode 100644 index 0000000..bada30c --- /dev/null +++ b/config-templates/pi4-ram-unlock.ps1 @@ -0,0 +1,63 @@ +$log = Join-Path $env:windir 'Temp\Pi4Disable3GB.log' + +try { +$source = @' +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; + +public static class Pi4Firmware +{ + [StructLayout(LayoutKind.Sequential)] + private struct Luid { public uint LowPart; public int HighPart; } + + [StructLayout(LayoutKind.Sequential)] + private struct TokenPrivileges { public uint Count; public Luid Luid; public uint Attributes; } + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool OpenProcessToken(IntPtr process, uint access, out IntPtr token); + + [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool LookupPrivilegeValue(string system, string name, out Luid luid); + + [DllImport("advapi32.dll", SetLastError = true)] + private static extern bool AdjustTokenPrivileges(IntPtr token, bool disableAll, ref TokenPrivileges privileges, uint length, IntPtr previous, IntPtr required); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + private static extern bool SetFirmwareEnvironmentVariableEx(string name, string guid, byte[] value, uint size, uint attributes); + + public static void DisableRamLimit() + { + IntPtr token; + Luid luid; + if (!OpenProcessToken(Process.GetCurrentProcess().Handle, 0x28, out token) || + !LookupPrivilegeValue(null, "SeSystemEnvironmentPrivilege", out luid)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + TokenPrivileges privileges = new TokenPrivileges { Count = 1, Luid = luid, Attributes = 2 }; + if (!AdjustTokenPrivileges(token, false, ref privileges, 0, IntPtr.Zero, IntPtr.Zero)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + byte[] disabled = new byte[] { 0, 0, 0, 0 }; + if (!SetFirmwareEnvironmentVariableEx("RamLimitTo3GB", "{CD7CC258-31DB-22E6-9F22-63B0B8EED6B5}", disabled, 4, 7)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + } +} +'@ + +Add-Type -TypeDefinition $source -Language CSharp +[Pi4Firmware]::DisableRamLimit() + +#Also clear any BCD-level memory cap, which is separate from the UEFI firmware limit above. +if ((& bcdedit /enum '{current}') -match 'truncatememory') { + & bcdedit /deletevalue '{current}' truncatememory | Out-Null +} + +Set-Content $log 'Pi 4 RAM limit disabled for this boot. Note: Raspberry Pi UEFI stores variables in RPI_EFI.fd; changes made from Windows may not persist after reboot. If the limit returns, disable RamLimitTo3GB in UEFI Device Manager or pre-edit RPI_EFI.fd.' +} catch { + Set-Content $log ('Pi 4 RAM unlock failed: ' + $_.Exception.Message) +} + +#A customization failure must not abort Windows Setup. +exit 0 diff --git a/config-templates/pi4.config.txt b/config-templates/pi4.config.txt new file mode 100644 index 0000000..1f73fe6 --- /dev/null +++ b/config-templates/pi4.config.txt @@ -0,0 +1,17 @@ +# don't change anything below this point # +arm_64bit=1 +arm_boost=1 +enable_uart=1 +uart_2ndstage=1 +enable_gic=1 +armstub=RPI_EFI.fd +disable_commandline_tags=1 +disable_overscan=1 +hdmi_drive=2 +hdmi_group=2 +hdmi_mode=87 +hdmi_cvt=1920 1200 60 6 0 0 0 +device_tree_address=0x3e0000 +device_tree_end=0x400000 +dtoverlay=miniuart-bt +dtoverlay=upstream-pi4 diff --git a/config-templates/pi5.config.txt b/config-templates/pi5.config.txt new file mode 100644 index 0000000..e6a3823 --- /dev/null +++ b/config-templates/pi5.config.txt @@ -0,0 +1,9 @@ +# don't change anything below this point # +armstub=RPI_EFI.fd +device_tree_address=0x1f0000 +device_tree_end=0x210000 +pciex4_reset=0. +framebuffer_depth=32 +disable_overscan=1 +usb_max_current_enable=1 +force_turbo=1 diff --git a/config-templates/prefinalize.cmd b/config-templates/prefinalize.cmd new file mode 100644 index 0000000..ca334fc --- /dev/null +++ b/config-templates/prefinalize.cmd @@ -0,0 +1,96 @@ +:: +:: Pre-finalization script for the Windows on Raspberry PE-based installer. +:: +:: WoR-PE applies install.wim with DISM rather than running Windows Setup's media flow, so the +:: Autounattend.xml written to the root of the media is never read - nothing performs the implicit +:: answer-file search. This hook runs just before the installer finalizes, with the freshly applied +:: Windows partition still mounted, and drops the answer file into the one location the installed +:: OS does read on first boot: %WINDIR%\Panther\unattend.xml. +:: +:: Documented at https://worproject.com/guides/wor-imager-customization +:: +@echo off + +set answerSource=%~dp0unattend.xml +set pantherDir=%WOR_DISK_WINDOWSPARTITION%\Windows\Panther + +if not exist "%WOR_DISK_WINDOWSPARTITION%\Windows\" ( + call :RaiseEvent LogWarn, "No Windows directory on %WOR_DISK_WINDOWSPARTITION%; skipping answer file." + goto :end +) + +if not exist "%answerSource%" ( + call :RaiseEvent LogWarn, "No answer file to install; leaving Windows setup unattended settings alone." + goto :stage_shell +) + +call :RaiseEvent LogInfo, "Installing offline setup answer file..." + +if not exist "%pantherDir%\" mkdir "%pantherDir%" + +copy /y "%answerSource%" "%pantherDir%\unattend.xml" >nul +if errorlevel 1 ( + call :RaiseEvent LogWarn, "Could not write the answer file; Windows setup will ask for a network." + goto :stage_shell +) + +call :RaiseEvent LogInfo, "Answer file installed to %pantherDir%\unattend.xml" + +:: +:: The Pi 4 RAM-unlock action runs during specialize, on the installed OS. Copying it there too +:: means it does not depend on the installation media still being visible and lettered by then. +:: +set ramUnlockSource=%~dp0Pi4Disable3GB.ps1 +set scriptsDir=%WOR_DISK_WINDOWSPARTITION%\Windows\Setup\Scripts + +if not exist "%ramUnlockSource%" goto :stage_shell + +if not exist "%scriptsDir%\" mkdir "%scriptsDir%" + +copy /y "%ramUnlockSource%" "%scriptsDir%\Pi4Disable3GB.ps1" >nul +if errorlevel 1 ( + call :RaiseEvent LogWarn, "Could not install the Pi 4 RAM unlock; the 3 GB limit will stay enabled." + goto :stage_shell +) + +call :RaiseEvent LogInfo, "Pi 4 RAM unlock installed to %scriptsDir%\Pi4Disable3GB.ps1" + +:: Optional one-time UEFI Shell handoff. The shell restores BOOTAA64.EFI before chain-loading it. +:stage_shell +set shellSource=%~dp0Shell.efi +set bootDir=%WOR_DISK_BOOTPARTITION% +if not exist "%shellSource%" goto :end +if not exist "%bootDir%\EFI\BOOT\BOOTAA64.EFI" ( + call :RaiseEvent LogWarn, "Could not find EFI\BOOT\BOOTAA64.EFI; skipping the automatic UEFI Shell handoff." + goto :end +) +if exist "%bootDir%\EFI\BOOT\BOOTAA64.WOR" ( + call :RaiseEvent LogWarn, "A previous UEFI Shell handoff backup exists; refusing to replace it." + goto :end +) +copy /y "%bootDir%\EFI\BOOT\BOOTAA64.EFI" "%bootDir%\EFI\BOOT\BOOTAA64.WOR" >nul +if errorlevel 1 goto :end +copy /y "%shellSource%" "%bootDir%\EFI\BOOT\BOOTAA64.EFI" >nul +if errorlevel 1 ( + copy /y "%bootDir%\EFI\BOOT\BOOTAA64.WOR" "%bootDir%\EFI\BOOT\BOOTAA64.EFI" >nul + goto :end +) +( + echo map -r + echo setvar RamLimitTo3GB -guid CD7CC258-31DB-22E6-9F22-63B0B8EED6B5 -bs -rt -nv =0x00000000 + echo rm \startup.nsh + echo rm \EFI\BOOT\BOOTAA64.EFI + echo mv \EFI\BOOT\BOOTAA64.WOR \EFI\BOOT\BOOTAA64.EFI + echo \EFI\BOOT\BOOTAA64.EFI +) > "%bootDir%\startup.nsh" +call :RaiseEvent LogInfo, "Staged one-time UEFI Shell RAM unlock; the original EFI loader will be restored automatically." +goto :end + +:: RaiseEvent Type, Data +:RaiseEvent +echo WoR-Event^|%~1^|%~2 +goto :eof + +:end +:: a non-zero exit code aborts the whole installation, and a missing answer file is not worth that +exit /b 0 diff --git a/install-wor-gui.sh b/install-wor-gui.sh index 01be76b..a7bd253 100755 --- a/install-wor-gui.sh +++ b/install-wor-gui.sh @@ -1,24 +1,132 @@ #!/bin/bash -#Written by Botspot -#This script is a GUI front-end for the install-wor.sh script - -gui_error_dialog() { #Input: error message - local plain - plain="$(echo -e "An error has occurred:\n$1\nExiting now." | sed 's/\x1b\[[0-9;]*m//g' | sed 's/\x1b\[[0-9;]*//g' | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g")" - if command -v zenity >/dev/null ;then - zenity --error --title "$(basename "$0")" --width 360 --text "$plain" - elif command -v osascript >/dev/null ;then - osascript -e 'on run argv' -e 'display alert (item 1 of argv) message (item 2 of argv) as critical' -e 'end run' "$(basename "$0")" "$plain" >/dev/null +#WoR-Flasher graphical front-end for Linux and macOS. +#Presentation only: every decision, validation and device operation belongs to install-wor.sh, +#which this script sources so the two can never disagree about what actually gets flashed. +#Native AppKit/JXA dialogs are used on macOS and yad dialogs on Linux; the two must stay aligned. +#Version, licensing and attribution are defined in src/lib/metadata.sh and install-wor.sh. +# +#Original author: Botspot - https://github.com/Botspot/wor-flasher +#Maintained with Blackout Secure support - https://github.com/Botspot/wor-flasher + +: "${WOR_ANNOUNCEMENT_TIMEOUT:=30}" + +export RUN_MODE=gui #this variable is detected by install-wor.sh to display gui error messages + +#Determine the directory that contains this script +[ -z "$DIRECTORY" ] && DIRECTORY="$(cd "$(dirname "$0")" && pwd -P)" + +#On Windows, hand off to the packaged Windows executable if present +if [ "${OS:-}" == "Windows_NT" ] || [[ "$(uname -s 2>/dev/null)" =~ MINGW|MSYS|CYGWIN|Windows ]];then + if [ -x "$DIRECTORY/release/windows/wor-flasher.exe" ];then + exec "$DIRECTORY/release/windows/wor-flasher.exe" "$@" + elif [ -x "$DIRECTORY/release/windows/WoR-Flasher.exe" ];then + exec "$DIRECTORY/release/windows/WoR-Flasher.exe" "$@" fi -} +fi + +#The native app owns macOS preflight and dependency setup. Local development runs the working tree +#directly; opt in to app handoff only when intentionally testing the packaged launcher. +if [ "$(uname -s 2>/dev/null)" == Darwin ] && [ "${WOR_NATIVE_APP:-0}" != 1 ] && [ "${WOR_USE_PACKAGED_APP:-0}" == 1 ] && [ -x "$DIRECTORY/release/macos/WoR-Flasher.app/Contents/MacOS/WoR-Flasher" ];then + exec /usr/bin/open -W "$DIRECTORY/release/macos/WoR-Flasher.app" +fi -error() { #Input: error message - echo -e "\\e[91m$1\\e[39m" - gui_error_dialog "$1" +#This script and cli-based install-wor.sh must be in the same directory. Source it before anything else +#so every shared behaviour - error dialogs, status output, drive detection, the settings summary - comes +#from the installer itself and can never drift out of step with what actually gets flashed. +#Nothing above this point may call a shared function, so failures here are reported by hand. +cli_script="$DIRECTORY/install-wor.sh" +if [ ! -d "$DIRECTORY" ] || [ ! -f "$cli_script" ];then + printf '\033[91m%b\033[0m\n' "No script found named install-wor.sh\nBoth scripts must be in the same directory." 1>&2 exit 1 +fi + +repair_missing_checkout_runtime() { #Restore only absent runtime files from local HEAD before shared libraries are sourced. + local required_path + local missing=() + command -v git >/dev/null 2>&1 && git -C "$DIRECTORY" rev-parse --git-dir >/dev/null 2>&1 || return 0 + while IFS= read -r required_path ;do + [ -e "$DIRECTORY/$required_path" ] || missing+=("$required_path") + done < <(git -C "$DIRECTORY" ls-tree -r --name-only HEAD -- \ + install-wor.sh install-wor-gui.sh install-wor-hook.sh src/lib src/config src/updater.mjs \ + config-templates assets) + [ "${#missing[@]}" -gt 0 ] || return 0 + printf 'Repairing missing WoR-Flasher files from local Git HEAD:\n' 1>&2 + printf ' %s\n' "${missing[@]}" 1>&2 + git -C "$DIRECTORY" restore --source=HEAD -- "${missing[@]}" \ + || { printf 'Failed to restore missing WoR-Flasher files from local Git HEAD.\n' 1>&2; return 1; } +} + +repair_missing_checkout_runtime || exit 1 + +#shellcheck disable=SC1090 +source "$cli_script" source #by sourcing, this script checks for and applies updates. + +#outside the packaged .app (which sets this from its own bundled .icns), fall back to the same +#logo PNG the rest of the app already uses, so the Dock and minimized-window tile are branded +#instead of showing the generic osascript icon +[ -z "$WOR_ICON_PATH" ] && WOR_ICON_PATH="$WOR_LOGO_PATH" + +find_macos_gui_process() { #Input: parent pid. Output: first descendant WoR-Flasher script-host pid. + local child found process_name + for child in $(pgrep -P "$1" 2>/dev/null) ;do + process_name="$(ps -p "$child" -o comm= 2>/dev/null)" + if [ "${process_name##*/}" == "$WOR_FLASHER_NAME" ];then + printf '%s\n' "$child" + return 0 + fi + found="$(find_macos_gui_process "$child")" && { printf '%s\n' "$found"; return 0; } + done + return 1 +} + +activate_macos_gui() { #Input: owning shell pid. Brings its current JXA window forward. + local gui_pid + gui_pid="$(find_macos_gui_process "$1")" || return 1 + wor_osascript -l JavaScript - "$gui_pid" <<'JXA' >/dev/null 2>&1 +ObjC.import('AppKit') +const pid = Number(ObjC.unwrap($.NSProcessInfo.processInfo.arguments.objectAtIndex(4))) +const runningApp = $.NSRunningApplication.runningApplicationWithProcessIdentifier(pid) +if (!runningApp) $.exit(1) +runningApp.unhide +if (!runningApp.activateWithOptions($.NSApplicationActivateAllWindows | $.NSApplicationActivateIgnoringOtherApps)) $.exit(1) +JXA +} + +release_gui_instance() { + local owner_pid + owner_pid="$(cat "$WOR_GUI_INSTANCE_DIR/pid" 2>/dev/null)" + [ "$owner_pid" == "$$" ] || return 0 + rm -f "$WOR_GUI_INSTANCE_DIR/pid" + rmdir "$WOR_GUI_INSTANCE_DIR" 2>/dev/null } +acquire_gui_instance() { + local owner_command owner_pid + WOR_GUI_INSTANCE_DIR="${TMPDIR:-/tmp}/wor-flasher-gui-${UID}.lock" + if ! mkdir "$WOR_GUI_INSTANCE_DIR" 2>/dev/null ;then + owner_pid="$(cat "$WOR_GUI_INSTANCE_DIR/pid" 2>/dev/null)" + owner_command="$(ps -p "$owner_pid" -o args= 2>/dev/null)" + if [ -n "$owner_pid" ] && kill -0 "$owner_pid" 2>/dev/null && [[ "$owner_command" == *install-wor-gui.sh* ]];then + [ "$(uname -s)" != Darwin ] || activate_macos_gui "$owner_pid" + return 1 + fi + rm -f "$WOR_GUI_INSTANCE_DIR/pid" + rmdir "$WOR_GUI_INSTANCE_DIR" 2>/dev/null + mkdir "$WOR_GUI_INSTANCE_DIR" 2>/dev/null || return 1 + fi + printf '%s\n' "$$" > "$WOR_GUI_INSTANCE_DIR/pid" + export WOR_GUI_INSTANCE_DIR + trap release_gui_instance EXIT + trap 'exit 130' INT + trap 'exit 143' HUP TERM +} + +acquire_gui_instance || exit 0 + +echo "DIRECTORY: $DIRECTORY" +echo "DL_DIR: $DL_DIR" + open_url() { #Input: url if command -v x-www-browser >/dev/null ;then x-www-browser "$1" & @@ -31,72 +139,2363 @@ open_url() { #Input: url fi } +kill_process_tree() { #Input: pid. Stops it and everything it started; most of the flash runs under sudo. + local pid="$1" child + for child in $(pgrep -P "$pid" 2>/dev/null) ;do + kill_process_tree "$child" + done + kill -TERM "$pid" 2>/dev/null || command sudo -n kill -TERM "$pid" 2>/dev/null +} + +gui_start_disk_alert_handler() { #Runs only the optional macOS alert automation, separate from the flash and progress window. + { + wor_osascript -l JavaScript "$DIRECTORY/src/lib/macos-disk-alerts.js" \ + "$progress_file" "$done_marker" "$abort_marker" "$installer_pid" "$disk_alert_status" \ + > "$disk_alert_log" 2>&1 + printf '%s\n' "$?" > "$disk_alert_done" + } & + disk_alert_pid=$! +} + +gui_stop_disk_alert_handler() { #Stops only this run's user-owned helper and preserves its diagnostics after the installer exits. + local attempt child children='' result='' wait_status=0 forced_stop=0 + [ -n "${disk_alert_pid:-}" ] || return 0 + for attempt in 1 2 3 4 5 6 7 8 9 10 ;do + if [ -s "$disk_alert_done" ] || ! kill -0 "$disk_alert_pid" 2>/dev/null;then + break + fi + sleep 0.1 + done + if kill -0 "$disk_alert_pid" 2>/dev/null;then + children="$(pgrep -P "$disk_alert_pid" 2>/dev/null)" + for child in $children ;do + kill -TERM "$child" 2>/dev/null || ! kill -0 "$child" 2>/dev/null \ + || printf 'Warning: could not stop disk-alert helper child %s.\n' "$child" >> "$output_log" + done + kill -TERM "$disk_alert_pid" 2>/dev/null || ! kill -0 "$disk_alert_pid" 2>/dev/null \ + || printf 'Warning: could not stop disk-alert helper %s.\n' "$disk_alert_pid" >> "$output_log" + for attempt in 1 2 3 4 5 6 7 8 9 10 ;do + kill -0 "$disk_alert_pid" 2>/dev/null || break + sleep 0.1 + done + for child in $children "$disk_alert_pid" ;do + if kill -0 "$child" 2>/dev/null;then + forced_stop=1 + kill -KILL "$child" 2>/dev/null || ! kill -0 "$child" 2>/dev/null \ + || printf 'Warning: could not terminate disk-alert helper process %s.\n' "$child" >> "$output_log" + fi + done + fi + wait "$disk_alert_pid" 2>/dev/null || wait_status=$? + if [ -s "$disk_alert_status" ];then + disk_alert_warning="$(jq -r 'if .state == "warning" then .message else "" end' "$disk_alert_status")" \ + || disk_alert_warning='Automatic Ignore status could not be read. Choose Ignore manually if the unreadable-disk alert appears.' + fi + [ ! -s "$disk_alert_done" ] || result="$(cat "$disk_alert_done")" + if [ "$forced_stop" == 1 ] || { [ -n "$result" ] && [ "$result" != 0 ]; } || { [ -z "$result" ] && [ "$wait_status" != 0 ] && [ "$wait_status" != 143 ]; };then + [ -n "$disk_alert_warning" ] || disk_alert_warning='Automatic Ignore stopped unexpectedly. Choose Ignore manually if the unreadable-disk alert appears.' + fi + [ ! -s "$disk_alert_log" ] || cat "$disk_alert_log" >> "$output_log" + [ -z "$disk_alert_warning" ] || printf 'Warning: %s\n' "$disk_alert_warning" >> "$output_log" + rm -f "$disk_alert_status" "$disk_alert_done" "$disk_alert_log" + disk_alert_pid='' +} + +gui_start_installer() { #Starts install-wor.sh in the background and waits for it to authenticate. Sets error_marker, output_log, progress_file, done_marker, auth_marker and installer_pid. + error_marker="$(mktemp)" || error "Failed to create a GUI error marker." + output_log="$(mktemp)" || error "Failed to create an install log." + progress_file="$(mktemp)" || error "Failed to create a progress file." + done_marker="$(mktemp -u)" + auth_marker="$(mktemp -u)" + disk_alert_pid='' disk_alert_warning='' + if is_macos;then + disk_alert_status="$(mktemp)" || error "Failed to create disk-alert status." + disk_alert_log="$(mktemp)" || error "Failed to create a disk-alert log." + disk_alert_done="$disk_alert_status.done" + fi + #start with a clean marker; the installer creates it only if an error occurs + rm -f "$error_marker" + + #No separate terminal is spawned, so the installer inherits this process's exported environment directly. + export_installer_settings + export WOR_GUI_ERROR_MARKER="$error_marker" + export WOR_GUI_PROGRESS_FILE="$progress_file" + export WOR_GUI_AUTH_MARKER="$auth_marker" + export WOR_GUI_ABORT_MARKER="${abort_marker:-}" + #this script already ran the update check while sourcing install-wor.sh; a second one would only + #repeat the network round-trip, and an update applied here would re-exec the installer mid-launch + export NO_UPDATE=1 + #only a password retry sets this; the installer still verifies the prepared files before trusting it + export WOR_RESUME_AT_FLASH="${resume_at_flash:-0}" + + #the job records its own status: a subshell cannot wait on a sibling, so waiting there returned 127 at once + { "$cli_script" > "$output_log" 2>&1; echo $? > "$done_marker"; } & + installer_pid=$! + if is_macos;then + gui_start_disk_alert_handler + fi + + #macOS opens progress immediately; its askpass dialog activates itself when authentication is needed. + if [ "${GUI_PROGRESS_EARLY:-0}" != 1 ];then + while [ ! -e "$auth_marker" ] && [ ! -f "$done_marker" ] ;do + sleep 0.3 + done + fi +} + +installer_showed_own_error() { #Exit 0 if install-wor.sh already displayed its own native error dialog. + #gui_error_dialog creates the marker before opening its own native dialog; the log has the details + [ -e "$error_marker" ] +} + +gui_save_failure_log() { #Output: where the installer log was kept. The dialog only shows a tail, and the GUI has no terminal to fall back on. + local saved_log last_log + saved_log="$(wor_log_file)" + mkdir -p "$(dirname "$saved_log")" 2>/dev/null + mv "$output_log" "$saved_log" 2>/dev/null || saved_log="$output_log" + last_log="$(wor_last_log_file)" + if [ "$saved_log" != "$last_log" ];then + mkdir -p "$(dirname "$last_log")" 2>/dev/null + cp "$saved_log" "$last_log" 2>/dev/null || true + fi + echo "Installer log saved to $saved_log" 1>&2 + echo "$saved_log" +} + +gui_log_tail() { #Input: log path. Output: the last lines, with terminal escapes and carriage returns removed. + #Installer logs can contain raw tool output; macOS sed rejects an invalid UTF-8 byte under the user locale. + LC_ALL=C sed 's/\x1b\[[0-9;]*[A-Za-z]//g; s/\r//g' "$1" | tail -n 18 +} + loading_dialog() { #display a dialog to say something is loading - (echo '# ' ; sleep infinity) | yad "${yadflags[@]}" --height=0 \ + local dialog_pid + #1, not 0: gtk_window_resize asserts height > 0, and GTK still grows the window to its natural size + (echo '# ' ; sleep infinity) | yad "${yadflags[@]}" --height=1 \ --progress --pulsate --title="$1" --text="$1" --no-buttons & - trap "kill $! 2>/dev/null" EXIT + dialog_pid=$! + trap 'kill "$dialog_pid" 2>/dev/null' EXIT sleep infinity } -RUN_MODE=gui #this variable is detected by install-wor.sh to display gui error messages +stop_loader() { + [ -z "${loader_pid:-}" ] || kill "$loader_pid" 2>/dev/null + loader_pid='' +} -#Determine the directory that contains this script -[ -z "$DIRECTORY" ] && DIRECTORY="$(cd "$(dirname "$0")" && pwd -P)" -[ ! -e "$DIRECTORY" ] && error "install-wor-gui.sh: Failed to determine the directory that contains this script. Try running this script with full paths." -echo "DIRECTORY: $DIRECTORY" +macos_choose() { #Input: newline-separated choices, prompt, default, cancel/back label, optional action label/value, optional image path/next label/icon path/window title/cancel value/timeout/process title. Output: selected choice or action value. + local result choose_jxa + choose_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +ObjC.import('stdlib') +ObjC.import('Foundation') + +const args = $.NSProcessInfo.processInfo.arguments +const rawChoices = ObjC.unwrap(args.objectAtIndex(4)) +const choices = rawChoices.length > 0 ? rawChoices.split('\n') : [] +const promptText = ObjC.unwrap(args.objectAtIndex(5)) +const defaultChoice = ObjC.unwrap(args.objectAtIndex(6)) +const cancelLabel = ObjC.unwrap(args.objectAtIndex(7)) +const actionLabel = ObjC.unwrap(args.objectAtIndex(8)) +const actionValue = ObjC.unwrap(args.objectAtIndex(9)) +const imagePath = ObjC.unwrap(args.objectAtIndex(10)) +const nextLabel = ObjC.unwrap(args.objectAtIndex(11)) +const iconPath = ObjC.unwrap(args.objectAtIndex(12)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(13)) +const cancelValue = ObjC.unwrap(args.objectAtIndex(14)) +const announcementTimeout = Number(ObjC.unwrap(args.objectAtIndex(15) || '0')) +const appTitle = ObjC.unwrap(args.objectAtIndex(16) || windowTitle) +//a message screen has no selectable rows; it may still show an image (e.g. the welcome screen) +const isMessageMode = choices.length === 0 +const isPartnershipAnnouncement = imagePath.endsWith('/partnership.png') + +function writeResult(value) { + const data = $(value + '\n').dataUsingEncoding($.NSUTF8StringEncoding) + $.NSFileHandle.fileHandleWithStandardOutput.writeData(data) +} -#Determine the directory to download windows component files to -[ -z "$DL_DIR" ] && DL_DIR="$HOME/wor-flasher-files" -echo "DL_DIR: $DL_DIR" +function cancelAndExit() { + writeResult('__WOR_CANCEL__') + $.exit(0) +} + +$.NSProcessInfo.processInfo.processName = appTitle +const app = $.NSApplication.sharedApplication +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worInstallAppMenu(app, appTitle, windowTitle, iconPath) +worSetAppIcon(app, iconPath) +let tableView +let window +let selectedValue = null +let allowTermination = false +let countdownSeconds = announcementTimeout +let countdownTimer = null +let nextButton +const Controller = ObjC.registerSubclass({ + name: 'WorChooserController', + superclass: 'NSObject', + methods: { + 'numberOfRowsInTableView:': { + types: ['NSInteger', ['id']], + implementation: function() { + return choices.length + } + }, + 'tableView:objectValueForTableColumn:row:': { + types: ['id', ['id', 'id', 'NSInteger']], + implementation: function(_tableView, _tableColumn, row) { + return $(choices[row]) + } + }, + 'nextClicked:': { + types: ['void', ['id']], + implementation: function() { + if (isMessageMode) { + selectedValue = defaultChoice + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + return + } + const row = tableView.selectedRow + if (row >= 0 && row < choices.length) { + selectedValue = choices[row] + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + } + }, + 'cancelClicked:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = cancelValue.length > 0 ? cancelValue : null + app.stopModalWithCode($.NSCancelButton) + window.orderOut(null) + } + }, + 'countdownTick:': { + types: ['void', ['id']], + implementation: function() { + if (countdownSeconds <= 0) return + countdownSeconds -= 1 + nextButton.title = nextLabel + ' (' + countdownSeconds + ')' + if (countdownSeconds === 0) { + selectedValue = defaultChoice + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + } + }, + 'textView:clickedOnLink:atIndex:': { + types: ['BOOL', ['id', 'id', 'NSUInteger']], + implementation: function(_textView, link, _index) { + const url = ObjC.unwrap(link) + $.NSWorkspace.sharedWorkspace.openURL($.NSURL.URLWithString($(url))) + return true + } + }, + 'actionClicked:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = actionValue + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'windowWillClose:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = null + app.stopModalWithCode($.NSCancelButton) + } + }, + //right-click Quit from the Dock/app-switcher sends terminate: to NSApp; since this window is + //hosted by osascript rather than a full NSApplicationMain run loop, that does not otherwise exit the process + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + cancelAndExit() + } + }, + 'pumpEvents:': { + types: ['void', ['id']], + implementation: function() { + $.NSRunLoop.currentRunLoop.runModeBeforeDate($.NSDefaultRunLoopMode, $.NSDate.dateWithTimeIntervalSinceNow(0.01)) + } + }, + //clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never come back + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + }, + 'applicationShouldTerminate:': { + types: ['NSUInteger', ['id']], + implementation: function() { + if (allowTermination) return $.NSTerminateNow + cancelAndExit() + } + } + } +}) + +const controller = $.WorChooserController.alloc.init +app.setDelegate(controller) +const screenFrame = $.NSScreen.mainScreen.visibleFrame +//clamp the desired size to whatever screen real estate is actually available, rather than assuming a full-size display +const maxWidth = Math.min(760, screenFrame.size.width - 40) +//a message screen only hides Cancel for the image-based welcome screen; an explicitly empty cancelLabel hides it everywhere else +const showCancelButton = cancelLabel.length > 0 && !(isMessageMode && imagePath.length > 0) +//the bottom button row is the hard floor on width; fitting the window to its content must never clip it +const minButtonRowWidth = 40 + 180 + (showCancelButton ? 188 : 0) + (actionLabel.length > 0 ? 120 : 0) + +function measuredTextWidth(text, font) { + const measured = $.NSMutableAttributedString.alloc.init + measured.mutableString.appendString($(text)) + measured.addAttributeValueRange($.NSFontAttributeName, font, $.NSMakeRange(0, text.length)) + const bounds = measured.boundingRectWithSizeOptions($.NSMakeSize(2000, 200), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) + return Math.ceil(bounds.size.width) +} + +const measureText = $.NSMutableAttributedString.alloc.init +measureText.mutableString.appendString($(promptText)) +const measureRange = $.NSMakeRange(0, promptText.length) +const promptFont = $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium) +measureText.addAttributeValueRange($.NSFontAttributeName, promptFont, measureRange) +const naturalBounds = measureText.boundingRectWithSizeOptions($.NSMakeSize(1000, 1000), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) + +const listRowHeight = 24 +//cap the visible list so a two-item chooser stays compact and a 190-entry language list still scrolls instead of filling the screen +const visibleListHeight = isMessageMode ? 0 : Math.min(384, Math.max(96, choices.length * listRowHeight + 8)) +let requestedWidth +if (isPartnershipAnnouncement || (isMessageMode && imagePath.length > 0)) { + requestedWidth = maxWidth +} else if (isMessageMode) { + requestedWidth = Math.max(360, Math.ceil(naturalBounds.size.width) + 40) +} else { + //fit the widest row plus its inset and the vertical scroller, so rows read fully without a needlessly wide window + const rowFont = $.NSFont.systemFontOfSize(13) + let widestChoice = 0 + for (let i = 0; i < choices.length; i++) widestChoice = Math.max(widestChoice, measuredTextWidth(choices[i], rowFont)) + requestedWidth = Math.max(Math.ceil(naturalBounds.size.width), widestChoice + 60) + 40 +} +const width = Math.min(maxWidth, Math.max(minButtonRowWidth, requestedWidth)) +const measuredBounds = measureText.boundingRectWithSizeOptions($.NSMakeSize(width - 40, 1000), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) +const contentHeight = Math.ceil(measuredBounds.size.height) + 150 +const requestedHeight = isPartnershipAnnouncement + ? 600 + : isMessageMode + ? (imagePath.length > 0 ? 500 : Math.max(220, contentHeight)) + : visibleListHeight + 132 +const height = Math.min(requestedHeight, screenFrame.size.height - 60) +//fixed layout: no drag-resize and no zoom/maximize button, only minimize (and restore) via the titlebar +window = worMakeWindow({ width: width, height: height, title: windowTitle, delegate: controller }) + +const content = window.contentView +content.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable + +const prompt = $.NSTextField.labelWithString(promptText) +prompt.font = $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium) +prompt.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin +if (isMessageMode) { + prompt.setUsesSingleLineMode(false) + prompt.cell.setWraps(true) + prompt.cell.setScrollable(false) + if (imagePath.length > 0) { + prompt.frame = $.NSMakeRect(20, 76, width - 40, 122) + const imageHeight = isPartnershipAnnouncement ? Math.min(315, height - 185) : 320 + const imageFrame = isPartnershipAnnouncement + ? $.NSMakeRect(20, height - imageHeight - 12, width - 40, imageHeight) + : $.NSMakeRect(20, 140, width - 40, imageHeight) + const imageView = $.NSImageView.alloc.initWithFrame(imageFrame) + imageView.image = $.NSImage.alloc.initWithContentsOfFile($(imagePath)) + imageView.imageScaling = $.NSImageScaleProportionallyUpOrDown + imageView.imageAlignment = $.NSImageAlignCenter + imageView.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(imageView) + } else if (isPartnershipAnnouncement) { + prompt.frame = $.NSMakeRect(20, 96, width - 40, height - 132) + } else { + prompt.frame = $.NSMakeRect(20, 72, width - 40, height - 112) + } + if (isPartnershipAnnouncement) { + const attributedPrompt = $.NSMutableAttributedString.alloc.init + attributedPrompt.mutableString.appendString($(promptText)) + const fullPromptRange = $.NSMakeRange(0, promptText.length) + const paragraphStyle = $.NSMutableParagraphStyle.alloc.init + paragraphStyle.alignment = $.NSTextAlignmentRight + paragraphStyle.lineBreakMode = $.NSLineBreakByWordWrapping + paragraphStyle.lineSpacing = 1 + paragraphStyle.paragraphSpacing = 4 + attributedPrompt.addAttributeValueRange($.NSForegroundColorAttributeName, $.NSColor.labelColor, fullPromptRange) + attributedPrompt.addAttributeValueRange($.NSFontAttributeName, $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium), fullPromptRange) + attributedPrompt.addAttributeValueRange($.NSParagraphStyleAttributeName, paragraphStyle, fullPromptRange) + function addPromptLink(label, url) { + const index = promptText.indexOf(label) + if (index < 0) return + const range = $.NSMakeRange(index, label.length) + attributedPrompt.addAttributeValueRange($.NSLinkAttributeName, $(url), range) + attributedPrompt.addAttributeValueRange($.NSForegroundColorAttributeName, $.NSColor.linkColor, range) + attributedPrompt.addAttributeValueRange($.NSUnderlineStyleAttributeName, $(1), range) + } + addPromptLink('Blackout Secure', 'https://blackoutsecure.app/') + addPromptLink('Botspot', 'https://github.com/Botspot') + addPromptLink('Windows on R', 'https://worproject.com/') + addPromptLink('Botspot/wor-flasher', 'https://github.com/Botspot/wor-flasher') + addPromptLink('sponsoring Botspot', 'https://github.com/sponsors/Botspot') + addPromptLink('buying Blackout Secure a coffee', 'https://github.com/sponsors/blackoutsecure?frequency=one-time&amount=8') + const textFrame = isPartnershipAnnouncement + ? (() => { + const textWidth = 680 + const textBounds = attributedPrompt.boundingRectWithSizeOptions($.NSMakeSize(textWidth, 1000), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) + const textHeight = Math.ceil(textBounds.size.height) + 12 + return $.NSMakeRect((width - textWidth) / 2, 58, textWidth, textHeight) + })() + : imagePath.length > 0 + ? $.NSMakeRect(32, 76, width - 64, 122) + : $.NSMakeRect(20, 96, width - 40, height - 132) + const textView = $.NSTextView.alloc.initWithFrame(textFrame) + textView.editable = false + textView.selectable = false + textView.drawsBackground = false + textView.alignment = $.NSTextAlignmentCenter + textView.textContainerInset = $.NSMakeSize(0, 0) + textView.textContainer.lineFragmentPadding = 0 + textView.textContainer.widthTracksTextView = true + textView.horizontallyResizable = false + textView.verticallyResizable = false + textView.autoresizingMask = isPartnershipAnnouncement + ? $.NSViewMinXMargin | $.NSViewMaxXMargin | $.NSViewMinYMargin + : $.NSViewWidthSizable | $.NSViewMinYMargin + textView.textStorage.setAttributedString(attributedPrompt) + textView.delegate = controller + content.addSubview(textView) + } else { + content.addSubview(prompt) + } +} else { + prompt.frame = $.NSMakeRect(20, height - 52, width - 40, 24) + content.addSubview(prompt) +} + +if (!isMessageMode) { + const scrollView = $.NSScrollView.alloc.initWithFrame($.NSMakeRect(20, 72, width - 40, height - 132)) + scrollView.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable + scrollView.borderType = $.NSBezelBorder + scrollView.hasVerticalScroller = true + + tableView = $.NSTableView.alloc.initWithFrame(scrollView.bounds) + tableView.setHeaderView(undefined) + tableView.rowHeight = 24 + tableView.setDelegate(controller) + tableView.setDataSource(controller) + tableView.setTarget(controller) + tableView.setDoubleAction('nextClicked:') + tableView.setAllowsEmptySelection(false) + tableView.setUsesAlternatingRowBackgroundColors(true) + + const column = $.NSTableColumn.alloc.initWithIdentifier('choice') + column.width = scrollView.contentSize.width + column.resizingMask = $.NSTableColumnAutoresizingMask + column.editable = false + tableView.addTableColumn(column) + scrollView.documentView = tableView + content.addSubview(scrollView) +} + +//a lone button in a message dialog reads better centered; once a Back/Advanced companion button is +//present, centering the primary button made the two overlap, so both use the standard bottom-right row instead +const centerNextButton = isMessageMode && !showCancelButton && actionLabel.length === 0 + +//buttons are sized to their own text so any label (e.g. "Proceed with WoR-Flasher") fits without truncation +nextButton = $.NSButton.buttonWithTitleTargetAction(nextLabel, controller, 'nextClicked:') +nextButton.setTarget(controller) +nextButton.setAction('nextClicked:') +nextButton.bezelStyle = $.NSBezelStyleRounded +nextButton.keyEquivalent = '\r' +if (announcementTimeout > 0 && isMessageMode) nextButton.title = nextLabel + ' (' + announcementTimeout + ')' +nextButton.sizeToFit +const nextWidth = Math.max(180, nextButton.frame.size.width) +const nextX = centerNextButton ? (width - nextWidth) / 2 : width - 20 - nextWidth +nextButton.frame = $.NSMakeRect(nextX, 22, nextWidth, 32) +nextButton.autoresizingMask = $.NSViewMinXMargin | $.NSViewMaxXMargin | $.NSViewMaxYMargin +content.addSubview(nextButton) + +if (announcementTimeout > 0 && isMessageMode) { + countdownTimer = $.NSTimer.timerWithTimeIntervalTargetSelectorUserInfoRepeats(1, controller, 'countdownTick:', $(), true) + $.NSRunLoop.currentRunLoop.addTimerForMode(countdownTimer, $.NSModalPanelRunLoopMode) +} + +if (showCancelButton) { + const cancelButton = $.NSButton.buttonWithTitleTargetAction(cancelLabel, controller, 'cancelClicked:') + cancelButton.bezelStyle = $.NSBezelStyleRounded + cancelButton.keyEquivalent = '\u001b' + cancelButton.sizeToFit + //match the next button width so the pair reads as one row instead of two different sizes + const cancelWidth = Math.max(nextWidth, cancelButton.frame.size.width) + //anchor to the next button real x position instead of re-deriving one, so the two never overlap + cancelButton.frame = $.NSMakeRect(nextButton.frame.origin.x - 8 - cancelWidth, 22, cancelWidth, 32) + cancelButton.autoresizingMask = $.NSViewMinXMargin | $.NSViewMaxYMargin + content.addSubview(cancelButton) +} + +if (actionLabel.length > 0) { + const actionButton = $.NSButton.buttonWithTitleTargetAction(actionLabel, controller, 'actionClicked:') + actionButton.bezelStyle = $.NSBezelStyleRounded + actionButton.sizeToFit + const actionWidth = Math.max(112, actionButton.frame.size.width) + actionButton.frame = $.NSMakeRect(20, 22, actionWidth, 32) + actionButton.autoresizingMask = $.NSViewMaxXMargin | $.NSViewMaxYMargin + content.addSubview(actionButton) +} + +if (!isMessageMode) { + const defaultIndex = Math.max(0, choices.indexOf(defaultChoice)) + tableView.selectRowIndexesByExtendingSelection($.NSIndexSet.indexSetWithIndex(defaultIndex), false) + tableView.scrollRowToVisible(defaultIndex) +} + +window.makeKeyAndOrderFront(null) +if (!app.isActive) app.requestUserAttention($.NSInformationalRequest) +app.activateIgnoringOtherApps(true) +worInstallWindowHandlers(controller) +app.runModalForWindow(window) + +if (selectedValue === null) { + writeResult('__WOR_CANCEL__') +} else { + writeResult(selectedValue) +} +allowTermination = true +app.terminate(null) +JXA +)" + result="$(wor_osascript -l JavaScript - "$1" "$2" "$3" "${4:-Cancel}" "${5:-}" "${6:-}" "${7:-}" "${8:-Next}" "${9:-$WOR_ICON_PATH}" "${10:-$WOR_WINDOW_TITLE}" "${11:-}" "${12:-0}" "${13:-$WOR_APP_TITLE}" <<<"$choose_jxa")" + if [ "$result" == __WOR_CANCEL__ ];then + return 1 + fi + printf '%s\n' "$result" +} + +macos_show_announcement() { #Output: proceed or project partner. + local announcement_choices announcement_choice announcement_text + announcement_choices='' + printf -v announcement_text '%s\n\n%s\n\n%s\n' \ + "Blackout Secure is proud to partner with Botspot and the Windows on R community, carrying WoR-Flasher forward while preserving Botspot's original authorship and project direction." \ + "Report issues, share feedback, or contribute at Botspot/wor-flasher." \ + "Support continued development by sponsoring Botspot or buying Blackout Secure a coffee on GitHub." + announcement_choice="$(macos_choose "$announcement_choices" "$announcement_text" 'Proceed with WoR-Flasher' Cancel '' '' "$WOR_ASSETS_DIR/partnership.png" 'Proceed with WoR-Flasher' "$WOR_ICON_PATH" "$WOR_WINDOW_TITLE" '' "$WOR_ANNOUNCEMENT_TIMEOUT" "$WOR_APP_TITLE")" || return 1 + + case "$announcement_choice" in + 'Proceed with WoR-Flasher') echo "$announcement_choice" ;; + *) return 1 ;; + esac +} + +macos_choose_device() { #Input: newline-separated detected volume rows. Output: selected row, __REFRESH__, or Back. Fails only on a genuine Quit/close, never on Back. + if [ -z "$1" ];then + #cancelValue Back makes clicking Back succeed with a literal value instead of failing like Quit does, + #so the wizard can tell "go back a step" apart from "the user is quitting the whole thing" + result="$(macos_choose '' 'No external, physical, writable drive was found. Connect a removable drive, then click Refresh.' __REFRESH__ Back '' '' '' Refresh "$WOR_ICON_PATH" "$WOR_WINDOW_TITLE" Back 0 "$WOR_APP_TITLE")" || return 1 + echo "$result" + else + device_choice="$(macos_choose "$1" 'Choose the external drive and volumes to erase' "$(printf '%s\n' "$1" | head -n1)" Back Refresh __REFRESH__ '' '' '' '' Back)" || return 1 + echo "$device_choice" + fi +} + +macos_confirm_flash() { #Output: Flash, Advanced, Back, or failure for Quit/close. + local result rows + rows="$(settings_summary)" + local confirm_jxa + confirm_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +ObjC.import('Foundation') +ObjC.import('stdlib') + +const args = $.NSProcessInfo.processInfo.arguments +const rawRows = ObjC.unwrap(args.objectAtIndex(4)) +const targetDevice = ObjC.unwrap(args.objectAtIndex(5)) +const iconPath = ObjC.unwrap(args.objectAtIndex(6)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(7)) +const appTitle = ObjC.unwrap(args.objectAtIndex(8)) +const rows = rawRows.split('\n') + .filter((line) => line.length > 0) + .map((line) => { + const parts = line.split('\t') + return { label: parts[0] || '', value: parts.slice(1).join('\t') || '' } + }) + +function writeResult(value) { + const data = $(value + '\n').dataUsingEncoding($.NSUTF8StringEncoding) + $.NSFileHandle.fileHandleWithStandardOutput.writeData(data) +} + +$.NSProcessInfo.processInfo.processName = appTitle +const app = $.NSApplication.sharedApplication +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worInstallAppMenu(app, appTitle, windowTitle, iconPath) +worSetAppIcon(app, iconPath) + +let window +let selectedValue = 'Cancel' +let allowTermination = false +const Controller = ObjC.registerSubclass({ + name: 'WorConfirmFlashController', + superclass: 'NSObject', + methods: { + 'flashClicked:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = 'Flash' + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'advancedClicked:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = 'Advanced' + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'backClicked:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = 'Back' + app.stopModalWithCode($.NSCancelButton) + window.orderOut(null) + } + }, + 'windowWillClose:': { + types: ['void', ['id']], + implementation: function() { + selectedValue = 'Cancel' + app.stopModalWithCode($.NSCancelButton) + } + }, + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + selectedValue = 'Cancel' + app.stopModalWithCode($.NSCancelButton) + window.orderOut(null) + } + }, + 'pumpEvents:': { + types: ['void', ['id']], + implementation: function() { + $.NSRunLoop.currentRunLoop.runModeBeforeDate($.NSDefaultRunLoopMode, $.NSDate.dateWithTimeIntervalSinceNow(0.01)) + } + }, + //clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never come back + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + }, + 'applicationShouldTerminate:': { + types: ['NSUInteger', ['id']], + implementation: function() { + if (allowTermination) return $.NSTerminateNow + selectedValue = 'Cancel' + app.stopModalWithCode($.NSCancelButton) + return 0 + } + } + } +}) +const controller = $.WorConfirmFlashController.alloc.init +app.setDelegate(controller) + +const screenFrame = $.NSScreen.mainScreen.visibleFrame +const rowHeight = 24 +//the settings list is the only variable-height part, so fit the window to it instead of always opening at full size +const valueFont = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightRegular) +let widestValue = 0 +for (let i = 0; i < rows.length; i++) { + const measured = $.NSMutableAttributedString.alloc.init + measured.mutableString.appendString($(rows[i].value)) + measured.addAttributeValueRange($.NSFontAttributeName, valueFont, $.NSMakeRange(0, rows[i].value.length)) + const bounds = measured.boundingRectWithSizeOptions($.NSMakeSize(2000, 100), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) + widestValue = Math.max(widestValue, Math.ceil(bounds.size.width)) +} +const width = Math.min(760, screenFrame.size.width - 40, Math.max(560, 214 + widestValue + 72)) +const height = Math.min(620, screenFrame.size.height - 60, Math.max(320, rows.length * rowHeight + 16 + 220)) +window = worMakeWindow({ width: width, height: height, title: windowTitle, delegate: controller }) + +const content = window.contentView + +const heading = $.NSTextField.labelWithString('Review flash settings') +heading.font = $.NSFont.systemFontOfSizeWeight(17, $.NSFontWeightBold) +heading.frame = $.NSMakeRect(24, height - 54, width - 48, 26) +content.addSubview(heading) + +const target = $.NSTextField.labelWithString('Target: ' + targetDevice) +target.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightMedium) +target.textColor = $.NSColor.secondaryLabelColor +target.frame = $.NSMakeRect(24, height - 80, width - 48, 20) +content.addSubview(target) + +const warning = $.NSTextField.labelWithString('All data on the target drive will be erased.') +warning.font = $.NSFont.systemFontOfSizeWeight(13, $.NSFontWeightSemibold) +warning.textColor = $.NSColor.systemRedColor +warning.frame = $.NSMakeRect(24, height - 114, width - 48, 22) +content.addSubview(warning) + +const documentHeight = Math.max(rows.length * rowHeight + 16, height - 210) +const scrollView = $.NSScrollView.alloc.initWithFrame($.NSMakeRect(24, 86, width - 48, height - 220)) +scrollView.borderType = $.NSBezelBorder +scrollView.hasVerticalScroller = true +scrollView.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable +const documentView = $.NSView.alloc.initWithFrame($.NSMakeRect(0, 0, width - 48, documentHeight)) +scrollView.documentView = documentView +content.addSubview(scrollView) + +let y = documentHeight - rowHeight - 8 +for (let i = 0; i < rows.length; i++) { + const label = $.NSTextField.labelWithString(rows[i].label) + label.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightMedium) + label.textColor = $.NSColor.secondaryLabelColor + label.alignment = $.NSTextAlignmentRight + label.frame = $.NSMakeRect(8, y, 190, 20) + documentView.addSubview(label) + + const value = $.NSTextField.labelWithString(rows[i].value) + value.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightRegular) + value.lineBreakMode = $.NSLineBreakByTruncatingMiddle + value.frame = $.NSMakeRect(214, y, width - 286, 20) + documentView.addSubview(value) + y -= rowHeight +} + +const guidance = $.NSTextField.labelWithString('Flash begins immediately after administrator approval. Use Advanced to change these settings.') +guidance.font = $.NSFont.systemFontOfSizeWeight(11, $.NSFontWeightRegular) +guidance.textColor = $.NSColor.secondaryLabelColor +guidance.frame = $.NSMakeRect(24, 58, width - 48, 18) +content.addSubview(guidance) + +const flashButton = $.NSButton.buttonWithTitleTargetAction('Flash', controller, 'flashClicked:') +flashButton.bezelStyle = $.NSBezelStyleRounded +flashButton.keyEquivalent = '\r' +flashButton.sizeToFit +const flashWidth = Math.max(132, flashButton.frame.size.width) +flashButton.frame = $.NSMakeRect(width - 24 - flashWidth, 20, flashWidth, 32) +content.addSubview(flashButton) + +const backButton = $.NSButton.buttonWithTitleTargetAction('Back', controller, 'backClicked:') +backButton.bezelStyle = $.NSBezelStyleRounded +backButton.keyEquivalent = '\u001b' +backButton.sizeToFit +const backWidth = Math.max(112, backButton.frame.size.width) +backButton.frame = $.NSMakeRect(width - 24 - flashWidth - 8 - backWidth, 20, backWidth, 32) +content.addSubview(backButton) + +const advancedButton = $.NSButton.buttonWithTitleTargetAction('Advanced...', controller, 'advancedClicked:') +advancedButton.bezelStyle = $.NSBezelStyleRounded +advancedButton.sizeToFit +const advancedWidth = Math.max(130, advancedButton.frame.size.width) +advancedButton.frame = $.NSMakeRect(24, 20, advancedWidth, 32) +content.addSubview(advancedButton) + +window.makeKeyAndOrderFront(null) +if (!app.isActive) app.requestUserAttention($.NSInformationalRequest) +app.activateIgnoringOtherApps(true) +worInstallWindowHandlers(controller) +app.runModalForWindow(window) +allowTermination = true +writeResult(selectedValue) +app.terminate(null) +JXA +)" + result="$(wor_osascript -l JavaScript - "$rows" "$DEVICE" "$WOR_ICON_PATH" "$WOR_WINDOW_TITLE" "$WOR_APP_TITLE" <<<"$confirm_jxa")" || return 1 + [ "$result" == Cancel ] && return 1 + printf '%s\n' "$result" +} + +macos_advanced_options() { #Reads/updates OOBE_NETWORK_BYPASS, PI4_AUTO_DISABLE_3GB, UEFI_USE_LATEST, DRIVERS_USE_LATEST, SKIP_IMAGE_VERIFICATION, DRY_RUN, APPLY_CUSTOM_CONFIG_TXT, USE_CACHE, CONFIG_TXT, WIN_LANG, PLAY_SOUND, COMPLETION_SOUND. + local advanced_jxa checkbox_spec result status line i uefi_pinned pi4_applicable pi4_label config_scope lang_spec locale_spec l_code l_name sel_win_lang sound_spec sel_sound LC_ALL + uefi_pinned="$(uefi_pinned_version)" + #the engine ignores PI4_AUTO_DISABLE_3GB unless RPI_MODEL is 4, so don't offer it as a live choice elsewhere + [ "$RPI_MODEL" == 4 ] && pi4_applicable=1 || pi4_applicable=0 + #in recovery mode this config.txt boots the installer media; WoR-PE writes the target drive's own copy + config_scope="$(wor_config_scope "$CAN_INSTALL_ON_SAME_DRIVE")" + #labels and caution flags come from gui.sh so the Linux dialog cannot describe these differently + checkbox_spec="$(wor_advanced_label oobe "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL") $OOBE_NETWORK_BYPASS 1 $(wor_advanced_caution oobe) +$(wor_advanced_label pi4 "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL") $([ "$pi4_applicable" == 1 ] && echo "$PI4_AUTO_DISABLE_3GB" || echo 0) $pi4_applicable $(wor_advanced_caution pi4) +$(wor_advanced_label uefi "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL") $UEFI_USE_LATEST 1 $(wor_advanced_caution uefi) +$(wor_advanced_label drivers "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL") $DRIVERS_USE_LATEST 1 $(wor_advanced_caution drivers) +$(wor_advanced_label verify "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL") $SKIP_IMAGE_VERIFICATION 1 $(wor_advanced_caution verify) +$(wor_advanced_label dryrun "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL") $DRY_RUN 1 $(wor_advanced_caution dryrun)" + + lang_spec="" + while IFS=: read -r l_code l_name ;do + [ -z "$l_code" ] && continue + lang_spec+="${l_code} ${l_name}"$'\n' + done < <(list_langs_preferred) + + locale_spec="$(list_windows_locale_options)" + sound_spec="$(wor_sound_options)" + + advanced_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +ObjC.import('Foundation') +ObjC.import('stdlib') +const args = $.NSProcessInfo.processInfo.arguments +const checkboxSpec = ObjC.unwrap(args.objectAtIndex(4)) +const configTxtDefault = ObjC.unwrap(args.objectAtIndex(5)) +const applyConfigDefault = ObjC.unwrap(args.objectAtIndex(6)) +const iconPath = ObjC.unwrap(args.objectAtIndex(7)) +const appTitle = ObjC.unwrap(args.objectAtIndex(8)) +const applyConfigLabel = ObjC.unwrap(args.objectAtIndex(9)) +const cacheModeDefault = ObjC.unwrap(args.objectAtIndex(10)) +const accountUsernameDefault = ObjC.unwrap(args.objectAtIndex(11)) +const accountPasswordDefault = ObjC.unwrap(args.objectAtIndex(12)) +const localeDefault = ObjC.unwrap(args.objectAtIndex(13)) +const langSpec = ObjC.unwrap(args.objectAtIndex(14)) +const currentLangCode = ObjC.unwrap(args.objectAtIndex(15)) +const accountSetupDefault = ObjC.unwrap(args.objectAtIndex(16)) +const localeSetupDefault = ObjC.unwrap(args.objectAtIndex(17)) +const localeSpec = ObjC.unwrap(args.objectAtIndex(18)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(19)) +const soundSpec = ObjC.unwrap(args.objectAtIndex(20) || '') +const playSoundDefault = ObjC.unwrap(args.objectAtIndex(21) || '1') +const soundDefault = ObjC.unwrap(args.objectAtIndex(22) || '') +const showNotificationDefault = ObjC.unwrap(args.objectAtIndex(23) || '1') + +const soundOptions = [] +let initialSoundIdx = 0 +if (soundSpec && soundSpec.length > 0) { + const soundLines = soundSpec.split('\n') + for (let i = 0; i < soundLines.length; i++) { + const line = soundLines[i].trim() + if (!line) continue + const parts = line.split('\t') + soundOptions.push({ value: parts[0], label: parts[1] || parts[0] }) + if (parts[0] === soundDefault) initialSoundIdx = soundOptions.length - 1 + } +} + +const rows = checkboxSpec.split('\n').map(function(line) { + const parts = line.split('\t') + return { label: parts[0], checked: parts[1] === '1', enabled: parts[2] !== '0', caution: parts[3] === '1' } +}) + +const langOptions = [] +let initialLangIdx = 0 +if (langSpec && langSpec.length > 0) { + const langLines = langSpec.split('\n') + for (let i = 0; i < langLines.length; i++) { + const line = langLines[i].trim() + if (!line) continue + const parts = line.split('\t') + const code = parts[0] + const name = parts[1] || code + const label = name + ' (' + code + ')' + langOptions.push({ code: code, label: label }) + if (code === currentLangCode) { + initialLangIdx = langOptions.length - 1 + } + } +} + +const localeOptions = [] +let initialLocaleIdx = 0 +if (localeSpec && localeSpec.length > 0) { + const localeLines = localeSpec.split('\n') + for (let i = 0; i < localeLines.length; i++) { + const line = localeLines[i].trim() + if (!line) continue + const parts = line.split('\t') + const locale = parts[0] + const label = parts[1] || locale + localeOptions.push({ locale: locale, label: label }) + if (locale.toLowerCase() === localeDefault.toLowerCase()) { + initialLocaleIdx = localeOptions.length - 1 + } + } +} + +$.NSProcessInfo.processInfo.processName = appTitle +const app = $.NSApplication.sharedApplication +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worSetAppIcon(app, iconPath) + +//this screen adds an Edit menu on top of the shared one: without it Cmd+C/V/X/A have nothing to route to +const mainMenu = worInstallAppMenu(app, appTitle, windowTitle, iconPath) +const editMenuItem = $.NSMenuItem.alloc.init +mainMenu.addItem(editMenuItem) +const editMenu = $.NSMenu.alloc.initWithTitle('Edit') +editMenuItem.submenu = editMenu +editMenu.addItemWithTitleActionKeyEquivalent('Cut', 'cut:', 'x') +editMenu.addItemWithTitleActionKeyEquivalent('Copy', 'copy:', 'c') +editMenu.addItemWithTitleActionKeyEquivalent('Paste', 'paste:', 'v') +editMenu.addItemWithTitleActionKeyEquivalent('Select All', 'selectAll:', 'a') + +let window, applyConfigCheckbox, editConfigButton, cachePopup, winLangPopup, accountCheckbox, accountUsernameField, accountPasswordField, localeCheckbox, localePopup, playSoundCheckbox, soundPopup, soundLabel, notificationCheckbox +let checkboxes = [] +let confirmed = false +let configTxtValue = configTxtDefault + +function updateConfigEditableState() { + const enabled = applyConfigCheckbox.state == 1 + editConfigButton.enabled = enabled + editConfigButton.alphaValue = enabled ? 1.0 : 0.5 +} + +function editConfigTxt() { + const editorScroll = $.NSScrollView.alloc.initWithFrame($.NSMakeRect(0, 0, 560, 320)) + editorScroll.borderType = $.NSBezelBorder + editorScroll.hasVerticalScroller = true + const editor = $.NSTextView.alloc.initWithFrame(editorScroll.bounds) + editor.font = $.NSFont.userFixedPitchFontOfSize(12) + editor.string = $(configTxtValue) + editor.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable + editorScroll.documentView = editor + + const dialog = $.NSAlert.alloc.init + dialog.messageText = $('View / Edit config.txt') + dialog.informativeText = $('These settings control Raspberry Pi firmware and boot behavior.') + dialog.alertStyle = $.NSAlertStyleInformational + const dialogIcon = $.NSImage.alloc.initWithContentsOfFile($(iconPath)) + if (!dialogIcon.isNil()) dialog.icon = dialogIcon + dialog.accessoryView = editorScroll + dialog.addButtonWithTitle('Save') + dialog.addButtonWithTitle('Cancel') + if (dialog.runModal == $.NSAlertFirstButtonReturn) { + configTxtValue = ObjC.unwrap(editor.string) + } +} + +function updateAccountEditableState() { + const enabled = accountCheckbox.state == 1 + accountUsernameField.enabled = enabled + accountPasswordField.enabled = enabled + accountUsernameField.alphaValue = enabled ? 1.0 : 0.5 + accountPasswordField.alphaValue = enabled ? 1.0 : 0.5 +} + +function updateLocaleEditableState() { + const enabled = localeCheckbox.state == 1 + localePopup.enabled = enabled + localePopup.alphaValue = enabled ? 1.0 : 0.5 +} + +function updateSoundEnabledState() { + if (!soundPopup) return + const enabled = playSoundCheckbox.state == 1 + soundPopup.enabled = enabled + soundPopup.alphaValue = enabled ? 1.0 : 0.5 + soundLabel.alphaValue = enabled ? 1.0 : 0.5 +} + +function previewSelectedSound() { + if (!soundPopup || playSoundCheckbox.state != 1) return + const idx = soundPopup.indexOfSelectedItem + if (idx < 0 || idx >= soundOptions.length) return + //soundNamed hands back a truthy wrapper for a name it cannot find, so isNil is the only guard + const preview = $.NSSound.soundNamed(soundOptions[idx].value) + if (!preview.isNil()) preview.play +} + +const Controller = ObjC.registerSubclass({ + name: 'WorAdvancedController', + superclass: 'NSObject', + methods: { + 'okClicked:': { + types: ['void', ['id']], + implementation: function() { + confirmed = true + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'cancelClicked:': { + types: ['void', ['id']], + implementation: function() { + confirmed = false + app.stopModalWithCode($.NSCancelButton) + window.orderOut(null) + } + }, + 'applyConfigToggled:': { + types: ['void', ['id']], + implementation: function() { + updateConfigEditableState() + } + }, + 'editConfigClicked:': { + types: ['void', ['id']], + implementation: function() { + editConfigTxt() + } + }, + 'accountToggled:': { + types: ['void', ['id']], + implementation: function() { + updateAccountEditableState() + } + }, + 'localeToggled:': { + types: ['void', ['id']], + implementation: function() { + updateLocaleEditableState() + } + }, + 'playSoundToggled:': { + types: ['void', ['id']], + implementation: function() { + updateSoundEnabledState() + previewSelectedSound() + } + }, + 'soundPicked:': { + types: ['void', ['id']], + implementation: function() { + previewSelectedSound() + } + }, + 'windowWillClose:': { + types: ['void', ['id']], + implementation: function() { + confirmed = false + app.stopModalWithCode($.NSCancelButton) + } + }, + //right-click Quit from the Dock/app-switcher sends terminate: to NSApp; since this window is + //hosted by osascript rather than a full NSApplicationMain run loop, that does not otherwise exit the process + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + $.exit(0) + } + }, + 'pumpEvents:': { + types: ['void', ['id']], + implementation: function() { + $.NSRunLoop.currentRunLoop.runModeBeforeDate($.NSDefaultRunLoopMode, $.NSDate.dateWithTimeIntervalSinceNow(0.01)) + } + }, + //clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never come back + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + }, + 'applicationShouldTerminate:': { + types: ['NSUInteger', ['id']], + implementation: function() { + $.exit(0) + } + } + } +}) +const controller = $.WorAdvancedController.alloc.init +app.setDelegate(controller) + +const screenFrame = $.NSScreen.mainScreen.visibleFrame +const rowHeight = 26 +const sectionHeaderCount = soundOptions.length > 0 ? 8 : 7 +const desiredWidth = 640 +//height grows with the number of advanced-option rows, so clamp both dimensions to the visible screen instead of assuming they fit +const desiredHeight = (rows.length + 1) * rowHeight + 20 + 220 + 60 + 8 + sectionHeaderCount * 30 + + (soundOptions.length > 0 ? rowHeight * 3 + 8 : 0) +const width = Math.min(desiredWidth, screenFrame.size.width - 40) +const height = Math.min(desiredHeight, screenFrame.size.height - 60) +//fixed layout: no drag-resize and no zoom/maximize button, only minimize (and restore) via the titlebar +window = worMakeWindow({ width: width, height: height, title: windowTitle + ' | Advanced Options', delegate: controller }) + +const content = window.contentView +content.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable + +let y = height - 40 +function addSectionHeader(title) { + y -= 8 + const section = $.NSTextField.labelWithString(title) + section.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightSemibold) + section.textColor = $.NSColor.secondaryLabelColor + section.frame = $.NSMakeRect(20, y, width - 40, 18) + section.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(section) + y -= 24 +} + +for (let i = 0; i < rows.length; i++) { + if (i === 0) addSectionHeader('Windows setup') + if (i === 2) addSectionHeader('Firmware and drivers') + if (i === 4) addSectionHeader('Validation') + const checkbox = $.NSButton.checkboxWithTitleTargetAction(rows[i].label, undefined, undefined) + if (rows[i].caution) worAnnotateCheckbox(checkbox, rows[i].label, 'Not recommended', $.NSColor.systemRedColor) + checkbox.frame = $.NSMakeRect(20, y, width - 40, 20) + checkbox.state = rows[i].checked ? 1 : 0 + checkbox.enabled = rows[i].enabled + checkbox.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(checkbox) + checkboxes.push(checkbox) + y -= rowHeight +} + +addSectionHeader('Downloads') +//USE_CACHE has three values, so it needs a menu rather than a checkbox +const cacheLabel = $.NSTextField.labelWithString('Downloaded files:') +cacheLabel.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightMedium) +cacheLabel.frame = $.NSMakeRect(20, y - 2, 120, 20) +cacheLabel.autoresizingMask = $.NSViewMaxXMargin | $.NSViewMinYMargin +content.addSubview(cacheLabel) +cachePopup = $.NSPopUpButton.alloc.initWithFramePullsDown($.NSMakeRect(146, y - 6, width - 166, 26), false) +cachePopup.addItemWithTitle('Re-download everything, ignoring the cache') +cachePopup.addItemWithTitle('Reuse cached files when they still match (recommended)') +cachePopup.addItemWithTitle('Trust the cache without checking it') +cachePopup.selectItemAtIndex(cacheModeDefault === '0' ? 0 : (cacheModeDefault === '2' ? 2 : 1)) +cachePopup.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin +content.addSubview(cachePopup) +y -= rowHeight + 8 + +if (langOptions.length > 0) { + const winLangLabel = $.NSTextField.labelWithString('Choose Windows language:') + winLangLabel.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightMedium) + winLangLabel.frame = $.NSMakeRect(20, y - 2, 170, 20) + winLangLabel.autoresizingMask = $.NSViewMaxXMargin | $.NSViewMinYMargin + content.addSubview(winLangLabel) + winLangPopup = $.NSPopUpButton.alloc.initWithFramePullsDown($.NSMakeRect(196, y - 6, width - 216, 26), false) + for (let i = 0; i < langOptions.length; i++) { + winLangPopup.addItemWithTitle($(langOptions[i].label)) + } + winLangPopup.selectItemAtIndex(initialLangIdx) + winLangPopup.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(winLangPopup) + y -= rowHeight + 8 +} + +if (soundOptions.length > 0) { + addSectionHeader('Notifications') + playSoundCheckbox = $.NSButton.checkboxWithTitleTargetAction('Play a sound when the flash finishes', controller, 'playSoundToggled:') + playSoundCheckbox.frame = $.NSMakeRect(20, y, width - 40, 20) + playSoundCheckbox.state = playSoundDefault === '0' ? 0 : 1 + playSoundCheckbox.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(playSoundCheckbox) + y -= rowHeight + + soundLabel = $.NSTextField.labelWithString('Completion sound:') + soundLabel.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightMedium) + soundLabel.frame = $.NSMakeRect(20, y - 2, 130, 20) + soundLabel.autoresizingMask = $.NSViewMaxXMargin | $.NSViewMinYMargin + content.addSubview(soundLabel) + soundPopup = $.NSPopUpButton.alloc.initWithFramePullsDown($.NSMakeRect(156, y - 6, width - 176, 26), false) + for (let i = 0; i < soundOptions.length; i++) { + soundPopup.addItemWithTitle($(soundOptions[i].label)) + } + soundPopup.selectItemAtIndex(initialSoundIdx) + soundPopup.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + //hearing the choice is the whole point, so play it as it is picked + soundPopup.target = controller + soundPopup.action = 'soundPicked:' + content.addSubview(soundPopup) + updateSoundEnabledState() + y -= rowHeight + 8 + + notificationCheckbox = $.NSButton.checkboxWithTitleTargetAction('Show a notification when the flash finishes', undefined, undefined) + notificationCheckbox.frame = $.NSMakeRect(20, y, width - 40, 20) + notificationCheckbox.state = showNotificationDefault === '0' ? 0 : 1 + notificationCheckbox.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(notificationCheckbox) + y -= rowHeight +} + +addSectionHeader('Windows account') +accountCheckbox = $.NSButton.checkboxWithTitleTargetAction('Create a local Windows administrator account', controller, 'accountToggled:') +accountCheckbox.frame = $.NSMakeRect(20, y, width - 40, 20) +accountCheckbox.state = accountSetupDefault === '1' ? 1 : 0 +accountCheckbox.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin +content.addSubview(accountCheckbox) +y -= rowHeight + +function addAdvancedField(label, value, secure) { + const fieldLabel = $.NSTextField.labelWithString(label) + fieldLabel.frame = $.NSMakeRect(20, y, 170, 20) + fieldLabel.autoresizingMask = $.NSViewMaxXMargin | $.NSViewMinYMargin + content.addSubview(fieldLabel) + const field = secure ? $.NSSecureTextField.alloc.initWithFrame($.NSMakeRect(196, y - 2, width - 216, 24)) : $.NSTextField.alloc.initWithFrame($.NSMakeRect(196, y - 2, width - 216, 24)) + field.stringValue = $(value) + field.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin + content.addSubview(field) + y -= rowHeight + return field +} +accountUsernameField = addAdvancedField('Windows username:', accountUsernameDefault, false) +accountPasswordField = addAdvancedField('Windows password:', accountPasswordDefault, true) +updateAccountEditableState() + +addSectionHeader('Regional settings') +localeCheckbox = $.NSButton.checkboxWithTitleTargetAction('Configure Windows keyboard and regional settings', controller, 'localeToggled:') +localeCheckbox.frame = $.NSMakeRect(20, y, width - 40, 20) +localeCheckbox.state = localeSetupDefault === '1' ? 1 : 0 +localeCheckbox.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin +content.addSubview(localeCheckbox) +y -= rowHeight + +const localeLabel = $.NSTextField.labelWithString('Windows locale:') +localeLabel.frame = $.NSMakeRect(20, y, 170, 20) +localeLabel.autoresizingMask = $.NSViewMaxXMargin | $.NSViewMinYMargin +content.addSubview(localeLabel) +localePopup = $.NSPopUpButton.alloc.initWithFramePullsDown($.NSMakeRect(196, y - 6, width - 216, 26), false) +for (let i = 0; i < localeOptions.length; i++) { + localePopup.addItemWithTitle($(localeOptions[i].label)) +} +if (localeOptions.length === 0) { + localeOptions.push({ locale: localeDefault, label: localeDefault }) + localePopup.addItemWithTitle($(localeDefault)) +} +localePopup.selectItemAtIndex(initialLocaleIdx) +localePopup.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin +content.addSubview(localePopup) +y -= rowHeight +updateLocaleEditableState() + +addSectionHeader('Raspberry Pi boot config') +applyConfigCheckbox = $.NSButton.checkboxWithTitleTargetAction(applyConfigLabel, controller, 'applyConfigToggled:') +worAnnotateCheckbox(applyConfigCheckbox, applyConfigLabel, 'Recommended', $.NSColor.systemGreenColor) +applyConfigCheckbox.frame = $.NSMakeRect(20, y, width - 200, 20) +applyConfigCheckbox.state = applyConfigDefault === '1' ? 1 : 0 +applyConfigCheckbox.autoresizingMask = $.NSViewWidthSizable | $.NSViewMinYMargin +content.addSubview(applyConfigCheckbox) +editConfigButton = $.NSButton.buttonWithTitleTargetAction('View / Edit…', controller, 'editConfigClicked:') +editConfigButton.bezelStyle = $.NSBezelStyleRounded +editConfigButton.frame = $.NSMakeRect(width - 160, y - 6, 140, 28) +editConfigButton.autoresizingMask = $.NSViewMinXMargin | $.NSViewMinYMargin +content.addSubview(editConfigButton) +updateConfigEditableState() + +const okButton = $.NSButton.buttonWithTitleTargetAction('OK', controller, 'okClicked:') +okButton.bezelStyle = $.NSBezelStyleRounded +okButton.keyEquivalent = '\r' +okButton.sizeToFit +const okWidth = Math.max(96, okButton.frame.size.width) +okButton.frame = $.NSMakeRect(width - 20 - okWidth, 20, okWidth, 32) +okButton.autoresizingMask = $.NSViewMinXMargin | $.NSViewMaxYMargin +content.addSubview(okButton) + +const cancelButton = $.NSButton.buttonWithTitleTargetAction('Back', controller, 'cancelClicked:') +cancelButton.bezelStyle = $.NSBezelStyleRounded +cancelButton.keyEquivalent = '\u001b' +cancelButton.sizeToFit +const cancelWidth = Math.max(96, cancelButton.frame.size.width) +cancelButton.frame = $.NSMakeRect(width - 20 - okWidth - 8 - cancelWidth, 20, cancelWidth, 32) +cancelButton.autoresizingMask = $.NSViewMinXMargin | $.NSViewMaxYMargin +content.addSubview(cancelButton) + +window.makeKeyAndOrderFront(null) +if (!app.isActive) app.requestUserAttention($.NSInformationalRequest) +app.activateIgnoringOtherApps(true) +worInstallWindowHandlers(controller) +app.runModalForWindow(window) + +function writeResult(lines) { + const data = $(lines.join('\n') + '\n').dataUsingEncoding($.NSUTF8StringEncoding) + $.NSFileHandle.fileHandleWithStandardOutput.writeData(data) +} + +if (!confirmed) { + writeResult(['CANCEL']) + app.terminate(null) +} + +const out = ['OK'] +for (let i = 0; i < checkboxes.length; i++) { + out.push(checkboxes[i].state == 1 ? '1' : '0') +} +out.push(accountCheckbox.state == 1 ? '1' : '0') +out.push(localeCheckbox.state == 1 ? '1' : '0') +out.push(applyConfigCheckbox.state == 1 ? '1' : '0') +out.push(String(cachePopup.indexOfSelectedItem)) +out.push(ObjC.unwrap(accountUsernameField.stringValue)) +out.push(ObjC.unwrap(accountPasswordField.stringValue)) +let selectedLocale = localeDefault +if (localePopup && localeOptions.length > 0) { + const localeIdx = localePopup.indexOfSelectedItem + if (localeIdx >= 0 && localeIdx < localeOptions.length) { + selectedLocale = localeOptions[localeIdx].locale + } +} +out.push(selectedLocale) +let selectedLang = currentLangCode +if (winLangPopup && langOptions.length > 0) { + const selIdx = winLangPopup.indexOfSelectedItem + if (selIdx >= 0 && selIdx < langOptions.length) { + selectedLang = langOptions[selIdx].code + } +} +out.push(selectedLang) +//appended after the existing fields so the positional reader above them keeps working +out.push(playSoundCheckbox && playSoundCheckbox.state == 1 ? '1' : (soundOptions.length > 0 ? '0' : playSoundDefault)) +let selectedSound = soundDefault +if (soundPopup && soundOptions.length > 0) { + const soundIdx = soundPopup.indexOfSelectedItem + if (soundIdx >= 0 && soundIdx < soundOptions.length) { + selectedSound = soundOptions[soundIdx].value + } +} +out.push(selectedSound) +out.push(notificationCheckbox && soundOptions.length > 0 ? (notificationCheckbox.state == 1 ? '1' : '0') : showNotificationDefault) +out.push('---CONFIG_TXT---') +out.push(configTxtValue) +writeResult(out) +app.terminate(null) +JXA +)" + + result="$(wor_osascript -l JavaScript - "$checkbox_spec" "$CONFIG_TXT" "$APPLY_CUSTOM_CONFIG_TXT" "$WOR_ICON_PATH" "$WOR_APP_TITLE" "$(wor_config_txt_label "$config_scope")" "$USE_CACHE" "$WINDOWS_ACCOUNT_USERNAME" "$WINDOWS_ACCOUNT_PASSWORD" "$WINDOWS_LOCALE" "$lang_spec" "$WIN_LANG" "$WINDOWS_ACCOUNT_SETUP" "$WINDOWS_LOCALE_SETUP" "$locale_spec" "$WOR_WINDOW_TITLE" "$sound_spec" "$PLAY_SOUND" "$(wor_completion_sound)" "$SHOW_NOTIFICATION" <<<"$advanced_jxa" 2>/dev/null)" + #config.txt and account fields are user-editable bytes; BSD sed rejects malformed UTF-8 under the + #desktop locale, so parse the machine-readable result in byte mode after AppKit has finished. + LC_ALL=C + status="$(printf '%s\n' "$result" | sed -n '1p')" + [ "$status" == OK ] || return 1 + + i=0 + while IFS= read -r line;do + i=$((i+1)) + case "$i" in + 1) OOBE_NETWORK_BYPASS="$line" ;; + 2) [ "$pi4_applicable" == 1 ] && PI4_AUTO_DISABLE_3GB="$line" ;; + 3) UEFI_USE_LATEST="$line" ;; + 4) DRIVERS_USE_LATEST="$line" ;; + 5) SKIP_IMAGE_VERIFICATION="$line" ;; + 6) DRY_RUN="$line" ;; + 7) WINDOWS_ACCOUNT_SETUP="$line" ;; + 8) WINDOWS_LOCALE_SETUP="$line" ;; + 9) APPLY_CUSTOM_CONFIG_TXT="$line" ;; + esac + done < <(printf '%s\n' "$result" | sed -n '2,10p') + case "$(printf '%s\n' "$result" | sed -n '11p')" in + 0 | 1 | 2) USE_CACHE="$(printf '%s\n' "$result" | sed -n '11p')" ;; + esac + WINDOWS_ACCOUNT_USERNAME="$(printf '%s\n' "$result" | sed -n '12p')" + WINDOWS_ACCOUNT_PASSWORD="$(printf '%s\n' "$result" | sed -n '13p')" + WINDOWS_LOCALE="$(printf '%s\n' "$result" | sed -n '14p')" + sel_win_lang="$(printf '%s\n' "$result" | sed -n '15p')" + if is_known_win_lang "$sel_win_lang" ;then + WIN_LANG="$sel_win_lang" + fi + case "$(printf '%s\n' "$result" | sed -n '16p')" in + 0 | 1) PLAY_SOUND="$(printf '%s\n' "$result" | sed -n '16p')" ;; + esac + sel_sound="$(printf '%s\n' "$result" | sed -n '17p')" + [ -n "$sel_sound" ] && COMPLETION_SOUND="$sel_sound" + case "$(printf '%s\n' "$result" | sed -n '18p')" in + 0 | 1) SHOW_NOTIFICATION="$(printf '%s\n' "$result" | sed -n '18p')" ;; + esac + + CONFIG_TXT="$(printf '%s\n' "$result" | sed -n '/^---CONFIG_TXT---$/,$p' | tail -n +2)" +} -if [ -z "$DRY_RUN" ];then - DRY_RUN=0 +macos_choose_target() { #Input: current-run Windows/Pi defaults. Output: selected Windows version and Raspberry Pi model separated by a tab. + local default_pi_label default_windows_label target_jxa result + default_windows_label="${1:-Windows 11}" + case "${2:-}" in + 5) default_pi_label='Raspberry Pi 5' ;; + 4) default_pi_label='Raspberry Pi 4 / Pi 400' ;; + 3) default_pi_label='Raspberry Pi 3 / Pi 2 v1.2' ;; + *) default_pi_label='Raspberry Pi 5' ;; + esac + target_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +ObjC.import('Foundation') +ObjC.import('stdlib') + +const args = $.NSProcessInfo.processInfo.arguments +const iconPath = ObjC.unwrap(args.objectAtIndex(4)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(5)) +const appTitle = ObjC.unwrap(args.objectAtIndex(6)) +const defaultWindows = ObjC.unwrap(args.objectAtIndex(7) || 'Windows 11') +const defaultPiModel = ObjC.unwrap(args.objectAtIndex(8) || 'Raspberry Pi 5') +const app = $.NSApplication.sharedApplication +const windows = ['Windows 11', 'Windows 10'] +const piModels = ['Raspberry Pi 5', 'Raspberry Pi 4 / Pi 400', 'Raspberry Pi 3 / Pi 2 v1.2'] +const defaultWindowsIdx = Math.max(0, windows.indexOf(defaultWindows)) +const defaultPiIdx = Math.max(0, piModels.indexOf(defaultPiModel)) +let window +let selectedValue = windows[defaultWindowsIdx] + '\t' + piModels[defaultPiIdx] +let allowTermination = false + +function writeResult(value) { + const data = $(value + '\n').dataUsingEncoding($.NSUTF8StringEncoding) + $.NSFileHandle.fileHandleWithStandardOutput.writeData(data) +} + +function cancelAndExit() { + writeResult('__WOR_CANCEL__') + $.exit(0) +} + +const Controller = ObjC.registerSubclass({ + name: 'WorTargetController', + superclass: 'NSObject', + methods: { + 'nextClicked:': { + types: ['void', ['id']], + implementation: function() { + const windowsIdx = windowsPopup.indexOfSelectedItem + const piIdx = piPopup.indexOfSelectedItem + selectedValue = windows[windowsIdx] + '\t' + piModels[piIdx] + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'cancelClicked:': { + types: ['void', ['id']], + implementation: function() { + cancelAndExit() + } + }, + 'windowWillClose:': { + types: ['void', ['id']], + implementation: function() { + cancelAndExit() + } + }, + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + cancelAndExit() + } + }, + 'pumpEvents:': { + types: ['void', ['id']], + implementation: function() { + $.NSRunLoop.currentRunLoop.runModeBeforeDate($.NSDefaultRunLoopMode, $.NSDate.dateWithTimeIntervalSinceNow(0.01)) + } + }, + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + }, + 'applicationShouldTerminate:': { + types: ['NSUInteger', ['id']], + implementation: function() { + if (allowTermination) return $.NSTerminateNow + cancelAndExit() + } + } + } +}) + +const controller = $.WorTargetController.alloc.init +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worInstallAppMenu(app, appTitle, windowTitle, iconPath) +worSetAppIcon(app, iconPath) +app.setDelegate(controller) +const screenFrame = $.NSScreen.mainScreen.visibleFrame +const width = Math.min(640, screenFrame.size.width - 40) +const height = Math.min(250, screenFrame.size.height - 60) +window = worMakeWindow({ width: width, height: height, title: windowTitle, delegate: controller }) +const content = window.contentView +content.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable + +const heading = $.NSTextField.labelWithString('Choose Windows and Raspberry Pi target') +heading.font = $.NSFont.systemFontOfSizeWeight(18, $.NSFontWeightSemibold) +heading.frame = $.NSMakeRect(24, height - 58, width - 48, 28) +content.addSubview(heading) + +const windowsLabel = $.NSTextField.labelWithString('Windows version:') +windowsLabel.frame = $.NSMakeRect(24, height - 112, 170, 24) +content.addSubview(windowsLabel) +const windowsPopup = $.NSPopUpButton.alloc.initWithFrame($.NSMakeRect(190, height - 116, width - 214, 30)) +for (let i = 0; i < windows.length; i++) windowsPopup.addItemWithTitle($(windows[i])) +windowsPopup.selectItemAtIndex(defaultWindowsIdx) +content.addSubview(windowsPopup) + +const piLabel = $.NSTextField.labelWithString('Raspberry Pi model:') +piLabel.frame = $.NSMakeRect(24, height - 158, 170, 24) +content.addSubview(piLabel) +const piPopup = $.NSPopUpButton.alloc.initWithFrame($.NSMakeRect(190, height - 162, width - 214, 30)) +for (let i = 0; i < piModels.length; i++) piPopup.addItemWithTitle($(piModels[i])) +piPopup.selectItemAtIndex(defaultPiIdx) +content.addSubview(piPopup) + +const cancelButton = $.NSButton.buttonWithTitleTargetAction('Cancel', controller, 'cancelClicked:') +cancelButton.bezelStyle = $.NSBezelStyleRounded +cancelButton.frame = $.NSMakeRect(width - 220, 20, 92, 32) +content.addSubview(cancelButton) +const nextButton = $.NSButton.buttonWithTitleTargetAction('Next', controller, 'nextClicked:') +nextButton.bezelStyle = $.NSBezelStyleRounded +nextButton.frame = $.NSMakeRect(width - 116, 20, 92, 32) +nextButton.keyEquivalent = '\\r' +content.addSubview(nextButton) + +worInstallWindowHandlers(controller) +window.makeKeyAndOrderFront(null) +app.activateIgnoringOtherApps(true) +app.runModalForWindow(window) +if (selectedValue === null || selectedValue.length === 0) cancelAndExit() +writeResult(selectedValue) +allowTermination = true +$.exit(0) +JXA +)" + result="$(wor_osascript -l JavaScript - "$WOR_ICON_PATH" "$WOR_WINDOW_TITLE" "$WOR_APP_TITLE" "$default_windows_label" "$default_pi_label" <<<"$target_jxa")" + result="$(printf '%s\n' "$result" | awk 'index($0, "\t") { print; exit }')" + result="${result%%__WOR_CANCEL__*}" + result="$(printf '%s' "$result" | tr -d '\r')" + [ "$result" != __WOR_CANCEL__ ] || return 1 + [ -n "$result" ] || return 1 + printf '%s\n' "$result" +} + +macos_start_cli() { + local completion_jxa confirm_summary confirmation current_rpi_model current_windows_ver default_language device_choices device_capability device_choice done_marker abort_marker auth_marker error_marker install_mode installer_pid installer_status language_choices mode_choices output_log password_retry_choice password_retry_reason privacy_guidance privacy_settings_url progress_file progress_jxa resume_at_flash saved_log step target_choice disk_alert_pid disk_alert_status disk_alert_done disk_alert_log disk_alert_warning + + current_windows_ver='Windows 11' + current_rpi_model='' + step=target + while true; do + case "$step" in + target) + target_choice="$(macos_choose_target "$current_windows_ver" "$current_rpi_model")" || exit 0 + WINDOWS_VER="${target_choice%%$'\t'*}" + case "${target_choice#*$'\t'}" in + 'Raspberry Pi 5') RPI_MODEL=5 ;; + 'Raspberry Pi 4 / Pi 400') RPI_MODEL=4 ;; + 'Raspberry Pi 3 / Pi 2 v1.2') RPI_MODEL=3 ;; + *) error "Unrecognized Raspberry Pi selection '${target_choice#*$'\t'}'" ;; + esac + current_windows_ver="$WINDOWS_VER" + current_rpi_model="$RPI_MODEL" + list_bids 10 >/dev/null || error "Failed to retrieve available Windows versions." + [ "$WINDOWS_VER" == 'Windows 11' ] && BID="$(get_bid 11)" || BID="$(get_bid 10)" + [ -n "$BID" ] || error "No compatible Windows build is available for Raspberry Pi $RPI_MODEL." + set_default_config_txt + [ -z "$WIN_LANG" ] && WIN_LANG="$(default_win_lang)" + step=device + ;; + language) + [ -z "$WIN_LANG" ] && WIN_LANG="$(default_win_lang)" + step=device + ;; + device) + device_choices="$(darwin_list_device_choices)" + device_choice="$(macos_choose_device "$device_choices")" || exit 0 + if [ "$device_choice" == Back ];then + step=target + continue + fi + [ "$device_choice" == __REFRESH__ ] && continue + DEVICE="${device_choice%%$'\t'*}" + is_safe_target_device "$DEVICE" || error "Refusing to overwrite $DEVICE. Choose an external, physical, writable whole disk." + device_capability="$(drive_capability "$DEVICE")" + validate_install_mode "$device_capability" + step=mode + ;; + mode) + if [ "$device_capability" == recovery ];then + CAN_INSTALL_ON_SAME_DRIVE=0 + else + mode_choices=$'Install Windows onto this drive\nCreate a recovery drive' + install_mode="$(macos_choose "$mode_choices" 'Choose installation mode' 'Install Windows onto this drive' Back '' '' '' '' '' '' Back)" || exit 0 + if [ "$install_mode" == Back ];then + step=device + continue + fi + [ "$install_mode" == 'Install Windows onto this drive' ] && CAN_INSTALL_ON_SAME_DRIVE=1 || CAN_INSTALL_ON_SAME_DRIVE=0 + fi + step=confirm + ;; + confirm) + confirmation="$(macos_confirm_flash)" || confirmation=Cancel + [ "$confirmation" == Cancel ] && exit 0 + if [ "$confirmation" == Advanced ];then + macos_advanced_options + continue + fi + [ "$confirmation" == Flash ] && break + step=mode + ;; + esac + done + + completion_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +ObjC.import('stdlib') +const args = $.NSProcessInfo.processInfo.arguments +const message = ObjC.unwrap(args.objectAtIndex(4)) +const iconPath = ObjC.unwrap(args.objectAtIndex(5)) +const appTitle = ObjC.unwrap(args.objectAtIndex(6)) +const imagePath = ObjC.unwrap(args.objectAtIndex(7) || '') +const windowTitle = ObjC.unwrap(args.objectAtIndex(8) || appTitle) +const successSound = ObjC.unwrap(args.objectAtIndex(9) || '') +const settingsUrl = ObjC.unwrap(args.objectAtIndex(10) || '') + +$.NSProcessInfo.processInfo.processName = appTitle +const app = $.NSApplication.sharedApplication +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worInstallAppMenu(app, appTitle, windowTitle, iconPath) +worSetAppIcon(app, iconPath) + +let window +const Controller = ObjC.registerSubclass({ + name: 'WorCompletionController', + superclass: 'NSObject', + methods: { + 'openLogClicked:': { + types: ['void', ['id']], + implementation: function() { + if (logPath.length > 0) { + $.NSWorkspace.sharedWorkspace.openFileWithApplication($(logPath), $()) + } + } + }, + 'copyLogClicked:': { + types: ['void', ['id']], + implementation: function() { + if (logPath.length > 0) { + const pb = $.NSPasteboard.generalPasteboard + pb.clearContents + pb.setStringForType($(logPath), $.NSPasteboardTypeString) + } + } + }, + 'openSettingsClicked:': { + types: ['void', ['id']], + implementation: function() { + if (settingsUrl.length > 0) { + $.NSWorkspace.sharedWorkspace.openURL($.NSURL.URLWithString($(settingsUrl))) + } + } + }, + 'okClicked:': { + types: ['void', ['id']], + implementation: function() { + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'windowWillClose:': { + types: ['void', ['id']], + implementation: function() { + app.stopModalWithCode($.NSOKButton) + } + }, + //right-click Quit from the Dock/app-switcher sends terminate: to NSApp; since this window is + //hosted by osascript rather than a full NSApplicationMain run loop, that does not otherwise exit the process + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + $.exit(0) + } + }, + 'pumpEvents:': { + types: ['void', ['id']], + implementation: function() { + $.NSRunLoop.currentRunLoop.runModeBeforeDate($.NSDefaultRunLoopMode, $.NSDate.dateWithTimeIntervalSinceNow(0.01)) + } + }, + //clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never come back + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + }, + 'applicationShouldTerminate:': { + types: ['NSUInteger', ['id']], + implementation: function() { + $.exit(0) + } + } + } +}) +const controller = $.WorCompletionController.alloc.init +app.setDelegate(controller) + +const contentMargin = 20 +const buttonY = 22 +const buttonHeight = 32 +let width = 600 +let height = 360 +let bannerImage = null +let bannerWidth = 0 +let bannerHeight = 0 + +if (imagePath.length > 0) { + bannerImage = $.NSImage.alloc.initWithContentsOfFile($(imagePath)) + //NSImage.size is DPI-scaled, so a 96dpi PNG reports three quarters of its pixels; measure the bitmap. + //Number() is required: JXA hands these back as strings, and '650' + 40 would concatenate into a bogus frame. + const reps = bannerImage.representations + const rep = Number(reps.count) > 0 ? reps.objectAtIndex(0) : null + bannerWidth = rep ? Number(rep.pixelsWide) : Number(bannerImage.size.width) + bannerHeight = rep ? Number(rep.pixelsHigh) : Number(bannerImage.size.height) + const maxBannerWidth = 760 + if (bannerWidth > maxBannerWidth) { + bannerHeight = Math.round(bannerHeight * (maxBannerWidth / bannerWidth)) + bannerWidth = maxBannerWidth + } + width = Math.max(600, bannerWidth + contentMargin * 2) +} + +//measure at the final width so neither layout can clip the message +const measured = $.NSMutableAttributedString.alloc.init +measured.mutableString.appendString($(message)) +measured.addAttributeValueRange($.NSFontAttributeName, $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium), $.NSMakeRange(0, message.length)) +const measuredBounds = measured.boundingRectWithSizeOptions($.NSMakeSize(width - contentMargin * 2, 2000), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) +const textHeight = Math.ceil(Number(measuredBounds.size.height)) + +if (imagePath.length > 0) { + height = contentMargin + bannerHeight + 24 + textHeight + 20 + buttonHeight + buttonY +} else { + //100pt of chrome: the button row below the text and the gap under the title bar + height = Math.min(560, Math.max(200, textHeight + 100)) +} +//fixed layout: no drag-resize, no zoom/maximize, and no minimize - this is a terminal screen, so +//the close box is the only titlebar control and behaves the same as clicking OK (see windowWillClose:) +window = worMakeWindow({ width: width, height: height, title: windowTitle, delegate: controller, miniaturizable: false }) + +const content = window.contentView + +const textY = imagePath.length > 0 ? buttonY + buttonHeight + 20 : 70 +const label = $.NSTextField.labelWithString(message) +label.font = $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium) +label.setUsesSingleLineMode(false) +label.cell.setWraps(true) +label.cell.setScrollable(false) +label.frame = $.NSMakeRect(contentMargin, textY, width - contentMargin * 2, imagePath.length > 0 ? textHeight : height - 100) +//1, not $.NSTextAlignmentCenter: that constant bridges as 2, which this runtime draws right-aligned +if (imagePath.length > 0) label.setAlignment(1) +content.addSubview(label) + +if (imagePath.length > 0) { + const imageView = $.NSImageView.alloc.initWithFrame($.NSMakeRect(Math.round((width - bannerWidth) / 2), textY + textHeight + 24, bannerWidth, bannerHeight)) + imageView.image = bannerImage + imageView.imageScaling = $.NSImageScaleProportionallyUpOrDown + content.addSubview(imageView) +} + +//extract the log file path if present (after "Full log: ") and create clickable buttons +let logPath = '' +const logMatch = message.match(/Full log: (.+)$/) +if (logMatch) { + logPath = logMatch[1].trim() +} + +let okButton, openButton, copyButton, settingsButton +const buttonGap = 8 +let nextButtonX = 20 +//"Complete" only on the success screen; a failure dialog reached through Open Log/Copy is not a completion +const primaryLabel = imagePath.length > 0 ? 'Complete' : 'OK' +if (logPath.length > 0) { + openButton = $.NSButton.buttonWithTitleTargetAction('Open Log', controller, 'openLogClicked:') + openButton.bezelStyle = $.NSBezelStyleRounded + openButton.sizeToFit + const openWidth = Math.max(96, openButton.frame.size.width) + openButton.frame = $.NSMakeRect(nextButtonX, buttonY, openWidth, buttonHeight) + content.addSubview(openButton) + nextButtonX += openWidth + buttonGap + + copyButton = $.NSButton.buttonWithTitleTargetAction('Copy', controller, 'copyLogClicked:') + copyButton.bezelStyle = $.NSBezelStyleRounded + copyButton.sizeToFit + const copyWidth = Math.max(80, copyButton.frame.size.width) + copyButton.frame = $.NSMakeRect(nextButtonX, buttonY, copyWidth, buttonHeight) + content.addSubview(copyButton) + nextButtonX += copyWidth + buttonGap + + if (settingsUrl.length > 0) { + settingsButton = $.NSButton.buttonWithTitleTargetAction('Open Settings', controller, 'openSettingsClicked:') + settingsButton.bezelStyle = $.NSBezelStyleRounded + settingsButton.sizeToFit + const settingsWidth = Math.max(120, settingsButton.frame.size.width) + settingsButton.frame = $.NSMakeRect(nextButtonX, buttonY, settingsWidth, buttonHeight) + content.addSubview(settingsButton) + } + + okButton = $.NSButton.buttonWithTitleTargetAction(primaryLabel, controller, 'okClicked:') + okButton.bezelStyle = $.NSBezelStyleRounded + okButton.keyEquivalent = '\r' + okButton.sizeToFit + const okWidth = Math.max(96, okButton.frame.size.width) + okButton.frame = $.NSMakeRect(width - 20 - okWidth, buttonY, okWidth, buttonHeight) + content.addSubview(okButton) +} else { + okButton = $.NSButton.buttonWithTitleTargetAction(primaryLabel, controller, 'okClicked:') + okButton.bezelStyle = $.NSBezelStyleRounded + okButton.keyEquivalent = '\r' + okButton.sizeToFit + const okWidth = Math.max(96, okButton.frame.size.width) + //centred under the message when it is the only button, right-aligned otherwise + const okX = imagePath.length > 0 ? Math.round((width - okWidth) / 2) : width - 20 - okWidth + okButton.frame = $.NSMakeRect(okX, buttonY, okWidth, buttonHeight) + content.addSubview(okButton) +} + +//a flash runs long enough to walk away from, so announce the result; these ship with macOS, so there +//is no audio asset to bundle, and they follow the system alert volume and Do Not Disturb settings. +//soundNamed returns a truthy wrapper for a missing name, so isNil is the only guard that works. +//An empty successSound means the user turned the sound off; a failure keeps the standard error sound. +const soundName = imagePath.length > 0 ? successSound : 'Basso' +if (soundName.length > 0) { + const completionSound = $.NSSound.soundNamed(soundName) + if (!completionSound.isNil()) completionSound.play +} + +window.makeKeyAndOrderFront(null) +if (!app.isActive) app.requestUserAttention($.NSInformationalRequest) +app.activateIgnoringOtherApps(true) +worInstallWindowHandlers(controller) +app.runModalForWindow(window) +app.terminate(null) +JXA +)" + + progress_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +ObjC.import('Foundation') +ObjC.import('stdlib') +const args = $.NSProcessInfo.processInfo.arguments +const progressFile = ObjC.unwrap(args.objectAtIndex(4)) +const doneMarker = ObjC.unwrap(args.objectAtIndex(5)) +const iconPath = ObjC.unwrap(args.objectAtIndex(6)) +const appTitle = ObjC.unwrap(args.objectAtIndex(7)) +const abortMarker = ObjC.unwrap(args.objectAtIndex(8)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(9) || appTitle) +const diskAlertStatus = ObjC.unwrap(args.objectAtIndex(10)) +const diskAlertDone = ObjC.unwrap(args.objectAtIndex(11)) + +$.NSProcessInfo.processInfo.processName = appTitle +const app = $.NSApplication.sharedApplication +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worInstallAppMenu(app, appTitle, windowTitle, iconPath) +worSetAppIcon(app, iconPath) + +const fm = $.NSFileManager.defaultManager + +function readFile(path) { + if (!fm.fileExistsAtPath($(path))) return '' + try { + const data = $.NSString.stringWithContentsOfFileEncodingError($(path), $.NSUTF8StringEncoding, undefined) + return ObjC.unwrap(data) || '' + } catch (e) { + return '' + } +} + +function lastMatch(lines, prefix) { + for (let i = lines.length - 1; i >= 0; i--) { + if (lines[i].indexOf(prefix) === 0) return lines[i] + } + return '' +} + +let window, bar, taskBar, phaseLabel, detailLabel, stepLabel, stepPercentLabel, taskPercentLabel, diskAlertLabel + +function updateDiskAlertStatus() { + try { + const text = readFile(diskAlertStatus) + if (text.length > 0) { + const status = JSON.parse(text) + diskAlertLabel.stringValue = status.message + diskAlertLabel.textColor = status.state === 'warning' ? $.NSColor.systemOrangeColor : $.NSColor.secondaryLabelColor + } + const result = readFile(diskAlertDone).trim() + if (result.length > 0 && result !== '0') { + diskAlertLabel.stringValue = 'Automatic Ignore is unavailable. Allow WoR-Flasher in Accessibility and Automation, or choose Ignore manually.' + diskAlertLabel.textColor = $.NSColor.systemOrangeColor + } + } catch (error) { + diskAlertLabel.stringValue = 'Automatic Ignore status is unavailable. Choose Ignore manually if the unreadable-disk alert appears.' + diskAlertLabel.textColor = $.NSColor.systemOrangeColor + } +} + +//stopping part-way leaves an unbootable drive, so make the user confirm and record why we stopped +function confirmAbort() { + const alert = $.NSAlert.alloc.init + alert.messageText = 'Stop flashing this drive?' + alert.informativeText = 'The drive will be left unusable and has to be flashed again before it can boot.' + alert.alertStyle = $.NSAlertStyleCritical + alert.addButtonWithTitle('Stop flashing') + alert.addButtonWithTitle('Keep going') + if (alert.runModal !== $.NSAlertFirstButtonReturn) return false + $('').writeToFileAtomicallyEncodingError(abortMarker, true, $.NSUTF8StringEncoding, null) + return true +} + +const Controller = ObjC.registerSubclass({ + name: 'WorProgressController', + superclass: 'NSObject', + methods: { + 'tick:': { + types: ['void', ['id']], + implementation: function() { + try { + if (fm.fileExistsAtPath($(doneMarker))) { + app.stopModalWithCode($.NSOKButton) + return + } + updateDiskAlertStatus() + const content = readFile(progressFile) + if (content.length === 0) return + const lines = content.split('\n') + const stepLine = lastMatch(lines, 'STEP\t') + const subLine = lastMatch(lines, 'SUBSTEP\t') + const taskLine = lastMatch(lines, 'TASK\t') + const statusLine = lastMatch(lines, 'STATUS\t') + let taskText = '' + let taskPercentText = '' + let currentPercent = -1 + if (taskLine.length > 0) { + const taskParts = taskLine.split('\t') + const taskPct = parseInt(taskParts[1], 10) + const taskLabel = taskParts.slice(2).join('\t') + if (taskLabel.length > 0 && !isNaN(taskPct)) { + currentPercent = Math.max(0, Math.min(100, taskPct)) + taskText = taskLabel + taskPercentText = 'Sub-progress: ' + currentPercent + '%' + } + } + if (stepLine.length > 0) { + const parts = stepLine.split('\t') + const stepNum = parseInt(parts[1], 10) + const stepTotal = parseInt(parts[2], 10) + if (!isNaN(stepNum) && !isNaN(stepTotal) && stepTotal > 0) { + let within = 0 + if (subLine.length > 0) { + const parsed = parseInt(subLine.split('\t')[1], 10) + if (!isNaN(parsed)) within = Math.max(0, Math.min(100, parsed)) + } + if (currentPercent < 0) currentPercent = within + if (taskPercentText.length === 0 && currentPercent > 0) taskPercentText = 'Sub-progress: ' + currentPercent + '%' + //count in hundredths of a step so progress inside a step is visible too + bar.indeterminate = false + bar.minValue = 0 + bar.maxValue = stepTotal * 100 + bar.doubleValue = (stepNum - 1) * 100 + within + stepPercentLabel.stringValue = 'Overall: ' + Math.round(bar.doubleValue / bar.maxValue * 100) + '%' + taskBar.indeterminate = false + taskBar.minValue = 0 + taskBar.maxValue = 100 + taskBar.doubleValue = Math.max(0, Math.min(100, currentPercent)) + + let stepStr = 'Step ' + stepNum + ' of ' + stepTotal + stepLabel.stringValue = stepStr + } + phaseLabel.stringValue = parts.slice(3).join('\t') + } else if (subLine.length > 0) { + //work before the first numbered step, such as clearing the cache, still has its own percentage + const parsed = parseInt(subLine.split('\t')[1], 10) + if (!isNaN(parsed)) { + bar.indeterminate = false + bar.minValue = 0 + bar.maxValue = 100 + bar.doubleValue = Math.max(0, Math.min(100, parsed)) + stepPercentLabel.stringValue = 'Overall: ' + parsed + '%' + taskBar.indeterminate = false + taskBar.minValue = 0 + taskBar.maxValue = 100 + taskBar.doubleValue = Math.max(0, Math.min(100, parsed)) + stepLabel.stringValue = parsed + '%' + taskPercentLabel.stringValue = 'Sub-progress: ' + parsed + '%' + } + } + if (taskText.length > 0) { + detailLabel.stringValue = taskText + } else if (statusLine.length > 0) { + detailLabel.stringValue = statusLine.split('\t').slice(1).join('\t') + } + taskPercentLabel.stringValue = taskPercentText + } catch (e) {} + } + }, + //right-click Quit from the Dock/app-switcher sends terminate: to NSApp; since this window is + //hosted by osascript rather than a full NSApplicationMain run loop, that does not otherwise exit the process + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + //quitting mid-flash must go through the same confirmation and let the shell stop the installer + if (confirmAbort()) app.stopModalWithCode($.NSCancelButton) + } + }, + 'pumpEvents:': { + types: ['void', ['id']], + implementation: function() { + $.NSRunLoop.currentRunLoop.runModeBeforeDate($.NSDefaultRunLoopMode, $.NSDate.dateWithTimeIntervalSinceNow(0.01)) + } + }, + 'abortClicked:': { + types: ['void', ['id']], + implementation: function() { + if (confirmAbort()) app.stopModalWithCode($.NSCancelButton) + } + }, + //the close box must go through the same confirmation, so veto it unless the user means it + 'windowShouldClose:': { + types: ['bool', ['id']], + implementation: function() { + if (!confirmAbort()) return false + app.stopModalWithCode($.NSCancelButton) + return true + } + }, + //clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never come back + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + }, + 'applicationShouldTerminate:': { + types: ['NSUInteger', ['id']], + implementation: function() { + if (confirmAbort()) app.stopModalWithCode($.NSCancelButton) + //NSTerminateCancel: never let AppKit kill this process while a flash is running + return 0 + } + } + } +}) +const controller = $.WorProgressController.alloc.init +app.setDelegate(controller) + +const width = 680 +const height = 330 +window = worMakeWindow({ width: width, height: height, title: windowTitle, delegate: controller }) + +const content = window.contentView +window.contentView = content + +const logoWidth = 56 +const logoHeight = 173 +const progressX = 96 +const progressWidth = width - progressX - 20 +const logoImage = $.NSImage.alloc.initWithContentsOfFile($(iconPath)) +if (!logoImage.isNil()) { + const logoView = $.NSImageView.alloc.initWithFrame($.NSMakeRect(20, height - 20 - logoHeight, logoWidth, logoHeight)) + logoView.image = logoImage + logoView.imageScaling = $.NSImageScaleProportionallyUpOrDown + content.addSubview(logoView) +} + +phaseLabel = $.NSTextField.labelWithString('Starting...') +phaseLabel.frame = $.NSMakeRect(progressX, 260, width - progressX - 140, 24) +phaseLabel.font = $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightBold) +content.addSubview(phaseLabel) + +stepLabel = $.NSTextField.labelWithString('') +stepLabel.frame = $.NSMakeRect(width - 120, 260, 100, 24) +stepLabel.font = $.NSFont.systemFontOfSizeWeight(12, $.NSFontWeightRegular) +stepLabel.alignment = $.NSTextAlignmentRight +content.addSubview(stepLabel) + +stepPercentLabel = $.NSTextField.labelWithString('') +stepPercentLabel.frame = $.NSMakeRect(width - 140, 207, 120, 16) +stepPercentLabel.font = $.NSFont.monospacedDigitSystemFontOfSizeWeight(11, $.NSFontWeightMedium) +stepPercentLabel.alignment = $.NSTextAlignmentRight +content.addSubview(stepPercentLabel) + +detailLabel = $.NSTextField.labelWithString('') +detailLabel.frame = $.NSMakeRect(progressX, 163, progressWidth - 170, 16) +detailLabel.font = $.NSFont.systemFontOfSizeWeight(11, $.NSFontWeightRegular) +content.addSubview(detailLabel) + +taskPercentLabel = $.NSTextField.labelWithString('') +taskPercentLabel.frame = $.NSMakeRect(width - 170, 163, 150, 16) +taskPercentLabel.font = $.NSFont.monospacedDigitSystemFontOfSizeWeight(11, $.NSFontWeightMedium) +taskPercentLabel.alignment = $.NSTextAlignmentRight +content.addSubview(taskPercentLabel) + +bar = $.NSProgressIndicator.alloc.initWithFrame($.NSMakeRect(progressX, 185, progressWidth, 20)) +bar.indeterminate = true +bar.startAnimation(null) +content.addSubview(bar) + +taskBar = $.NSProgressIndicator.alloc.initWithFrame($.NSMakeRect(progressX, 142, progressWidth, 10)) +taskBar.indeterminate = false +taskBar.minValue = 0 +taskBar.maxValue = 100 +taskBar.doubleValue = 0 +content.addSubview(taskBar) + +diskAlertLabel = $.NSTextField.wrappingLabelWithString('Automatic Ignore is waiting for disk preparation.') +diskAlertLabel.frame = $.NSMakeRect(20, 106, width - 40, 30) +diskAlertLabel.font = $.NSFont.systemFontOfSizeWeight(10, $.NSFontWeightRegular) +diskAlertLabel.textColor = $.NSColor.secondaryLabelColor +content.addSubview(diskAlertLabel) + +const noteLabel = $.NSTextField.labelWithString('This window will close automatically when the process finishes.') +noteLabel.frame = $.NSMakeRect(20, 82, width - 40, 20) +noteLabel.font = $.NSFont.systemFontOfSizeWeight(11, $.NSFontWeightRegular) +//1, not $.NSTextAlignmentCenter: that constant bridges as 2, which this runtime draws right-aligned +noteLabel.alignment = 1 +content.addSubview(noteLabel) + +const abortButton = $.NSButton.buttonWithTitleTargetAction('Abort', controller, 'abortClicked:') +abortButton.bezelStyle = $.NSBezelStyleRounded +abortButton.sizeToFit +const abortWidth = Math.max(96, abortButton.frame.size.width) +abortButton.frame = $.NSMakeRect(Math.round((width - abortWidth) / 2), 22, abortWidth, 32) +content.addSubview(abortButton) + +window.makeKeyAndOrderFront(null) +app.activateIgnoringOtherApps(true) + +const timer = $.NSTimer.scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeats(0.4, controller, 'tick:', undefined, true) +worInstallWindowHandlers(controller) +app.runModalForWindow(window) +timer.invalidate +window.orderOut(null) +JXA +)" + + resume_at_flash=0 + while true; do + abort_marker="$(mktemp -u)" + + gui_start_installer + + wor_osascript -l JavaScript - "$progress_file" "$done_marker" "$WOR_ICON_PATH" "$WOR_APP_TITLE" "$abort_marker" "$WOR_WINDOW_TITLE" "$disk_alert_status" "$disk_alert_done" <<<"$progress_jxa" >/dev/null 2>&1 + + #Command-Q or a crashed/killed osascript can bypass the JXA abort handler. Never leave the flash unattended. + [ -f "$done_marker" ] || touch "$abort_marker" + + if [ -e "$abort_marker" ];then + status "Aborting at your request" + #most of the work runs under sudo, so the tree has to come down with the credential we already hold + kill_process_tree "$installer_pid" + wait "$installer_pid" 2>/dev/null + gui_stop_disk_alert_handler + rm -f "$progress_file" "$done_marker" "$abort_marker" "$auth_marker" "$error_marker" + saved_log="$(gui_save_failure_log)" + wor_show_result_notification failure + wor_osascript -l JavaScript - "Flashing was stopped before it finished. + +$DEVICE is now in an unusable state and has to be flashed again before it can boot. + + Full log: $saved_log" "$WOR_ICON_PATH" "$WOR_APP_TITLE" '' "$WOR_WINDOW_TITLE" '' <<<"$completion_jxa" >/dev/null 2>&1 + exit 1 + fi + + #the window can disappear while the flash is still going; wait for the real status instead of + #assuming failure, which would report a bogus error and leave the flash running unattended + wait "$installer_pid" 2>/dev/null + gui_stop_disk_alert_handler + + installer_status="$(cat "$done_marker" 2>/dev/null)" + [ -z "$installer_status" ] && installer_status=1 + rm -f "$progress_file" "$done_marker" "$abort_marker" "$auth_marker" + + privacy_guidance='' + privacy_settings_url='' + if [ "$installer_status" == 0 ];then + rm -f "$output_log" "$error_marker" + completion_text="Process completed successfully. + + It is now safe to remove your USB drive." + [ -z "$disk_alert_warning" ] || completion_text="$completion_text"$'\n\n'"$disk_alert_warning" + else + #keep the log on failure; the dialog only shows a tail, and the GUI has no terminal to fall back on + saved_log="$(gui_save_failure_log)" + #installer writes the error_marker before showing its own error dialog; if it exists, skip the completion dialog + if installer_showed_own_error ;then + rm -f "$error_marker" + exit "$installer_status" + fi + #canceling the password dialog, or mistyping it three times, fails before the destructive script + #ever runs; say so plainly instead of the generic "stopped unexpectedly" wording, which reads like + #a real crash, and offer to try the password again rather than forcing a full app restart + password_retry_reason='' + if grep -qF 'Administrator authentication was canceled or unavailable' "$saved_log" 2>/dev/null ;then + password_retry_reason='Flashing did not start: administrator access was canceled or the password dialog closed before a password was entered.' + elif grep -qF 'incorrect password attempts' "$saved_log" 2>/dev/null ;then + password_retry_reason='Flashing did not start: the administrator password was entered incorrectly too many times.' + fi + if [ -n "$password_retry_reason" ];then + password_retry_choice="$(macos_choose '' "$password_retry_reason + +No changes have been made to $DEVICE yet. Trying again picks up at the password step and keeps the files already prepared. + + Full log: $saved_log" retry Abort '' '' '' 'Try Again')" || password_retry_choice=abort + if [ "$password_retry_choice" == retry ];then + #the downloads and extraction finished before the password was ever asked for + resume_at_flash=1 + continue + fi + exit "$installer_status" + fi + if grep -qF 'macOS denied removable-volume access' "$saved_log" 2>/dev/null ;then + privacy_settings_url='x-apple.systempreferences:com.apple.preference.security?Privacy_FilesAndFolders' + privacy_guidance="Click Open Settings, then in Files and Folders enable Removable Volumes for bash when it is listed. If macOS does not offer that narrower permission, open Full Disk Access, click +, press Shift-Command-G, enter /bin/bash, click Open, and enable its toggle. Then quit WoR-Flasher completely and try again." + elif grep -qF 'Full Disk Access' "$saved_log" 2>/dev/null || grep -qF 'Operation not permitted' "$saved_log" 2>/dev/null ;then + privacy_settings_url='x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles' + privacy_guidance="Click Open Settings, then enable Full Disk Access for the exact WoR-Flasher.app copy you launched. If it is not listed, click + and add that app. If this still fails, also enable the launcher app you used, such as Visual Studio Code.app, Terminal.app, or iTerm.app. Then quit WoR-Flasher completely and try again." + fi + [ -z "$privacy_guidance" ] || privacy_guidance="$privacy_guidance + + " + completion_text="The Windows on Raspberry script stopped unexpectedly (exit code $installer_status). + + $privacy_guidance$(gui_log_tail "$saved_log") + +Full log: $saved_log" + fi + completion_image='' + [ "$installer_status" == 0 ] && completion_image="$WOR_ASSETS_DIR/next-steps.png" + #posted before the window opens, so it lands while the app is still in the background + [ "$installer_status" == 0 ] && wor_show_result_notification success || wor_show_result_notification failure + wor_osascript -l JavaScript - "$completion_text" "$WOR_ICON_PATH" "$WOR_APP_TITLE" "$completion_image" "$WOR_WINDOW_TITLE" "$([ "${PLAY_SOUND:-1}" == 1 ] && wor_completion_sound)" "$privacy_settings_url" <<<"$completion_jxa" >/dev/null 2>&1 + exit "$installer_status" + done +} + +if is_macos ;then + command -v osascript >/dev/null 2>&1 || error "Cannot present graphical interface: osascript is unavailable on this macOS host. Cannot continue." + setup || exit 1 + announcement_choice="$(macos_show_announcement)" || exit 0 + macos_start_cli + exit $? fi -#this script and cli-based install-wor.sh should be in same directory. -cli_script="$DIRECTORY/install-wor.sh" -if [ ! -f "$cli_script" ];then - error "No script found named install-wor.sh\nBoth scripts must be in the same directory." +if is_wsl ;then + error "Cannot present graphical interface: WoR-Flasher does not support WSL. +WSL cannot access USB drives directly, and the drives it does list are WSL's own virtual disks. +On Windows, use the official Windows on Raspberry Imager instead: https://worproject.com/downloads" +fi + +if [ "$HOST_OS" != Linux ];then + if [ "${OS:-}" == "Windows_NT" ] || [[ "$HOST_OS" =~ MINGW|MSYS|CYGWIN|Windows ]];then + error "Cannot present graphical interface: WoR-Flasher does not support Windows hosts. On Windows, use the official Windows on Raspberry Imager instead: https://worproject.com/downloads" + else + error "Cannot present graphical interface: WoR-Flasher supports Linux and macOS hosts only. This host is $HOST_OS." + fi +fi + +if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ];then + error "Cannot present graphical interface: No active graphical display session (DISPLAY or WAYLAND_DISPLAY is unset). Cannot continue." fi -#source the script to acquire necessary functions -source "$cli_script" source #by sourcing, this script checks for and applies updates. #run safety checks and install packages setup || exit 1 -#this array stores flags that are used in all yad windows - saves on the typing and makes it easy to change an attribute on all dialogs from one place. -yadflags=(--center --width=400 --height=250 --window-icon="$DIRECTORY/logo.png" --title="Windows on Raspberry" --separator='\n') - -#display BVM announcement -yad "${yadflags[@]}" --buttons-layout=spread \ - --image="$DIRECTORY/announcement.png" \ - --button='Proceed with WoR-Flasher':1 \ - --button='Check out the BVM project':0 -if [ "$?" == 0 ];then - open_url 'https://github.com/Botspot/bvm' - exit 0 +if ! command -v yad >/dev/null 2>&1 ;then + error "Cannot present graphical interface: 'yad' is missing and could not be installed. Cannot continue." fi -{ #choose destination RPi model and windows build ID -if [ -z "$RPI_MODEL" ] || [ -z "$BID" ];then - output="$(yad "${yadflags[@]}" --height=0 --form --columns=2 \ - --image="$DIRECTORY/logo-full.png" \ - --text=$'Welcome to Windows on Raspberry!\nThis wizard will help you easily install the full desktop version of Windows on your Raspberry Pi computer.' \ - --field="Install":CB "Windows 11!Windows 10!More options" \ - --field="on a":CB "Pi5!Pi4/Pi400!Pi3/Pi2_v1.2" \ - --button='Next':0)" +ensure_linux_desktop_identity() { #Install a desktop identity so GNOME maps yad windows to the WoR-Flasher icon. + is_macos && return 0 + [ -n "${HOME:-}" ] || return 0 + [ -f "$WOR_LOGO_PATH" ] || return 0 + local app_dir desktop_file exec_path icon_path + app_dir="$HOME/.local/share/applications" + desktop_file="$app_dir/wor-flasher.desktop" + mkdir -p "$app_dir" 2>/dev/null || return 0 + exec_path="$(printf '%s' "$DIRECTORY/install-wor-gui.sh" | sed 's/\\/\\\\/g; s/"/\\"/g')" + icon_path="$(printf '%s' "$WOR_LOGO_PATH" | sed 's/\\/\\\\/g; s/"/\\"/g')" + cat > "$desktop_file" </dev/null || return 0 +[Desktop Entry] +Type=Application +Name=$WOR_APP_TITLE +Exec="$exec_path" +Icon=$icon_path +StartupWMClass=$WOR_ICON_NAME +Terminal=false +Categories=Utility; +DESKTOP +} + +linux_no_device_message() { #Output: explanation when lsblk sees devices but no safe target drive. + printf '%s\n\n%s\n%s\n%s' \ + 'No external writable target drive was found.' \ + 'WoR-Flasher hides loop/snap devices, the current boot disk, and anything that is not a writable whole disk.' \ + 'Connect a USB drive or SD card reader, then click Refresh.' \ + 'If you expected a drive to appear, run lsblk and confirm it is not the system disk.' +} + +linux_choose_one() { #Input: newline choices, prompt, default, optional Back label. Output: choice or Back. + local choices="$1" prompt="$2" default_choice="$3" back_label="${4:-}" choice result button rows='' + local buttons=(--button='Next':0) + [ -z "$back_label" ] || buttons=(--button="$back_label":1 "${buttons[@]}") + while IFS= read -r choice ;do + [ -n "$choice" ] || continue + if [ "$choice" == "$default_choice" ];then + rows+="TRUE\n${choice}\n" + else + rows+="FALSE\n${choice}\n" + fi + done <<< "$choices" + result="$(printf '%b' "$rows" | yad "${yadflags[@]}" --width="$(wor_yad_width 560)" --height="$(wor_yad_height 420)" \ + --list --radiolist --no-selection --no-headers --column=selected:CHK --column=choice \ + --print-column=2 --text="$prompt" "${buttons[@]}")" button=$? - [ $button != 0 ] && exit 0 + if [ "$button" == 0 ];then + [ -n "$result" ] || return 1 + printf '%s\n' "$result" + elif [ "$button" == 1 ] && [ -n "$back_label" ];then + printf 'Back\n' + else + return 1 + fi +} + +ensure_linux_desktop_identity - WINDOWS_VER="$(echo "$output" | sed -n 1p)" - RPI_MODEL="$(echo "$output" | sed -n 2p | sed 's+Pi5+5+g' | sed 's+Pi4/Pi400+4+g' | sed 's+Pi3/Pi2_v1.2+3+g')" +#this array stores flags that are used in all yad windows - saves on the typing and makes it easy to change an attribute on all dialogs from one place. +#--class sets the window's WM_CLASS so the taskbar/Alt-Tab switcher shows WoR-Flasher instead of +#the generic "yad" process name +wor_init_yad_flags +announcement_image="$(wor_yad_image_for_screen "$WOR_ASSETS_DIR/partnership.png" "$WOR_LOGO_PATH" 880 740)" + +#display partnership announcement +#match the macOS announcement composition: the full banner sits above readable centered copy rather +#than consuming almost the whole width and squeezing the text into a one-word column beside it. +#--image must precede --form (as it does for the overview.png dialog below) or yad packs the image +#beside the field column instead of above it, regardless of --image-on-top. +announcement_status=0 +yad "${yadflags[@]}" --width="$(wor_yad_width 840)" --height="$(wor_yad_height 720)" --center \ + --image="$announcement_image" --image-on-top --text-align=center \ + --form --align=center --buttons-layout=center --timeout="$WOR_ANNOUNCEMENT_TIMEOUT" --timeout-indicator=bottom \ + --field=$'Blackout Secure is proud to partner with Botspot and the Windows on R community, carrying WoR-Flasher forward while preserving Botspot\'s original authorship and project direction.\n\nReport issues, share feedback, or contribute at Botspot/wor-flasher.\n\nSupport continued development by sponsoring Botspot or buying Blackout Secure a coffee on GitHub.':LBL '' \ + --button='Proceed with WoR-Flasher':0 >/dev/null || announcement_status=$? +#yad returns 70 when its timeout expires; that is the automatic Proceed action, not a quit. +[ "$announcement_status" == 0 ] || [ "$announcement_status" == 70 ] || exit 0 - case "$WINDOWS_VER" in +{ #choose destination RPi model and windows build ID +RPI_MODEL='' +BID='' +target_choice="$(yad "${yadflags[@]}" --width="$(wor_yad_width 620)" --height="$(wor_yad_height 260)" \ + --text='Choose Windows and Raspberry Pi target' \ + --form --align=center --buttons-layout=center \ + --field='Windows version:CB' 'Windows 11!Windows 10!More options' \ + --field='Raspberry Pi model:CB' 'Raspberry Pi 5!Raspberry Pi 4 / Pi 400!Raspberry Pi 3 / Pi 2 v1.2' \ + --button='Cancel':1 --button='Next':0)" +button=$? +[ "$button" == 0 ] || exit 0 +WINDOWS_VER="$(printf '%s\n' "$target_choice" | sed -n '1p')" +rpi_choice="$(printf '%s\n' "$target_choice" | sed -n '2p')" +case "$rpi_choice" in + 'Raspberry Pi 5') RPI_MODEL=5 ;; + 'Raspberry Pi 4 / Pi 400') RPI_MODEL=4 ;; + 'Raspberry Pi 3 / Pi 2 v1.2') RPI_MODEL=3 ;; + *) error "Unrecognized Raspberry Pi selection '$rpi_choice'" ;; +esac + +case "$WINDOWS_VER" in 'Windows 11' | 'Windows 10') loading_dialog "Finding best $WINDOWS_VER image version..." & loader_pid=$! - trap "kill $loader_pid 2>/dev/null" EXIT + trap stop_loader EXIT list_bids 10 >/dev/null #set $versions globally so it is not downloaded twice if [ "$WINDOWS_VER" == 'Windows 11' ];then @@ -105,7 +2504,7 @@ if [ -z "$RPI_MODEL" ] || [ -z "$BID" ];then BID="$(get_bid 10)" || exit 1 fi - kill $loader_pid 2>/dev/null + stop_loader ;; 'More options') @@ -115,7 +2514,7 @@ if [ -z "$RPI_MODEL" ] || [ -z "$BID" ];then while [ -z "$BID" ];do reply="$(echo -e "FALSE\nChoose an exact Windows version to download\nenter exact FALSE\nUse a Windows ISO file\nuse iso -FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "${yadflags[@]}" --width=420 \ +FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "${yadflags[@]}" --width="$(wor_yad_width 420)" \ --list --radiolist --column=chk:CHK --column=human --column=script:HD --no-headers --print-column=3 --no-selection \ --text=$'More options' \ --button='Next':0)" @@ -127,7 +2526,7 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ list_bids 10 >/dev/null #set $versions globally so it is not downloaded twice while [ -z "$BID" ];do BID="$(echo -n "$(list_bids_supported 11 | sed 's/^/Windows 11 /g' - list_bids_supported 10 | sed 's/^/Windows 10 /g')" | sed 's/^/FALSE\n/g' | yad "${yadflags[@]}" --width=420 \ + list_bids_supported 10 | sed 's/^/Windows 10 /g')" | sed 's/^/FALSE\n/g' | yad "${yadflags[@]}" --width="$(wor_yad_width 420)" \ --list --radiolist --column=chk:CHK --column=human --no-headers --print-column=2 --no-selection \ --text=$'Choose version of Windows:' \ --button='Next':0)" @@ -140,7 +2539,7 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ break ;; 'use iso') - SOURCE_FILE="$(yad "${yadflags[@]}" --width=420 \ + SOURCE_FILE="$(yad "${yadflags[@]}" --width="$(wor_yad_width 420)" \ --file --file-filter "ISO disk images | *.ISO *.iso" \ --text=$'Import ISO file\nMust be an ARM64 version of Windows from uupdump.net' \ --button="Cancel":1 --button="OK":0)" @@ -148,18 +2547,12 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ #verify ISO file if [ -z "$SOURCE_FILE" ];then break #exit ISO file menu - elif [ ! -f "$SOURCE_FILE" ];then - yad "${yadflags[@]}" --text="This file does not exist. Check spelling and try again." - SOURCE_FILE='' - elif [[ "$SOURCE_FILE" != *'.ISO' ]] && [[ "$SOURCE_FILE" != *'.iso' ]];then - yad "${yadflags[@]}" --text="This file does not have a .ISO file extension." - SOURCE_FILE='' - elif [ "$(get_file_size "$SOURCE_FILE")" -lt $((3*1024*1024*1024)) ];then - yad "${yadflags[@]}" --text="This file is smaller than 3GB and is probably incomplete." + elif ! iso_problem="$(validate_iso_file "$SOURCE_FILE")" ;then + yad "${yadflags[@]}" --text="$iso_problem" SOURCE_FILE='' else #ISO file looks good #Infer Build ID based on filename of ISO - BID="$(basename "$SOURCE_FILE" | tr '_ -' '\n' | grep -E -m 1 '^[0-9]{5}')" + BID="$(bid_from_iso_name "$SOURCE_FILE")" if [ -z "$BID" ];then BID="$(yad --form --field= '' "${yadflags[@]}" \ --text='To store files from this ISO, this script needs to know the Windows build number of this ISO.\nPlease enter it now: (example: '"$EXAMPLE_BID"')' \ @@ -167,14 +2560,14 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ [ -z "$BID" ] && error "Cannot proceed without a build number for your ISO file." fi #Infer language based on filename of ISO - WIN_LANG="$(basename "$SOURCE_FILE" | tr '_ ' '\n' | grep -io -m 1 "$(list_langs | awk -F: '{print $1}' | tr '\n' ';' | sed 's/;/\\|/g' | sed 's/\\|$/\n/g')" | tr '[A-Z]' '[a-z]')" + WIN_LANG="$(lang_from_iso_name "$SOURCE_FILE")" if [ -z "$WIN_LANG" ];then WIN_LANG="$(yad --form --field= '' "${yadflags[@]}" \ --text='To store files from this ISO, this script needs to know the language of this Windows ISO.\nPlease enter it now: (example: en-us)' \ --button="OK":0)" if [ -z "$WIN_LANG" ];then error "Cannot proceed without a language for your ISO file." - elif ! list_langs | awk -F: '{print $1}' | grep -q "$WIN_LANG" ;then + elif ! is_known_win_lang "$WIN_LANG" ;then error "Language code was not found in the list!\n$(list_langs | awk '{print $1}' | tr '\n' ' ')" fi fi @@ -186,23 +2579,23 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ #folders in DL_DIR named winfiles_from_iso__ while true;do list='' - existing_winfiles="$(find "$DL_DIR" -maxdepth 2 -type f -name 'alldone' | grep -o "/winfiles_from_iso.*/\|/winfiles.*/" | tr -d / | sort -r -n)" + existing_winfiles="$(list_cached_winfiles)" echo "$existing_winfiles" for folder in $existing_winfiles ;do - BID="$(echo "$folder" | sed 's/^winfiles_from_iso_//g' | sed 's/^winfiles_//g' | awk -F_ '{print $1}')" - WIN_LANG="$(echo "$folder" | sed 's/^winfiles_from_iso_//g' | sed 's/^winfiles_//g' | awk -F_ '{print $2}')" + BID="$(bid_from_winfiles_dir "$folder")" + WIN_LANG="$(lang_from_winfiles_dir "$folder")" list+="FALSE\n$(get_os_name "$BID") $WIN_LANG\n${folder}\n" num_opts=$((num_opts+1)) done unset BID WIN_LANG #Avoid leaving these variables set from the loop - folder="$(echo -ne "$list" | yad "${yadflags[@]}" --height=320 \ + folder="$(echo -ne "$list" | yad "${yadflags[@]}" --height="$(wor_yad_height 320)" \ --list --radiolist --column=chk:CHK --column=human --column=script:HD --no-headers --print-column=3 --no-selection \ - --text=$'Choose cached version\nIf the list is empty, please use the same working directory (DL_DIR) you used last time.\nDL_DIR: '"$DL_DIR"'' \ - --button='Change DL DIR':2 \ + --text=$'Choose Cached Windows Files\nIf the list is empty, choose the download folder used by the previous run.\nCurrent download folder: '"$DL_DIR"'' \ + --button='Change Download Folder':2 \ --button='Next':0)" button=$? @@ -210,8 +2603,8 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ 0) #Next if [ ! -z "$folder" ];then #A cached version of windows (winfiles folder) was selected; infer BID and WIN_LANG from it - BID="$(echo "$folder" | sed 's/^winfiles_from_iso_//g' | sed 's/^winfiles_//g' | awk -F_ '{print $1}')" - WIN_LANG="$(echo "$folder" | sed 's/^winfiles_from_iso_//g' | sed 's/^winfiles_//g' | awk -F_ '{print $2}')" + BID="$(bid_from_winfiles_dir "$folder")" + WIN_LANG="$(lang_from_winfiles_dir "$folder")" #DL_DIR cannot be changed later on - it is being relied upon for winfiles break @@ -222,8 +2615,8 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ ;; 2) #change DL_DIR DL_DIR="$(yad "${yadflags[@]}" --file --directory --mime-filter="Directories | inode/directory" \ - --width=500 --height=400 --title="Choose DL_DIR" \ - --text=$'Choose directory for everything to be downloaded.\nIn this case you should select the directory where everything was downloaded the last time you ran WoR-Flasher.' \ + --width="$(wor_yad_width 500)" --height="$(wor_yad_height 400)" --title="Choose Download Folder" \ + --text=$'Choose where WoR-Flasher stores downloaded and extracted Windows files.\nFor cached Windows files, select the folder used by the previous run.' \ --button="Cancel":1 --button="OK":0 \ || echo "$DL_DIR")" #This ^^^^^^^^^^^ preserves the current value of DL_DIR if anything other than OK is clicked @@ -241,32 +2634,13 @@ FALSE\nUse a cached version of Windows from a previous run\nuse cached" | yad "$ error "Unrecognized user-selected WINDOWS_VER '$WINDOWS_VER'" ;; esac -fi echo "BID: $BID RPI_MODEL: $RPI_MODEL" } { #choose language if [ -z "$WIN_LANG" ];then - - #move 'en-*' languages to top of list, and of those put en-us at the very top and make it preselected - LANG_LIST="$(list_langs | grep '^en-us' -list_langs | grep '^en-*' | grep -v '^en-us' -list_langs | grep -v '^en-')" - - while true; do - WIN_LANG="$(echo "$LANG_LIST" | sed 's/^/FALSE:/g' | tr ':' '\n' | sed -e '0,/FALSE/ s/FALSE/TRUE/' | yad "${yadflags[@]}" \ - --list --radiolist --column=chk:CHK --column=short --column=long --no-headers --print-column=2 --no-selection \ - --text=$'Language\nChoose language for Windows:' \ - --button='Next':0)" - button=$? - [ $button != 0 ] && exit 1 - - if echo "$LANG_LIST" | grep -q "^$WIN_LANG": ;then - #if selected language matches line in language list - break - fi - done + WIN_LANG="$(default_win_lang)" fi echo "WIN_LANG: $WIN_LANG" } @@ -276,7 +2650,8 @@ if [ -z "$DEVICE" ];then while [ -z "$DEVICE" ] || [ ! -b "$DEVICE" ];do IFS=$'\n' DEV_LIST='' - for device in $(lsblk -I 8,179,259 -dno PATH | grep -v loop | grep -vx "$ROOT_DEV") ;do + device_buttons=(--button="Refresh!!Reload the list of connected drives to detect new ones":2 --button='Next':0) + for device in $(list_dev_paths) ;do [ "$(get_size_raw "$device")" -le 0 ] && continue DEV_LIST="FALSE ${device} @@ -286,10 +2661,17 @@ $(get_device_name "$device") $DEV_LIST" done - DEVICE="$(echo -n "$DEV_LIST" | sed -e '0,/FALSE/ s/FALSE/TRUE/' | yad "${yadflags[@]}" --text='Choose device to flash:' --width=420 \ + if [ -z "$DEV_LIST" ];then + device_prompt="$(linux_no_device_message)" + device_buttons=(--button='Cancel':1 --button="Refresh!!Reload the list of connected drives to detect new ones":2) + else + device_prompt='Choose device to flash:' + fi + + DEVICE="$(echo -n "$DEV_LIST" | sed -e '0,/FALSE/ s/FALSE/TRUE/' | yad "${yadflags[@]}" --text="$device_prompt" --width="$(wor_yad_width 520)" \ --list --radiolist --no-selection --no-headers --column=chk:CHK --column=echoname:HD --column=name --column=size --column=pretty-name \ --print-column=2 --tooltip-column=3 \ - --button="Refresh!!Reload the list of connected drives to detect new ones":2 --button='Next':0)" + "${device_buttons[@]}")" button=$? if [ $button == 0 ];then #OK @@ -305,6 +2687,8 @@ $DEV_LIST" elif [ -z "$(lsblk -no PATH "$DEVICE")" ];then error "Invalid value for DEVICE: $DEVICE is not a valid drive!" fi +#same guard the macOS wizard applies, so a caller-supplied DEVICE cannot be the host's own boot disk +is_safe_target_device "$DEVICE" || error "Refusing to overwrite $DEVICE, which is this system's boot drive." echo "DEVICE: $DEVICE" } @@ -317,7 +2701,7 @@ if [ -z "$CAN_INSTALL_ON_SAME_DRIVE" ] && [ "$device_capability" == recovery ];t CAN_INSTALL_ON_SAME_DRIVE=0 elif [ -z "$CAN_INSTALL_ON_SAME_DRIVE" ];then while [ -z "$CAN_INSTALL_ON_SAME_DRIVE" ];do - install_mode="$(echo -e "TRUE\ninstall\nInstallation drive\nInstall Windows onto this 25 GB+ drive\nFALSE\nrecovery\nRecovery drive\nInstall Windows onto another >16 GB drive" | yad "${yadflags[@]}" --width=520 \ + install_mode="$(echo -e "TRUE\ninstall\nInstallation drive\nInstall Windows onto this 25 GB+ drive\nFALSE\nrecovery\nRecovery drive\nInstall Windows onto another >16 GB drive" | yad "${yadflags[@]}" --width="$(wor_yad_width 520)" \ --list --radiolist --column=chk:CHK --column=value:HD --column=Mode --column=Description --no-headers --print-column=2 --no-selection \ --text=$'Installation mode\nThis drive is large enough for either mode. Choose what you want it to do:' \ --button='Next':0)" @@ -346,28 +2730,28 @@ if [ ! -f "${DL_DIR}/winfiles_from_iso_${BID}_${WIN_LANG}/alldone" ] && [ ! -f " if [ -d "/lib/modules/$(uname -r)" ];then #tooltip text of 'Use RAM' button will explain that More RAM app from Pi-Apps will be installed, assuming it is not already installed. if [ -f /usr/bin/zram.sh ] && [ -d /zram ];then - tooltip='Will set DL_DIR to the /zram folder - this folder was set up when you installed More RAM from Pi-Apps.' + tooltip='Will use the /zram folder that was set up when you installed More RAM from Pi-Apps.' elif [ -f /usr/local/bin/pi-apps ];then - tooltip='Will install More RAM from Pi-Apps and then set DL_DIR to the new ramdisk at /zram.' + tooltip='Will install More RAM from Pi-Apps and then use the new ramdisk at /zram.' else - tooltip='Will setup a RAM-compression tool from Pi-Apps and then set DL_DIR to the new ramdisk at /zram. Please note that Pi-Apps itself will not be installed.' + tooltip='Will set up a RAM-compression tool from Pi-Apps and then use the new ramdisk at /zram. Please note that Pi-Apps itself will not be installed.' fi - yad "${yadflags[@]}" --width=500 --form --field="About 4.2GB of files need to be downloaded to system storage before flashing can begin. + yad "${yadflags[@]}" --width="$(wor_yad_width 500)" --form --field="About 4.2GB of files need to be downloaded to system storage before flashing can begin. But your system has $(echo "scale=1 ; $( awk '/MemTotal/ {print $2}' /proc/meminfo ) / 1048576 " | bc )GB of RAM. Everything can be downloaded to RAM if you prefer. Choose this if: - You don't have enough space in $HOME - You want your system storage to last as long as possible - You don't plan to use WoR-Flasher often:LBL" \ - --image="$DIRECTORY/ram.png" --image-on-top \ - --button="Use ${DL_DIR}":2 \ + --image="$WOR_ASSETS_DIR/ram.png" --image-on-top \ + --button="Use download folder!!${DL_DIR}":2 \ --button="Use RAM!!${tooltip}":0 button=$? if [ "$button" == 0 ];then status "User chose to download everything to RAM." echo "For best results, please close all other programs. (especially web browsers and games)" - yad "${yadflags[@]}" --width=500 --image="$DIRECTORY/ram.png" --image-on-top \ + yad "${yadflags[@]}" --width="$(wor_yad_width 500)" --image="$WOR_ASSETS_DIR/ram.png" --image-on-top \ --form --field="OK! Will download everything to RAM. For best results, please close all other programs. (especially web browsers and games):LBL" \ --button='OK':0 >/dev/null @@ -376,7 +2760,7 @@ Choose this if: #install More RAM loading_dialog "Setting up RAM..." & loader_pid=$! - trap "kill $loader_pid 2>/dev/null" EXIT + trap stop_loader EXIT if [ -f "$HOME/pi-apps/manage" ];then #if Pi-Apps installed to default location, install More RAM from there @@ -384,7 +2768,7 @@ Choose this if: exitcode=$? elif [ -f /usr/local/bin/pi-apps ] && [ -f "$(dirname "$(cat /usr/local/bin/pi-apps | sed -n 2p)")/manage" ];then #if Pi-Apps installed to another folder, install More RAM from there - "$(dirname "$(cat /usr/local/bin/pi-apps | sed -n 2p)")/manage" + "$(dirname "$(cat /usr/local/bin/pi-apps | sed -n 2p)")/manage" install 'More RAM' exitcode=$? else #Pi-Apps is not installed, so run More RAM script straight from the pi-apps github repo @@ -397,7 +2781,7 @@ Choose this if: fi fi #installation complete, so close pulsating progress bar dialog - kill $loader_pid 2>/dev/null + stop_loader #edge case: if user had installed More RAM before and disabled the /zram folder, enable it now if [ "$exitcode" == 0 ] && [ ! -d /zram ];then @@ -434,64 +2818,17 @@ fi } #if no user-supplied CONFIG_TXT variable, set it to initial value for yad to change later -if [ -z "$CONFIG_TXT" ];then - if [ "$RPI_MODEL" == 3 ];then - - CONFIG_TXT=" - -# don't change anything below this point # -arm_64bit=1 -disable_commandline_tags=2 -disable_overscan=1 -enable_uart=1 -uart_2ndstage=1 -armstub=RPI_EFI.fd -device_tree_address=0x1f0000 -device_tree_end=0x200000 -# Uncomment if you have trouble with the UART console during boot -#core_freq=250" - - elif [ "$RPI_MODEL" == 4 ];then - CONFIG_TXT=" - -# don't change anything below this point # -arm_64bit=1 -arm_boost=1 -enable_uart=1 -uart_2ndstage=1 -enable_gic=1 -armstub=RPI_EFI.fd -disable_commandline_tags=1 -disable_overscan=1 -device_tree_address=0x1f0000 -device_tree_end=0x200000 -dtoverlay=miniuart-bt -dtoverlay=upstream-pi4" - - elif [ "$RPI_MODEL" == 5 ];then - CONFIG_TXT=" - -# don't change anything below this point # -armstub=RPI_EFI.fd -device_tree_address=0x1f0000 -device_tree_end=0x210000 -pciex4_reset=0. -framebuffer_depth=32 -disable_overscan=1 -usb_max_current_enable=1 -force_turbo=1" - fi -fi +set_default_config_txt { #confirmation dialog and edit config.txt -window_text="- Target drive: $DEVICE ($(lsblk -dno SIZE "$DEVICE" | tr -d ' ')B $(get_device_name "$DEVICE")) -- Installation mode: $(echo "$CAN_INSTALL_ON_SAME_DRIVE" | sed 's/1/Install Windows onto this drive/g' | sed 's/0/Recovery drive for another >16 GB drive/g') -- Target hardware: Raspberry Pi $RPI_MODEL -- Operating system: $(get_os_name "$BID" | sed "s/ build / ($WIN_LANG) arm64 build /g")" +window_text="$(settings_summary_markup) + +To continue, click Flash. To review or change these settings, click Advanced. To cancel, close this window." #by default, if a windows image exists, don't delete it to rebuild it rm_img=FALSE +existing_img_chk=() while true;do #repeat the Installation Overview window until Flash button clicked @@ -503,21 +2840,14 @@ while true;do #repeat the Installation Overview window until Flash button clicke deletion_warning_2="$deletion_warning Backup any files before it's too late!" fi - output="$(yad "${yadflags[@]}" --width=500 --height=400 --image="$DIRECTORY/overview.png" --image-on-top \ - --form --field="$window_text":LBL '' \ + yad "${yadflags[@]}" --width="$(wor_yad_width 720)" --height="$(wor_yad_height 700)" --image="$WOR_ASSETS_DIR/overview.png" --image-on-top \ + --form --scroll --field="$window_text":LBL '' \ "${existing_img_chk[@]}" \ - --field="Edit config.txt: Configuration reference":TXT "$CONFIG_TXT" \ --field="$deletion_warning":LBL '' \ --button='Advanced...'!!"More settings, intended for the advanced user or for troubleshooting":2 \ - --button='Flash'!!"$deletion_warning_2":0 - )" + --button='Flash'!!"$deletion_warning_2":0 >/dev/null button=$? - #remove first line from yad output - remove newline from label field - output="$(echo -e "$output" | tail -n +2)" - - CONFIG_TXT="$output" - if [ $button == 0 ];then #button: Flash break @@ -528,72 +2858,158 @@ while true;do #repeat the Installation Overview window until Flash button clicke while true;do #repeat the advanced options window until the DL_DIR is not changed, or until Cancel is clicked fields=() + config_edit_file="$(mktemp)" + printf '%s' "$CONFIG_TXT" > "$config_edit_file" + config_editor_title="$WOR_WINDOW_TITLE | config.txt" + config_editor_action="@yad --center --no-markup --width=$(wor_yad_width 700) --height=$(wor_yad_height 520) --title=$(printf '%q' "$config_editor_title") --window-icon=$(printf '%q' "$WOR_LOGO_PATH") --class=$(printf '%q' "$WOR_ICON_NAME") --text-info --editable --in-place --confirm-save='Save config.txt changes?' --filename=$(printf '%q' "$config_edit_file") --button=Close:0 >/dev/null" + uefi_pinned="$(uefi_pinned_version)" + #make entries for the customization toggles + #the engine ignores PI4_AUTO_DISABLE_3GB unless RPI_MODEL is 4; yad can't disable one field, so mark it and drop the value below + [ "$RPI_MODEL" == 4 ] && pi4_applicable=1 || pi4_applicable=0 + #yad renders markup, so an inapplicable row is italicised rather than greyed out + pi4_label="$(wor_pi4_label "$RPI_MODEL")" + [ "$pi4_applicable" == 1 ] || pi4_label="$pi4_label" + fields+=("--field=Windows setup:LBL" '') + oobe_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$(wor_yad_label "$(wor_advanced_label oobe "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL")" "$(wor_advanced_caution oobe)")":CHK "$(wor_yad_bool "$OOBE_NETWORK_BYPASS")") + pi4_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$pi4_label":CHK "$(wor_yad_bool "$([ "$pi4_applicable" == 1 ] && echo "$PI4_AUTO_DISABLE_3GB" || echo 0)")") + fields+=("--field=Firmware and drivers:LBL" '') + uefi_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$(wor_yad_label "$(wor_advanced_label uefi "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL")" "$(wor_advanced_caution uefi)")":CHK "$(wor_yad_bool "$UEFI_USE_LATEST")") + drivers_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$(wor_yad_label "$(wor_advanced_label drivers "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL")" "$(wor_advanced_caution drivers)")":CHK "$(wor_yad_bool "$DRIVERS_USE_LATEST")") + fields+=("--field=Validation:LBL" '') + verify_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$(wor_yad_label "$(wor_advanced_label verify "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL")" "$(wor_advanced_caution verify)")":CHK "$(wor_yad_bool "$SKIP_IMAGE_VERIFICATION")") + dryrun_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$(wor_advanced_label dryrun "$uefi_pinned" "$DRIVER_VER" "$RPI_MODEL"):CHK" "$(wor_yad_bool "$DRY_RUN")") + #in recovery mode this config.txt boots the installer media; WoR-PE writes the target drive's own copy + config_scope="$(wor_config_scope "$CAN_INSTALL_ON_SAME_DRIVE")" + fields+=("--field=Downloads:LBL" '') #make entry to change DL_DIR if [ -f "${DL_DIR}/winfiles_from_iso_${BID}_${WIN_LANG}/alldone" ];then - #lock DL_DIR if winfiles come from previously extracted ISO - changing it would lose these files and they cannot be replaced by the internet - fields+=("--field=Working directory: (DL DIR):RO" 'Cannot be changed') - else - fields+=("--field=Working directory: (DL DIR):DIR" "$DL_DIR") - fi - - #make entry for peinstaller - if [ -d "$DL_DIR/peinstaller" ];then - fields+=("--field=Check this box to re-download PE Installer":CHK 'FALSE') + working_dir_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Download folder:RO" 'Cannot be changed') else - fields+=("--field=Will download PE Installer":LBL '') + working_dir_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Download folder:DIR" "$DL_DIR") fi - fields+=("--field= $DL_DIR/peinstaller":LBL '') - - #make entry for driverpackage - if [ -d "$DL_DIR/driverpackage" ];then - fields+=("--field=Check this box to re-download RPi Drivers":CHK 'FALSE') - else - fields+=("--field=Will download RPi Drivers":LBL '') - fi - fields+=("--field= $DL_DIR/driverpackage":LBL '') - - #make entry for uefipackage - if [ -d "$DL_DIR/pi${RPI_MODEL}-uefipackage" ];then - fields+=("--field=Check this box to re-download UEFI package":CHK 'FALSE') - else - fields+=("--field=Will download UEFI package":LBL '') - fi - fields+=("--field= $DL_DIR/pi${RPI_MODEL}-uefipackage":LBL '') - - #display status of winfiles - if they will be downloaded or are ready to use if [ -f "${DL_DIR}/winfiles_${BID}_${WIN_LANG}/alldone" ];then - #already extracted - fields+=("--field=Windows files: Already extracted and ready to use.":LBL '') - fields+=("--field= ${DL_DIR}/winfiles_${BID}_${WIN_LANG}":LBL '') + windows_files_status='Already extracted and ready to use' elif [ -f "${DL_DIR}/winfiles_from_iso_${BID}_${WIN_LANG}/alldone" ];then - #already extracted - fields+=("--field=Windows files: Already extracted and ready to use.":LBL '') - fields+=("--field= ${DL_DIR}/winfiles_from_iso_${BID}_${WIN_LANG}":LBL '') - elif [ ! -z "$SOURCE_FILE" ];then - #will use ISO file - fields+=("--field=Windows files: Will be extracted from your ISO file.":LBL '') - fields+=("--field= ${SOURCE_FILE}":LBL '') + windows_files_status='Already extracted and ready to use' + elif [ -n "$SOURCE_FILE" ];then + windows_files_status='Will be extracted from the selected ISO' else - #ESD will be downloaded - fields+=("--field=Windows files: Will download and extract Windows ESD image":LBL '') - fields+=("--field= ${DL_DIR}/winfiles_${BID}_${WIN_LANG}":LBL '') + windows_files_status='Will download and extract the Windows image' fi - - #make entry for dry run - fields+=("--field=Skip flashing the device (DRY_RUN)":CHK "$(echo "$DRY_RUN" | sed 's/1/TRUE/g' | sed 's/0/FALSE/g')") - - output="$(yad "${yadflags[@]}" --width=500 --height=400 --image-on-top \ + windows_files_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Windows files:RO" "$windows_files_status") + #USE_CACHE has three values, so it needs a combo rather than a check box; the selected item comes first + case "$USE_CACHE" in + 0) cache_items='Re-download everything, ignoring the cache!Reuse cached files when they still match (recommended)!Trust the cache without checking it' ;; + 2) cache_items='Trust the cache without checking it!Re-download everything, ignoring the cache!Reuse cached files when they still match (recommended)' ;; + *) cache_items='Reuse cached files when they still match (recommended)!Re-download everything, ignoring the cache!Trust the cache without checking it' ;; + esac + cache_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Downloaded files":CB "$cache_items") + fields+=("--field=Notifications:LBL" '') + sound_items="" + curr_sound_item="" + other_sound_items="" + sel_sound="$(wor_completion_sound)" + while IFS=$'\t' read -r sound_value sound_label ;do + [ -z "$sound_value" ] && continue + if [ "$sound_value" == "$sel_sound" ];then curr_sound_item="$sound_label"; else [ -n "$other_sound_items" ] && other_sound_items+="!${sound_label}" || other_sound_items="${sound_label}"; fi + done < <(wor_sound_options) + if [ -n "$curr_sound_item" ] || [ -n "$other_sound_items" ];then + [ -n "$curr_sound_item" ] && sound_items="${curr_sound_item}!${other_sound_items}" || sound_items="${other_sound_items}" + play_sound_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Play a sound when the flash finishes":CHK "$(wor_yad_bool "${PLAY_SOUND:-1}")") + completion_sound_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Completion sound":CB "$sound_items") + notification_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Show a notification when the flash finishes":CHK "$(wor_yad_bool "${SHOW_NOTIFICATION:-1}")") + fi + fields+=("--field=Windows account:LBL" '') + account_checkbox_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Create an optional local Windows administrator account":CHK "$(wor_yad_bool "$WINDOWS_ACCOUNT_SETUP")") + account_username_field=$((${#fields[@]} / 2 + 1)) + account_username_value="$WINDOWS_ACCOUNT_USERNAME" + account_password_value="$WINDOWS_ACCOUNT_PASSWORD" + [ "$WINDOWS_ACCOUNT_SETUP" == 1 ] || account_username_value='@disabled@' + [ "$WINDOWS_ACCOUNT_SETUP" == 1 ] || account_password_value='@disabled@' + fields+=("--field=Windows username":TXT "$account_username_value") + account_password_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Windows password":H "$account_password_value") + fields+=("--field=Regional settings:LBL" '') + locale_checkbox_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Configure Windows keyboard and regional settings":CHK "$(wor_yad_bool "$WINDOWS_LOCALE_SETUP")") + locale_items="" + curr_locale_item="" + other_locale_items="" + while IFS=$'\t' read -r locale_code locale_label ;do + [ -z "$locale_code" ] && continue + entry="${locale_code}: ${locale_label:-$locale_code}" + if [ "$(printf '%s' "$locale_code" | tr '[:upper:]' '[:lower:]')" == "$(printf '%s' "$WINDOWS_LOCALE" | tr '[:upper:]' '[:lower:]')" ];then + curr_locale_item="$entry" + else + [ -n "$other_locale_items" ] && other_locale_items+="!${entry}" || other_locale_items="${entry}" + fi + done < <(list_windows_locale_options) + [ -n "$curr_locale_item" ] && locale_items="${curr_locale_item}!${other_locale_items}" || locale_items="${WINDOWS_LOCALE}: ${WINDOWS_LOCALE}!${other_locale_items}" + locale_value="$locale_items" + [ "$WINDOWS_LOCALE_SETUP" == 1 ] || locale_value='@disabled@' + locale_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Windows locale":CB "$locale_value") + lang_items="" + curr_item="" + other_items="" + while IFS=: read -r l_code l_name ;do + [ -z "$l_code" ] && continue + entry="${l_code}: ${l_name}" + if [ "$l_code" == "$WIN_LANG" ];then + curr_item="$entry" + else + [ -n "$other_items" ] && other_items+="!${entry}" || other_items="${entry}" + fi + done < <(list_langs_preferred) + [ -n "$curr_item" ] && lang_items="${curr_item}!${other_items}" || lang_items="${other_items}" + language_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=Choose Windows language":CB "$lang_items") + fields+=("--field=Raspberry Pi boot config:LBL" '') + config_button_value="$config_editor_action" + [ "$APPLY_CUSTOM_CONFIG_TXT" == 1 ] || config_button_value='@disabled@' + account_username_update="${account_username_field}:$(printf '%q' "$account_username_value")" + account_password_update="${account_password_field}:$(printf '%q' "$account_password_value")" + locale_update="${locale_field}:$(printf '%q' "$locale_value")" + config_checkbox_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=$(wor_config_txt_label "$config_scope") (recommended):CHK" "$(wor_yad_bool "$APPLY_CUSTOM_CONFIG_TXT")") + config_button_field=$((${#fields[@]} / 2 + 1)) + fields+=("--field=View / Edit config.txt:BTN" "$config_button_value") + changed_action="case \"\$1\" in + ${account_checkbox_field}) if [ \"\$2\" == TRUE ];then printf '%s\\n' \"${account_username_update}\" \"${account_password_update}\"; else printf \"${account_username_field}:@disabled@\\n${account_password_field}:@disabled@\\n\"; fi;; + ${locale_checkbox_field}) if [ \"\$2\" == TRUE ];then printf '%s\\n' \"${locale_update}\"; else printf \"${locale_field}:@disabled@\\n\"; fi;; + ${config_checkbox_field}) if [ \"\$2\" == TRUE ];then printf '%s\\n' \"${config_button_field}:${config_editor_action}\"; else printf '${config_button_field}:@disabled@\\n'; fi;; + esac" + + output="$(yad "${yadflags[@]}" --use-markup --changed-action="$changed_action" --width="$(wor_yad_width 720)" --height="$(wor_yad_height 720)" --image-on-top \ "${refresh_prompt[@]}" \ - --form \ + --text=$'Advanced Options\nChanges apply to this flash only.' \ + --form --scroll \ "${fields[@]}" \ - --button="Cancel":1 --button="OK":0 + --button="Back":1 --button="OK":0 )" button=$? + [ -f "$config_edit_file" ] && CONFIG_TXT="$(cat "$config_edit_file")" + rm -f "$config_edit_file" if [ "$button" == 0 ];then #everything in this if statement is skipped if Cancel is clicked - if [ ! -f "${DL_DIR}/winfiles_from_iso_${BID}_${WIN_LANG}/alldone" ] && [ "$DL_DIR" != "$(echo "$output" | sed -n 1p)" ];then + yad_field_value() { printf '%s\n' "$output" | sed -n "${1}p"; } + if [ ! -f "${DL_DIR}/winfiles_from_iso_${BID}_${WIN_LANG}/alldone" ] && [ "$DL_DIR" != "$(yad_field_value "$working_dir_field")" ];then #DL_DIR was changed - only honor the value if it is allowed to be changed - DL_DIR="$(echo "$output" | sed -n 1p)" + DL_DIR="$(yad_field_value "$working_dir_field")" echo "In the Advanced Options window, user changed DL_DIR to $DL_DIR" #explain to user why the Advanced Options window was refreshed when they clicked OK @@ -603,34 +3019,47 @@ while true;do #repeat the Installation Overview window until Flash button clicke else #if DL_DIR was not changed, then review the subsequent check-box values #peinstaller - if [ "$(echo "$output" | sed -n 2p)" == TRUE ];then - echo "User checked the box to delete $DL_DIR/peinstaller" - rm -rf "$DL_DIR/peinstaller" - fi - #driverpackage - if [ "$(echo "$output" | sed -n 4p)" == TRUE ];then - echo "User checked the box to delete $DL_DIR/driverpackage" - rm -rf "$DL_DIR/driverpackage" - fi - #uefipackage - if [ "$(echo "$output" | sed -n 6p)" == TRUE ];then - echo "User checked the box to delete $DL_DIR/pi${RPI_MODEL}-uefipackage" - rm -rf "$DL_DIR/pi${RPI_MODEL}-uefipackage" - fi - #windows image - if [ "$(echo "$output" | sed -n 8p)" == TRUE ];then - echo "User checked the box to delete $(echo "$DL_DIR"/uupdump/*ARM64*.ISO)" - rm -f "$DL_DIR"/uupdump/*ARM64*.ISO - rm_img=FALSE #This "Advanced..." dialog just deleted the windows image, so no need for the var to remain 'TRUE' - remove unnecessary output when removing twice - fi #DRY_RUN - if [ "$(echo "$output" | sed -n 10p)" == TRUE ] && [ "$DRY_RUN" == 0 ];then + if [ "$(yad_field_value "$dryrun_field")" == TRUE ] && [ "$DRY_RUN" == 0 ];then echo "User checked the box to set DRY_RUN=1" DRY_RUN=1 - elif [ "$(echo "$output" | sed -n 10p)" == FALSE ] && [ "$DRY_RUN" == 1 ];then + elif [ "$(yad_field_value "$dryrun_field")" == FALSE ] && [ "$DRY_RUN" == 1 ];then echo "User checked the box to set DRY_RUN=0" DRY_RUN=0 fi + #customization toggles + [ "$(yad_field_value "$oobe_field")" == TRUE ] && OOBE_NETWORK_BYPASS=1 || OOBE_NETWORK_BYPASS=0 + #keep the existing preference when the toggle wasn't applicable, so switching back to a Pi 4 doesn't lose it + if [ "$pi4_applicable" == 1 ];then + [ "$(yad_field_value "$pi4_field")" == TRUE ] && PI4_AUTO_DISABLE_3GB=1 || PI4_AUTO_DISABLE_3GB=0 + fi + [ "$(yad_field_value "$uefi_field")" == TRUE ] && UEFI_USE_LATEST=1 || UEFI_USE_LATEST=0 + [ "$(yad_field_value "$drivers_field")" == TRUE ] && DRIVERS_USE_LATEST=1 || DRIVERS_USE_LATEST=0 + [ "$(yad_field_value "$verify_field")" == TRUE ] && SKIP_IMAGE_VERIFICATION=1 || SKIP_IMAGE_VERIFICATION=0 + [ "$(yad_field_value "$config_checkbox_field")" == TRUE ] && APPLY_CUSTOM_CONFIG_TXT=1 || APPLY_CUSTOM_CONFIG_TXT=0 + case "$(yad_field_value "$cache_field")" in + 'Re-download everything'*) USE_CACHE=0 ;; + 'Trust the cache'*) USE_CACHE=2 ;; + 'Reuse cached files'*) USE_CACHE=1 ;; + esac + [ "$(yad_field_value "$account_checkbox_field")" == TRUE ] && WINDOWS_ACCOUNT_SETUP=1 || WINDOWS_ACCOUNT_SETUP=0 + WINDOWS_ACCOUNT_USERNAME="$(yad_field_value "$account_username_field")" + WINDOWS_ACCOUNT_PASSWORD="$(yad_field_value "$account_password_field")" + [ "$(yad_field_value "$locale_checkbox_field")" == TRUE ] && WINDOWS_LOCALE_SETUP=1 || WINDOWS_LOCALE_SETUP=0 + WINDOWS_LOCALE="$(yad_field_value "$locale_field" | awk -F': ' '{print $1}')" + sel_lang="$(yad_field_value "$language_field")" + sel_code="${sel_lang%%:*}" + if is_known_win_lang "$sel_code" ;then + WIN_LANG="$sel_code" + fi + if [ -n "$sound_items" ];then + [ "$(yad_field_value "$play_sound_field")" == TRUE ] && PLAY_SOUND=1 || PLAY_SOUND=0 + #the combo shows labels, so map the chosen one back to the value the player needs + sel_sound_label="$(yad_field_value "$completion_sound_field")" + sel_sound="$(wor_sound_options | awk -F'\t' -v l="$sel_sound_label" '$2 == l {print $1; exit}')" + [ -n "$sel_sound" ] && COMPLETION_SOUND="$sel_sound" + [ "$(yad_field_value "$notification_field")" == TRUE ] && SHOW_NOTIFICATION=1 || SHOW_NOTIFICATION=0 + fi #end of parsing check-box values for advanced options window break #as the DL_DIR value was not changed, go back to the Installation Overview window @@ -658,22 +3087,81 @@ fi echo -e "CONFIG_TXT: ⤵\n$(echo "$CONFIG_TXT" | sed 's/^/ > /g')\nCONFIG_TXT: ⤴\n" } -echo "Launching install-wor.sh in a separate terminal" +echo "Running install-wor.sh" + +abort_marker="$(mktemp -u)" +#Ubuntu must show progress immediately: package-manager and host preflight work can occur before +#the child reaches its authentication marker, and waiting there makes the GUI look hung at 0%. +if ! is_macos;then + export GUI_PROGRESS_EARLY=1 +fi +gui_start_installer + +progress_fifo="$(mktemp -u)" +mkfifo "$progress_fifo" +tail -n +1 -F "$progress_file" > "$progress_fifo" 2>/dev/null & +tail_pid=$! +# LINUX_PROGRESS_AWK_BEGIN +awk -F'\t' ' + #pct, not sub: sub() is a built-in awk function and cannot be used as a variable + #before the first STEP (e.g. while clearing the cache) the percentage stands on its own + function overall() { + msg=title; + if (pct > 0 && task_title != "") msg=task_title; + if (total+0 > 0) { + printf("1:%d\n", ((step-1)*100 + pct) / (total*100) * 100); + if (pct > 0) { + printf("1:# Overall - Step %d/%d: %s (%d%%)\n", step, total, msg, pct); + } else { + printf("1:# Overall - Step %d/%d: %s\n", step, total, title); + } + } else { + printf("1:%d\n", pct+0); + printf("1:# Overall: %s (%d%%)\n", status_msg, pct); + } + printf("2:%d\n", pct+0); + printf("2:# Sub-progress: %s (%d%%)\n", msg, pct); + } + /^STEP/ { step=$2+0; total=$3+0; title=$4; pct=0; task_title=""; overall(); fflush() } + /^SUBSTEP/ { pct=$2+0; if (pct<0) pct=0; if (pct>100) pct=100; overall(); fflush() } + /^TASK/ { pct=$2+0; if (pct<0) pct=0; if (pct>100) pct=100; task_title=$3; overall(); fflush() } + /^STATUS/ { status_msg=$2; if (step+0 == 0) overall(); else printf("1:# %s\n", status_msg); fflush() } +# LINUX_PROGRESS_AWK_END +' < "$progress_fifo" | + yad "${yadflags[@]}" --width="$(wor_yad_width 680)" --height="$(wor_yad_height 330)" \ + --progress --image="$WOR_LOGO_PATH" --text=$'Preparing flash...\nRunning setup and preflight checks. The bars will move when WoR-Flasher reaches a measured step.' --bar="Overall:NORM" --bar="Sub-progress:NORM" --button='Abort':1 & +yad_pid=$! + +progress_aborted=0 +while [ ! -f "$done_marker" ];do + if ! kill -0 "$yad_pid" 2>/dev/null ;then + progress_aborted=1 + break + fi + sleep 0.3 +done + +if [ "$progress_aborted" == 1 ];then + touch "$abort_marker" + status "Aborting at your request" + kill_process_tree "$installer_pid" + wait "$installer_pid" 2>/dev/null + kill "$tail_pid" 2>/dev/null + wait "$tail_pid" 2>/dev/null + rm -f "$progress_fifo" "$progress_file" "$done_marker" "$auth_marker" "$abort_marker" "$error_marker" + saved_log="$(gui_save_failure_log)" + wor_play_result_sound failure + wor_show_result_notification failure + yad "${yadflags[@]}" --text="Flashing was stopped before it finished.\n\n$DEVICE is now in an unusable state and has to be flashed again before it can boot.\n\nFull log: $saved_log" + exit 1 +fi -#Terminals like lxterminal and gnome-terminal reuse an existing process, so an exported -#environment does not reach the new window. Write the values to a file instead and source -#it there. declare -p quotes everything correctly, so no value can break out of the string. -export DIRECTORY cli_script DL_DIR BID WIN_LANG RPI_MODEL DEVICE CAN_INSTALL_ON_SAME_DRIVE CONFIG_TXT DRY_RUN USE_CACHE SOURCE_FILE -export RUN_MODE=gui -env_file="$(mktemp)" || error "Failed to create a temporary file" -trap 'rm -f "$env_file"' EXIT -declare -p DIRECTORY cli_script DL_DIR BID WIN_LANG RPI_MODEL DEVICE CAN_INSTALL_ON_SAME_DRIVE CONFIG_TXT DRY_RUN USE_CACHE SOURCE_FILE RUN_MODE > "$env_file" +exitcode="$(cat "$done_marker" 2>/dev/null)" +[ -z "$exitcode" ] && exitcode=1 -#run the install-wor.sh script in a terminal. If it succeeds, the "Next steps" window opens. If it fails, the terminal stays open forever until you close it. -"$DIRECTORY/terminal-run" 'source "'"$env_file"'" -rm -f "'"$env_file"'" -"$cli_script" -exitcode=$? +kill "$tail_pid" "$yad_pid" 2>/dev/null +wait "$tail_pid" "$yad_pid" 2>/dev/null +rm -f "$progress_fifo" "$progress_file" "$done_marker" "$auth_marker" "$abort_marker" #clear zram - avoid leaving files occupying space in /zram if [ "$DL_DIR" == /zram ];then @@ -681,14 +3169,28 @@ if [ "$DL_DIR" == /zram ];then fi if [ "$exitcode" == 0 ];then + rm -f "$output_log" "$error_marker" + wor_play_result_sound success + wor_show_result_notification success #display "next steps" window - yad --center --window-icon="$DIRECTORY/logo.png" --title="Windows on Raspberry" \ - --image="$DIRECTORY/next-steps.png" --button=Close:0 + linux_completion_image="$(wor_yad_image_for_screen "$WOR_ASSETS_DIR/next-steps.png" "$WOR_LOGO_PATH" 730 440)" + yad --center --width="$(wor_yad_width 690)" --height="$(wor_yad_height 380)" --window-icon="$WOR_LOGO_PATH" --class="$WOR_ICON_NAME" --title="$WOR_WINDOW_TITLE" \ + --form --align=center --image-on-top --buttons-layout=center --image="$linux_completion_image" \ + --field="It is now safe to remove your USB drive.":LBL '' --button=Close:0 >/dev/null else - sleep infinity -fi' "Running $(basename "$cli_script")" + #keep the log on failure; the dialog only shows a tail, and the GUI has no terminal to fall back on + saved_log="$(gui_save_failure_log)" + wor_play_result_sound failure + wor_show_result_notification failure + if installer_showed_own_error ;then + : #install-wor.sh already displayed its own native error dialog. + else + yad "${yadflags[@]}" --text="The Windows on Raspberry script stopped unexpectedly (exit code $exitcode).\n\n$(gui_log_tail "$saved_log")\n\nFull log: $saved_log" + fi + rm -f "$error_marker" +fi -echo "The terminal running install-wor.sh has been closed." +echo "install-wor.sh has finished." #if downloading to ram, empty it now if [ "$DL_DIR" == /zram ] && [ -d /zram/peinstaller ];then diff --git a/install-wor-hook.sh b/install-wor-hook.sh new file mode 100755 index 0000000..67c3761 --- /dev/null +++ b/install-wor-hook.sh @@ -0,0 +1,151 @@ +#!/bin/bash + +#Stable integration adapter for front-ends and automation. +#It exposes the install-wor.sh engine without owning a GUI or a device workflow. +#When distributed on its own it obtains a complete checkout, because the engine needs repository assets. + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" +ENGINE="$SCRIPT_DIR/install-wor.sh" + +hook_export_assignment() { #Input: NAME=VALUE. Export one engine setting passed as a hook option. + local assignment="$1" name + name="${assignment%%=*}" + case "$assignment" in + *=*) ;; + *) printf 'Invalid --set value. Expected NAME=VALUE.\n' >&2; exit 2 ;; + esac + case "$name" in + ''|[!A-Za-z_]*|*[!A-Za-z0-9_]*) printf 'Invalid --set name: %s\n' "$name" >&2; exit 2 ;; + esac + export "$assignment" +} + +hook_checkout_complete() { #Input: checkout directory. Output: success when every runtime file exists. + local checkout_dir="$1" + local required_path + for required_path in \ + install-wor.sh \ + src/lib/metadata.sh src/lib/dependencies.sh src/lib/paths.sh src/lib/cleanup.sh src/lib/gui.sh \ + src/config/metadata.json src/config/metadata.schema.json \ + config-templates/config.json config-templates/config.schema.json \ + config-templates/pi3.config.txt config-templates/pi4.config.txt config-templates/pi5.config.txt \ + config-templates/pi4-ram-unlock.ps1 config-templates/pi4-ram-unlock-specialize.xml \ + config-templates/oobe-network-bypass.xml config-templates/prefinalize.cmd ;do + [ -f "$checkout_dir/$required_path" ] || return 1 + done +} + +if ! hook_checkout_complete "$SCRIPT_DIR" ;then + : "${WOR_HOOK_REPOSITORY:=https://github.com/blackoutsecure/wor-flasher.git}" + #a released hook must clone a durable ref; working branches are deleted after merge + : "${WOR_HOOK_REF:=main}" + : "${WOR_HOOK_INSTALL_DIR:=${XDG_CACHE_HOME:-$HOME/.cache}/wor-flasher-hook}" + ENGINE="$WOR_HOOK_INSTALL_DIR/install-wor.sh" + if [ ! -f "$ENGINE" ];then + command -v git >/dev/null || { printf 'git is required to obtain WoR-Flasher.\n' >&2; exit 1; } + [ ! -e "$WOR_HOOK_INSTALL_DIR" ] || { printf 'WoR-Flasher cache is incomplete: %s\n' "$WOR_HOOK_INSTALL_DIR" >&2; exit 1; } + mkdir -p "$(dirname "$WOR_HOOK_INSTALL_DIR")" || exit 1 + bootstrap_dir="${WOR_HOOK_INSTALL_DIR}.tmp.$$" + trap 'rm -rf "$bootstrap_dir"' EXIT + printf 'Obtaining WoR-Flasher from %s (%s)...\n' "$WOR_HOOK_REPOSITORY" "$WOR_HOOK_REF" >&2 + git clone --quiet --depth 1 --branch "$WOR_HOOK_REF" "$WOR_HOOK_REPOSITORY" "$bootstrap_dir" \ + || { printf 'Failed to obtain WoR-Flasher.\n' >&2; exit 1; } + hook_checkout_complete "$bootstrap_dir" \ + || { printf 'The obtained WoR-Flasher checkout is incomplete.\n' >&2; exit 1; } + mv "$bootstrap_dir" "$WOR_HOOK_INSTALL_DIR" || exit 1 + trap - EXIT + fi + if ! hook_checkout_complete "$WOR_HOOK_INSTALL_DIR" ;then + printf 'The obtained WoR-Flasher checkout is incomplete: %s\n' "$WOR_HOOK_INSTALL_DIR" >&2 + exit 1 + fi +fi + +while [ $# -gt 0 ]; do + case "$1" in + --config=*) + WOR_CONFIG_FILE="${1#*=}" + export WOR_CONFIG_FILE + shift + ;; + --config) + [ -n "${2:-}" ] || { printf 'Usage: %s [--config FILE] [--progress-file FILE] [--set NAME=VALUE] COMMAND\n' "$0" >&2; exit 2; } + WOR_CONFIG_FILE="$2" + export WOR_CONFIG_FILE + shift 2 + ;; + --progress-file=*) + WOR_GUI_PROGRESS_FILE="${1#*=}" + case "$WOR_GUI_PROGRESS_FILE" in + ''|*$'\n'*|*$'\r'*) printf 'Invalid --progress-file value.\n' >&2; exit 2 ;; + esac + export WOR_GUI_PROGRESS_FILE + shift + ;; + --progress-file) + [ -n "${2:-}" ] || { printf 'Usage: %s [--config FILE] [--progress-file FILE] [--set NAME=VALUE] COMMAND\n' "$0" >&2; exit 2; } + WOR_GUI_PROGRESS_FILE="$2" + case "$WOR_GUI_PROGRESS_FILE" in + ''|*$'\n'*|*$'\r'*) printf 'Invalid --progress-file value.\n' >&2; exit 2 ;; + esac + export WOR_GUI_PROGRESS_FILE + shift 2 + ;; + --set=*) + hook_export_assignment "${1#*=}" + shift + ;; + --set) + [ -n "${2:-}" ] || { printf 'Usage: %s [--config FILE] [--progress-file FILE] [--set NAME=VALUE] COMMAND\n' "$0" >&2; exit 2; } + hook_export_assignment "$2" + shift 2 + ;; + *) + break + ;; + esac +done + +if [ "${1:-}" == run ];then + shift + exec "$ENGINE" ${WOR_CONFIG_FILE:+--config="$WOR_CONFIG_FILE"} "$@" +fi + +# shellcheck disable=SC1090 +source "$ENGINE" source +[ -n "${WOR_CONFIG_FILE:-}" ] && load_config_json "$WOR_CONFIG_FILE" || load_config_json + +case "${1:-}" in + list-devices) + require_linux_host + list_dev_paths + ;; + describe-device) + [ -n "${2:-}" ] || { printf 'Usage: %s describe-device DEVICE\n' "$0" >&2; exit 2; } + describe_device "$2" + ;; + summary) + settings_summary + ;; + *) + cat >&2 <value lines. + run [ARGS...] Run install-wor.sh with environment-provided settings. + +Set the same variables documented in README.md before calling run, including +DEVICE, RPI_MODEL, BID, WIN_LANG and CAN_INSTALL_ON_SAME_DRIVE, or pass --config FILE. +Use --progress-file FILE with run to receive STATUS, STEP, SUBSTEP and TASK events. +Use --set NAME=VALUE to pass engine settings as options instead of environment variables. + +If install-wor.sh is not next to this adapter, the complete WoR-Flasher checkout +is obtained automatically. Override WOR_HOOK_REPOSITORY, WOR_HOOK_REF or +WOR_HOOK_INSTALL_DIR to use a trusted mirror, ref or installation directory. +USAGE + exit 2 + ;; +esac diff --git a/install-wor.sh b/install-wor.sh index 85237c1..3c79b06 100755 --- a/install-wor.sh +++ b/install-wor.sh @@ -1,123 +1,1543 @@ #!/bin/bash -#Written by Botspot -#This script is an automation for the tutorial that can be found here: https://worproject.com/guides/how-to-install/from-other-os +#WoR-Flasher - install Windows 10/11 ARM64 on a Raspberry Pi from Linux or macOS. +#Version is defined in src/lib/metadata.sh. +# +#Copyright (C) 2021-2026 Botspot and the WoR-Flasher contributors. +#Copyright (C) 2026 Blackout Secure. +# +#Original project and author: +# Botspot: https://github.com/Botspot +# Upstream WoR-Flasher: https://github.com/Botspot/wor-flasher +#This maintained fork and source repository: +# Blackout Secure: https://blackoutsecure.app +# Repository: https://github.com/blackoutsecure/wor-flasher +# Maintainer contact: https://linktr.ee/billmcilhargey +# +#Documentation and related projects: +# Installation guide: https://worproject.com/guides/how-to-install/from-other-os +# Windows on Raspberry: https://worproject.com/ +# Issues and support: https://github.com/Botspot/wor-flasher/issues +# Security reports: https://github.com/blackoutsecure/wor-flasher/security/policy +# +#License and attribution: +# GPL-3.0 is provided for the Blackout Secure additions; see LICENSE and NOTICE. +# The pre-existing Botspot code shipped without an explicit license. This notice does +# not relicense that code; preserve Botspot's credit and consult NOTICE before +# redistributing or relicensing it. +# +#Safety recommendations: +# Use an external, physical, writable whole disk and verify the target before flashing. +# Back up the target first. Flashing erases the selected disk and can destroy data. +# Do not run as root, remove power, or unplug the target until verification and eject finish. +# Read README.md before use and prefer the tested pinned firmware and driver versions. +# +#Originally written by Botspot. Automates this tutorial: +# https://worproject.com/guides/how-to-install/from-other-os +# +#This source is directly maintained with Botspot's project direction visible: +# https://github.com/Botspot/wor-flasher +#WOR_FLASHER_VERSION below is the single source of truth for its version line. +# +#Version history +#--------------- +#2.0.0 - Modernized the cross-platform flashing workflow, release tooling and configuration. +# Report the macOS partition finalizer's exit status when its result file is unavailable. +# Require finalizer readiness before disk preparation and pre-create user-owned results. +# Launch the macOS finalizer from the authenticated parent for no-terminal sudo sessions. +# Reuse that worker for the late Pi3 GPT patch; macOS does not depend on sudo keepalive. +# Detach user-mounted macOS ISO images without requesting administrator access. +# Automatically choose Ignore for the exact macOS unreadable-disk alert during active GUI writes. +# Package source notices with each runtime and derive release versions from canonical JSON metadata. +# Validate staged runtime freshness and allow the optional Pi4 UEFI Shell without an answer file. +# Added a native standalone macOS runtime, resilient disk handling, configurable completion +# sounds and notifications, password-retry resume, and a compact config.txt editor. +# macOS privacy-denial failures now expose an Open Settings action for Removable Volumes +# or Full Disk Access from the native completion dialog. +# macOS GUI authentication is established in the main installer shell before disk helpers +# capture output, preventing a duplicate administrator-password prompt during verification. +# Windows keyboard and regional settings now default on using the host locale, fall back to +# en-US, and preserve a selection made during the current GUI run. +# Deduplicated cached-image and built-in locale choices so the macOS picker selects the +# detected host locale instead of an adjacent duplicate entry. +#1.0.2 - WoR-Flasher.app now carries a validated immutable runtime for standalone use, stages +# detached updates under Application Support, rejects downgrades, and falls back through +# active, previous, and embedded runtimes without modifying its own bundle. +# A standalone install-wor-hook.sh now obtains a complete trusted checkout automatically. +# Fixed the native macOS partnership announcement construction, dark-mode text contrast +# and layout on current JXA runtimes. +# Scaled the partnership banner to 800x533 so the Linux announcement window fits on +# screen; yad draws --image at its native size and cannot shrink it. +# Linux dialogs that auto-size their height no longer log a Gtk-CRITICAL warning. +# Direct maintenance messaging and default update checks now use Blackout Secure's source. +#1.0.1 - Pi 4 UEFI pinned to v1.50, the only release where both the Ethernet MAC and microSD +# boot work. v1.51 (the previous pin) and v1.52 report a MAC of 00:00:00:00:00:00, +# leaving Windows on an APIPA address with no DHCP; v1.53 fixes that but still does +# not boot from microSD. See pftf/RPi4 issues 283 and 285. +# Pi 4 RAM unlock and the offline-OOBE answer file now reach the installed OS through +# WoR-PE's prefinalize hook; the media-root copies alone were never read. +#1.0.0 - First versioned release of this fork. +# macOS host support: diskutil/hdiutil drive discovery, sgdisk GPT partitioning that keeps +# WOR_BOOT as partition 1 (an extra ESP made the Pi 4 fall back to PXE boot), and a native +# AppKit/JXA wizard, progress window, Advanced Options window and error dialogs. +# No visible terminal in GUI mode: install-wor.sh reports progress over WOR_GUI_PROGRESS_FILE +# and both front-ends render it (AppKit on macOS, yad on Linux). Administrator access is +# requested through a native password dialog on both platforms. +# Post-flash verification of partitions, boot files and checksums (SKIP_IMAGE_VERIFICATION). +# Offline Windows OOBE via a shipped Autounattend.xml (OOBE_NETWORK_BYPASS, default on). +# Automatic Pi 4 3 GB RAM unlock after the WoR-PE reboot (PI4_AUTO_DISABLE_3GB). +# Pinned, overridable UEFI firmware and driver versions; Pi 4 stays on UEFI v1.51 because +# v1.52 does not boot reliably from microSD. +# Cache modes with SHA-256 payload manifests (USE_CACHE), free-space preflight, and +# HideEmptyDrives written into the cached WoR-PE settings.ini. +# Editable config.txt sourced from config-templates/, applied by the CLI and the GUI alike +# (APPLY_CUSTOM_CONFIG_TXT). +# One engine, two front-ends: install-wor-gui.sh sources this script and adds only windows. +# Explicit entry point '--gui'; the front-end is never chosen by sniffing DISPLAY. +# Test suite (tests/run-tests.sh) plus ShellCheck, macOS and Linux dry-run CI. +#0.x - Upstream Botspot releases, never tagged. Highlights, oldest first: initial WoR automation, +# self-updater, "next steps" window, complete rewrite to use ESD releases, download-to-RAM +# support, Pi 5 support, GitHub API fallback for UEFI firmware, empty block devices filtered +# out of the drive list, and SHA-256 hashed ESD image handling. + +#Single source of truth for the product name, window title, and version. +#shellcheck source=src/lib/metadata.sh +WOR_METADATA_FILE="$(dirname "${BASH_SOURCE[0]}")/src/lib/metadata.sh" +source "$WOR_METADATA_FILE" +#shellcheck source=src/lib/dependencies.sh +source "$(dirname "$WOR_METADATA_FILE")/dependencies.sh" +#shellcheck source=src/lib/paths.sh +source "$(dirname "$WOR_METADATA_FILE")/paths.sh" +#shellcheck source=src/lib/cleanup.sh +source "$(dirname "$WOR_METADATA_FILE")/cleanup.sh" +#shellcheck source=src/lib/gui.sh +source "$(dirname "$WOR_METADATA_FILE")/gui.sh" +export WOR_METADATA_FILE WOR_FLASHER_NAME WOR_FLASHER_VERSION WOR_APP_TITLE WOR_WINDOW_TITLE CLEANUP_MOUNTS=() +CLEANUP_DEVICES=() +CLEANUP_FILES=() -cleanup_mounts() { - local mountpoint - for mountpoint in "${CLEANUP_MOUNTS[@]}" ;do - sudo umount -q "$mountpoint" 2>/dev/null - done +HOST_OS="$(uname -s)" + +is_macos() { + [ "$HOST_OS" == Darwin ] } -register_mount_cleanup() { #Input: mountpoint - CLEANUP_MOUNTS+=("$1") - trap cleanup_mounts EXIT +gui_start_sudo_keepalive() { #Keeps the GUI sudo timestamp fresh after the one allowed password prompt. + [ "$RUN_MODE" == gui ] || return 0 + #The macOS worker retains authorization; a background shell cannot refresh its parent's ticket. + is_macos && return 0 + [ "${BASH_SUBSHELL:-0}" -eq 0 ] || return 0 + [ -z "${WOR_GUI_SUDO_KEEPALIVE_PID:-}" ] || return 0 + ( while kill -0 "$$" 2>/dev/null ;do command sudo -n -v >/dev/null 2>&1 || true; sleep 30; done ) >/dev/null 2>&1 "$MACOS_ASKPASS" <<'ASKPASS' +#!/bin/bash +#shellcheck disable=SC1090 +source "$WOR_METADATA_FILE" +wor_osascript - "$WOR_WINDOW_TITLE" "$WOR_FLASH_TARGET" <<'APPLESCRIPT' +on run argv + set windowTitle to item 1 of argv + set targetDevice to item 2 of argv + set promptText to "To continue, WoR-Flasher needs administrator access for disk preparation, formatting, and flashing." & return & return & "Target: " & targetDevice & return & return & "Only continue if this is the drive you intend to erase." + activate + set dialogResult to display dialog promptText default answer "" with hidden answer with title windowTitle with icon caution + return text returned of dialogResult +end run +APPLESCRIPT +ASKPASS + register_file_cleanup "$MACOS_ASKPASS" + fi + if [ -n "$MACOS_ASKPASS" ];then + if command sudo -n -v >/dev/null 2>&1;then + export WOR_GUI_SUDO_PROMPTED=1 + gui_start_sudo_keepalive + command sudo -n "$@" + return + fi + if [ "${WOR_GUI_SUDO_PROMPTED:-0}" == 1 ];then + printf '%s\n' 'Administrator authentication is no longer reusable; refusing to show a second password dialog during the flash.' >&2 + return 1 + fi + printf '%s\n' 'Administrator access: requesting macOS password with the native WoR-Flasher dialog.' >&2 + emit_gui_task_progress 0 'Waiting for administrator access...' + WOR_FLASH_TARGET="$DEVICE" WOR_METADATA_FILE="$WOR_METADATA_FILE" WOR_APP_TITLE="$WOR_APP_TITLE" WOR_WINDOW_TITLE="$WOR_WINDOW_TITLE" SUDO_ASKPASS="$MACOS_ASKPASS" command sudo -A "$@" + sudo_status=$? + if [ "$sudo_status" == 0 ];then + export WOR_GUI_SUDO_PROMPTED=1 + gui_start_sudo_keepalive + return 0 + fi + return "$sudo_status" + fi + elif ! is_macos && [ "$RUN_MODE" == gui ];then + if [ -z "$LINUX_ASKPASS" ] && LINUX_ASKPASS="$(mktemp)" && chmod +x "$LINUX_ASKPASS";then + cat > "$LINUX_ASKPASS" <<'ASKPASS' +#!/bin/bash +prompt="To continue, WoR-Flasher needs administrator access for disk preparation, formatting, and flashing.\n\nTarget: $WOR_FLASH_TARGET\n\nOnly continue if this is the drive you intend to erase." +if command -v yad >/dev/null ;then + yad --center --window-icon="$WOR_ICON_PATH" --class="$WOR_ICON_NAME" --title="$WOR_WINDOW_TITLE" --entry --hide-text --text="$prompt" +elif command -v zenity >/dev/null ;then + zenity --password --title="$WOR_WINDOW_TITLE" +fi +ASKPASS + register_file_cleanup "$LINUX_ASKPASS" + fi + if [ -n "$LINUX_ASKPASS" ];then + if command sudo -n -v >/dev/null 2>&1;then + export WOR_GUI_SUDO_PROMPTED=1 + gui_start_sudo_keepalive + command sudo -n "$@" + return + fi + if [ "${WOR_GUI_SUDO_PROMPTED:-0}" == 1 ];then + printf '%s\n' 'Administrator authentication is no longer reusable; refusing to show a second password dialog during the flash.' >&2 + return 1 + fi + printf '%s\n' 'Administrator access: requesting password with the WoR-Flasher dialog.' >&2 + emit_gui_task_progress 0 'Waiting for administrator access...' + WOR_FLASH_TARGET="$DEVICE" WOR_ICON_PATH="$WOR_LOGO_PATH" SUDO_ASKPASS="$LINUX_ASKPASS" command sudo -A "$@" + sudo_status=$? + if [ "$sudo_status" == 0 ];then + export WOR_GUI_SUDO_PROMPTED=1 + gui_start_sudo_keepalive + return 0 + fi + return "$sudo_status" + fi + fi + if is_macos && [ "$RUN_MODE" == gui ];then + printf '%s\n' 'Administrator password dialog was unavailable or canceled; refusing to prompt in the console.' >&2 + return 1 + fi + command sudo "$@" } gui_error_dialog() { #Input: error message - local plain + local plain icon_path error_jxa plain="$(echo -e "An error has occurred:\n$1\nExiting now." | sed 's/\x1b\[[0-9;]*m//g' | sed 's/\x1b\[[0-9;]*//g' | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g")" - if command -v zenity >/dev/null ;then - zenity --error --title "$(basename "$0")" --width 360 --text "$plain" - elif command -v osascript >/dev/null ;then - osascript -e 'on run argv' -e 'display alert (item 1 of argv) message (item 2 of argv) as critical' -e 'end run' "$(basename "$0")" "$plain" >/dev/null + #write the error marker before showing the dialog, so the GUI doesn't race with the installer + if [ -n "$WOR_GUI_ERROR_MARKER" ] ;then + mkdir -p "$(dirname "$WOR_GUI_ERROR_MARKER")" 2>/dev/null + touch "$WOR_GUI_ERROR_MARKER" 2>/dev/null + sync 2>/dev/null || true + fi + [ -f "$WOR_LOGO_PATH" ] && icon_path="$WOR_LOGO_PATH" || icon_path='' + if command -v osascript >/dev/null ;then + error_jxa="$(wor_jxa_window_lib; cat <<'JXA' +ObjC.import('AppKit') +const args = $.NSProcessInfo.processInfo.arguments +const message = ObjC.unwrap(args.objectAtIndex(4)) +const iconPath = ObjC.unwrap(args.objectAtIndex(5)) +const appTitle = ObjC.unwrap(args.objectAtIndex(6)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(7) || appTitle) + +const app = $.NSApplication.sharedApplication +$.NSProcessInfo.processInfo.processName = appTitle +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +worInstallAppMenu(app, appTitle, windowTitle, iconPath) +worSetAppIcon(app, iconPath) + +let window +const Controller = ObjC.registerSubclass({ + name: 'WorErrorController', + superclass: 'NSObject', + methods: { + 'okClicked:': { + types: ['void', ['id']], + implementation: function() { + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + 'windowWillClose:': { + types: ['void', ['id']], + implementation: function() { + app.stopModalWithCode($.NSOKButton) + } + }, + 'handleQuitEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + app.stopModalWithCode($.NSOKButton) + window.orderOut(null) + } + }, + //clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never come back + 'handleReopenEvent:withReplyEvent:': { + types: ['void', ['id', 'id']], + implementation: function() { + if (window.isMiniaturized) window.deminiaturize(null) + window.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + } + } + } +}) +const controller = $.WorErrorController.alloc.init +app.setDelegate(controller) + +const width = 560 +const measured = $.NSMutableAttributedString.alloc.init +measured.mutableString.appendString($(message)) +measured.addAttributeValueRange($.NSFontAttributeName, $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium), $.NSMakeRange(0, message.length)) +const measuredBounds = measured.boundingRectWithSizeOptions($.NSMakeSize(width - 40, 2000), $.NSStringDrawingUsesLineFragmentOrigin | $.NSStringDrawingUsesFontLeading) +//100pt of chrome: the button row below the text and the gap under the title bar +const height = Math.min(560, Math.max(180, Math.ceil(measuredBounds.size.height) + 100)) +//fixed layout: no drag-resize and no zoom/maximize button, only minimize (and restore) via the titlebar +window = worMakeWindow({ width: width, height: height, title: windowTitle, delegate: controller }) + +const content = window.contentView + +const label = $.NSTextField.labelWithString(message) +label.frame = $.NSMakeRect(20, 70, width - 40, height - 100) +label.font = $.NSFont.systemFontOfSizeWeight(14, $.NSFontWeightMedium) +label.setUsesSingleLineMode(false) +label.cell.setWraps(true) +label.cell.setScrollable(false) +content.addSubview(label) + +const okButton = $.NSButton.buttonWithTitleTargetAction('OK', controller, 'okClicked:') +okButton.bezelStyle = $.NSBezelStyleRounded +okButton.keyEquivalent = '\r' +okButton.sizeToFit +const okWidth = Math.max(96, okButton.frame.size.width) +okButton.frame = $.NSMakeRect(width - 20 - okWidth, 22, okWidth, 32) +content.addSubview(okButton) + +window.makeKeyAndOrderFront(null) +if (!app.isActive) app.requestUserAttention($.NSInformationalRequest) +app.activateIgnoringOtherApps(true) +//the Dock Quit item sends an aevt/quit Apple Event that a modal session would otherwise never see +$.NSAppleEventManager.sharedAppleEventManager.setEventHandlerAndSelectorForEventClassAndEventID(controller, 'handleQuitEvent:withReplyEvent:', 0x61657674, 0x71756974) +$.NSAppleEventManager.sharedAppleEventManager.setEventHandlerAndSelectorForEventClassAndEventID(controller, 'handleReopenEvent:withReplyEvent:', 0x61657674, 0x72617070) +app.runModalForWindow(window) +app.terminate(null) +JXA +)" + wor_osascript -l JavaScript - "$plain" "$icon_path" "$WOR_APP_TITLE" "$WOR_WINDOW_TITLE" <<<"$error_jxa" >/dev/null 2>&1 + elif command -v yad >/dev/null ;then + yad --center --window-icon="$icon_path" --class="$WOR_ICON_NAME" --title="$WOR_WINDOW_TITLE" --text="$plain" + elif command -v zenity >/dev/null ;then + zenity --error --title "$WOR_WINDOW_TITLE" --width 360 --text "$plain" + fi +} + +error() { #Input: error message + printf '\033[91m%b\033[0m\n' "$1" 1>&2 + if [ "$RUN_MODE" == gui ];then + #Once the progress window is open, an engine-owned modal blocks process exit and leaves two + #windows stacked. Let the front-end close progress, save the log, and show the failure dialog. + if [ -z "${WOR_GUI_PROGRESS_FILE:-}" ];then + gui_error_dialog "$1" + fi + fi + exit 1 +} + +handle_interrupt() { #Signal handler. GUI Abort has its own completion dialog, so do not stack a second error. + if [ "$RUN_MODE" == gui ] && [ -n "${WOR_GUI_ABORT_MARKER:-}" ] && [ -e "$WOR_GUI_ABORT_MARKER" ];then + printf 'Interrupted at GUI request.\n' 1>&2 + exit 130 + fi + error "Interrupted." +} + +status() { #blue text to indicate what is happening + if [[ "$1" == '-'* ]] && [ ! -z "$2" ];then + printf '\033[96m%b\033[0m' "$2" 1>&2 + [ "$1" == '-n' ] || printf '\n' 1>&2 + emit_gui_progress "STATUS $2" + else + printf '\033[96m%b\033[0m\n' "$1" 1>&2 + emit_gui_progress "STATUS $1" + fi +} + +echo_green() { #announce the success of a major action + printf '\033[92m%b\033[0m\n' "$1" 1>&2 +} + +echo_red() { #announce the failure of a nonfatal action + printf '\033[91m%b\033[0m\n' "$1" 1>&2 +} + +warning() { #Input: message. A nonfatal problem the user should know about, but which does not stop the run. + printf '\033[93m%b\033[0m\n' "$1" 1>&2 +} + +cli_intro() { + [ "$RUN_MODE" == gui ] && return + printf '\n\033[1;96m' + cat 1>&2 <<'BANNER' + __ __ ____ _____ _ _ + \ \ / /__ | _ \ | ___| | __ _ ___| |__ ___ _ __ + \ \ /\ / / _ \| |_) |____| |_ | |/ _` / __| '_ \ / _ \ '__| + \ V V / (_) | _ <_____| _| | | (_| \__ \ | | | __/ | + \_/\_/ \___/|_| \_\ |_| |_|\__,_|___/_| |_|\___|_| +BANNER + printf '\033[0m\033[1;96m %s\033[0m \033[1;92m| STRONGER TOGETHER. BETTER FOR EVERYONE.\033[0m\n' "$WOR_FLASHER_NAME" 1>&2 + printf ' Botspot + Blackout Secure | Community maintained\n' 1>&2 + printf ' github.com/Botspot | blackoutsecure.app\n\n' 1>&2 +} + +emit_gui_progress() { #Input: line. Lets a native GUI progress window show live status without a visible terminal. + [ -n "$WOR_GUI_PROGRESS_FILE" ] && printf '%s\n' "$1" >> "$WOR_GUI_PROGRESS_FILE" 2>/dev/null +} + +emit_gui_substep() { #Input: percent complete of the current step, so the bar moves within a step too. + [ -n "$WOR_GUI_PROGRESS_FILE" ] && printf 'SUBSTEP\t%s\n' "$1" >> "$WOR_GUI_PROGRESS_FILE" 2>/dev/null +} + +emit_gui_task_progress() { #Input: percent and label. Shows the current subtask alongside the step bar. + [ -n "$WOR_GUI_PROGRESS_FILE" ] && [ -n "${2:-}" ] \ + && printf 'TASK\t%s\t%s\n' "$1" "$2" >> "$WOR_GUI_PROGRESS_FILE" 2>/dev/null +} + +report_copy_task() { #Input: percent, label. Standard way to advance the task bar for a named sub-step + #that has no byte-level progress of its own; keeps it moving instead of frozen at the previous task's value. + emit_gui_task_progress "$1" "$2" + emit_gui_substep "$1" + echo " - $2" +} + +report_verification_task() { #Input: percent, label. Advances verification when a validation tool cannot report byte progress. + emit_gui_task_progress "$1" "$2" + emit_gui_substep "$1" +} + +progress_task_label() { #Input: command and args. Output: friendly label for GUI subprogress. + local previous='' arg + for arg in "$@" ;do + if [ "$previous" == '-N' ];then + printf '%s\n' "$arg" + return + fi + previous="$arg" + done + basename "$1" +} + +gui_percent_stream() { #Mirrors a progress stream back out while reporting any percentage it carries. + local line percent + while IFS= read -r line ;do + printf '%s\n' "$line" 1>&2 + if [[ "$line" =~ ([0-9]{1,3})% ]];then + percent="${BASH_REMATCH[1]}" + emit_gui_substep "$percent" + emit_gui_task_progress "$percent" "${WOR_CURRENT_PROGRESS_TASK:-}" + fi + done +} + +with_progress_capture() { #Input: command. Feeds the GUI bar from tools that already print a percentage. + local previous_task progress_task rc + if [ -n "$WOR_GUI_PROGRESS_FILE" ];then + #pv and wimlib redraw with carriage returns, which read would otherwise never see as lines + previous_task="${WOR_CURRENT_PROGRESS_TASK:-}" + progress_task="$(progress_task_label "$@")" + export WOR_CURRENT_PROGRESS_TASK="$progress_task" + "$@" 2> >(tr '\r' '\n' | gui_percent_stream) + rc=$? + export WOR_CURRENT_PROGRESS_TASK="$previous_task" + return "$rc" + else + "$@" + fi +} + +clear_cached_components() { #Deletes every cached download, one at a time so the GUI can show it happening. + local targets=() target s seen removed=0 total + #winfiles_from_iso_* is also matched by winfiles_*, so skip anything already listed + for target in "$PWD/peinstaller" "$PWD/driverpackage" "$PWD"/pi[345]-uefipackage "$PWD"/winfiles_* ;do + #an unmatched glob stays literal, so only keep entries that exist + [ -e "$target" ] || continue + seen=0 + for s in "${targets[@]}" ;do [ "$s" == "$target" ] && seen=1 && break ;done + [ "$seen" == 0 ] && targets+=("$target") + done + [ -n "$WOR_CACHE_DIR" ] && [ -e "$WOR_CACHE_DIR" ] && targets+=("$WOR_CACHE_DIR") + total="${#targets[@]}" + if [ "$total" == 0 ];then + status "Nothing cached to delete" + emit_gui_substep 100 + else + for target in "${targets[@]}" ;do + status "Deleting $(basename "$target") ($((removed+1)) of $total)" + rm -rf "$target" + removed=$((removed+1)) + emit_gui_substep $((removed * 100 / total)) + done + fi + [ -z "$WOR_CACHE_DIR" ] || mkdir -p "$WOR_CACHE_DIR" +} + +phase() { #Input: message. A numbered status() line marking one of the major installation stages. + STEP_NUM=$((STEP_NUM+1)) + status "[Step $STEP_NUM/$STEP_TOTAL] $1" + emit_gui_progress "STEP $STEP_NUM $STEP_TOTAL $1" + #must follow the STEP line: the window reads the newest SUBSTEP as belonging to the current step + emit_gui_substep 0 + emit_gui_progress "TASK 0 " +} + +cli_pause() { + if [ "$RUN_MODE" != gui ] && [ -t 0 ];then + printf 'Press Enter to exit.\n' + read -r /dev/null +} + +require_linux_host() { + if is_wsl ;then + #WSL cannot reach USB storage without usbipd, and lsblk there lists WSL's own virtual disks as erasable targets + error "WoR-Flasher does not support WSL. +WSL cannot access USB drives directly, and the drives it does list are WSL's own virtual disks. +Erasing one of those would damage your WSL installation. +On Windows, use the official Windows on Raspberry Imager instead: https://worproject.com/downloads +To use WoR-Flasher, run it from a Debian-based Linux host or macOS." + fi + [ "$HOST_OS" == Linux ] || is_macos && return 0 + error "WoR-Flasher supports Linux and macOS hosts only. This host is $HOST_OS. +On Windows, use the official Windows on Raspberry Imager instead: https://worproject.com/downloads" +} + +require_macos_tools() { + if ! command -v brew >/dev/null ;then + if [ -x /opt/homebrew/bin/brew ];then + PATH="/opt/homebrew/bin:/opt/homebrew/sbin:$PATH" + elif [ -x /usr/local/bin/brew ];then + PATH="/usr/local/bin:/usr/local/sbin:$PATH" + fi + fi + local tool + for tool in diskutil hdiutil plutil; do + command -v "$tool" >/dev/null || error "macOS support requires '$tool' on PATH. Run this script from a normal macOS shell, then try again." + done +} + +darwin_plist_json() { #Input: a diskutil or hdiutil command. Output: its property list as JSON. + "$@" | plutil -convert json -o - - +} + +darwin_is_safe_device() { #Input: whole disk /dev path. Only external, physical, writable disks can be erased. + local details + details="$(darwin_plist_json diskutil info -plist "$1")" || return 1 + jq -e '.WholeDisk == true and .Internal == false and .VirtualOrPhysical == "Physical" and (.ReadOnlyMedia == false or .WritableMedia == true)' >/dev/null <<<"$details" +} + +is_safe_target_device() { #Input: target device. Protects the host boot disk and Darwin internal/virtual disks. + if is_macos ;then + [ "$1" != "$ROOT_DEV" ] && darwin_is_safe_device "$1" + else + [ "$1" != "$ROOT_DEV" ] + fi +} + +darwin_device_value() { #Input: device, jq filter. Output: a value from diskutil info. + darwin_plist_json diskutil info -plist "$1" | jq -er "$2" +} + +darwin_partition_by_volume_name() { #Input: whole disk, volume name. Output: matching partition /dev path. + local identifier + identifier="$(darwin_plist_json diskutil list -plist "$1" | jq -er --arg name "$2" '.AllDisksAndPartitions[0].Partitions[]? | select(.VolumeName == $name) | .DeviceIdentifier' | head -n1)" || return 1 + [ -n "$identifier" ] || return 1 + printf '/dev/%s\n' "$identifier" +} + +darwin_list_devices() { #Output: external physical whole disks from diskutil's plist interface. + local device + while read -r device; do + [ -z "$device" ] && continue + device="/dev/$device" + if darwin_is_safe_device "$device"; then + printf '\e[1m\e[97m%s\e[0m - \e[92m%sB\e[0m - \e[36m%s\e[0m\n' \ + "$device" \ + "$(darwin_device_value "$device" '.DiskSize // .TotalSize // .Size')" \ + "$(darwin_device_value "$device" '.MediaName // .DeviceIdentifier')" + fi + done < <(darwin_plist_json diskutil list -plist external physical | jq -r '.AllDisks[]') +} + +darwin_list_device_paths() { #Output: external physical whole-disk paths suitable for a GUI choice list. + local device + while read -r device; do + device="/dev/$device" + darwin_is_safe_device "$device" && printf '%s\n' "$device" + done < <(darwin_plist_json diskutil list -plist external physical | jq -r '.AllDisks[]') +} + +human_size() { #Input: size in bytes. Output: human-readable size (e.g. 1.9 TB) + awk -v bytes="$1" 'BEGIN { + units[0]="B"; units[1]="KB"; units[2]="MB"; units[3]="GB"; units[4]="TB" + size = bytes + 0 + i = 0 + while (size >= 1024 && i < 4) { size /= 1024; i++ } + printf "%.1f %s", size, units[i] + }' +} + +darwin_apfs_volume_names() { #Input: whole disk. Output: APFS volume labels backed by this physical disk. + local apfs_details partition_ids + partition_ids="$(darwin_plist_json diskutil list -plist "$1" | jq -r '.AllDisksAndPartitions[0].Partitions[]?.DeviceIdentifier')" || return 0 + [ -z "$partition_ids" ] && return 0 + apfs_details="$(diskutil apfs list -plist 2>/dev/null | plutil -convert json -o - -)" || return 0 + jq -r --arg ids "$partition_ids" ' + ($ids | split("\n") | map(select(length > 0))) as $stores | + [.Containers[]? + | select([.PhysicalStores[]?.DeviceIdentifier] | any(. as $id | $stores | index($id))) + | .Volumes[]?.Name] + | unique + | if length == 0 then empty else join(", ") end + ' <<<"$apfs_details" +} + +darwin_list_device_choices() { #Output: tab-delimited safe disk path, size, media name, labels, and detected volumes. + local device details label_names volume_names + while read -r device; do + device="/dev/$device" + darwin_is_safe_device "$device" || continue + details="$(darwin_plist_json diskutil list -plist "$device")" || continue + label_names="$( + { + jq -r '.AllDisksAndPartitions[0].Partitions[]?.VolumeName? // empty' <<<"$details" + darwin_apfs_volume_names "$device" + } | awk 'NF && !seen[$0]++ { labels = labels ? labels ", " $0 : $0 } END { print labels }' + )" + [ -z "$label_names" ] && label_names='No labels' + volume_names="$(jq -r '[.AllDisksAndPartitions[0].Partitions[]? | .VolumeName // .DeviceIdentifier] | if length == 0 then "No volumes" else join(", ") end' <<<"$details")" + #only the field before the first tab is parsed as the device path, so the remaining + #fields use readable spacing instead of raw tabs, which render squished together + printf '%s\t%s %s Labels: %s Volumes: %s\n' \ + "$device" \ + "$(human_size "$(darwin_device_value "$device" '.DiskSize // .TotalSize // .Size')")" \ + "$(darwin_device_value "$device" '.MediaName // .DeviceIdentifier')" \ + "$label_names" \ + "$volume_names" + done < <(darwin_plist_json diskutil list -plist external physical | jq -r '.AllDisks[]') +} + +darwin_mount_iso() { #Input: ISO path. Sets ISO_MOUNTPOINT and ISO_DEVICE. + local details + details="$(hdiutil attach -nobrowse -readonly -plist "$1")" || return 1 + ISO_MOUNTPOINT="$(plutil -convert json -o - - <<<"$details" | jq -er '."system-entities"[] | select(.["mount-point"] != null) | .["mount-point"]' | head -n1)" || return 1 + ISO_DEVICE="$(plutil -convert json -o - - <<<"$details" | jq -er '."system-entities"[] | select(.["mount-point"] != null) | .["dev-entry"]' | head -n1)" || return 1 +} + +unattend_xml() { #Output: the answer file for this run, or nothing when neither customization is wanted. + local auto_disable_3gb=0 + [ "$RPI_MODEL" == 4 ] && [ "$PI4_AUTO_DISABLE_3GB" == 1 ] && auto_disable_3gb=1 + [ "$OOBE_NETWORK_BYPASS" == 1 ] || [ "$auto_disable_3gb" == 1 ] || [ "$WINDOWS_ACCOUNT_SETUP" == 1 ] || [ "$WINDOWS_LOCALE_SETUP" == 1 ] || return 1 + + cat <<'EOF' + + +EOF + [ "$auto_disable_3gb" == 1 ] && read_config_template pi4-ram-unlock-specialize.xml + [ "$OOBE_NETWORK_BYPASS" == 1 ] && read_config_template oobe-network-bypass.xml + if [ "$WINDOWS_ACCOUNT_SETUP" == 1 ];then + cat < + + $(xml_escape "$WINDOWS_ACCOUNT_PASSWORD")true</PlainText></Password><Description>WoR-Flasher local administrator</Description><DisplayName>$(xml_escape "$WINDOWS_ACCOUNT_USERNAME")</DisplayName><Group>Administrators</Group><Name>$(xml_escape "$WINDOWS_ACCOUNT_USERNAME")</Name></LocalAccount></LocalAccounts></UserAccounts> + </component> + </settings> +EOF + fi + if [ "$WINDOWS_LOCALE_SETUP" == 1 ];then + cat <<EOF + <settings pass="oobeSystem"> + <component name="Microsoft-Windows-International-Core" processorArchitecture="arm64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"><InputLocale>$WINDOWS_LOCALE</InputLocale><SystemLocale>$WINDOWS_LOCALE</SystemLocale><UILanguage>$WINDOWS_LOCALE</UILanguage><UILanguageFallback>en-US</UILanguageFallback><UserLocale>$WINDOWS_LOCALE</UserLocale></component> + </settings> +EOF + fi + cat <<'EOF' +</unattend> +EOF +} + +xml_escape() { #Input: text. Output: XML-safe text. + printf '%s' "$1" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&apos;/g' +} + +install_windows_setup_configuration() { #Input: mounted boot partition, mounted installation partition. + local auto_disable_3gb=0 destination + [ "$RPI_MODEL" == 4 ] && [ "$PI4_AUTO_DISABLE_3GB" == 1 ] && auto_disable_3gb=1 + [ "$OOBE_NETWORK_BYPASS" == 1 ] || [ "$auto_disable_3gb" == 1 ] || return 0 + + if [ "$auto_disable_3gb" == 1 ];then + for destination in "$1/Pi4Disable3GB.ps1" "$2/Pi4Disable3GB.ps1";do + if is_macos;then + read_config_template pi4-ram-unlock.ps1 > "$destination" + else + read_config_template pi4-ram-unlock.ps1 | sudo tee "$destination" >/dev/null + fi + done + fi + + for destination in "$1/Autounattend.xml" "$2/Autounattend.xml";do + if is_macos;then + unattend_xml > "$destination" + else + unattend_xml | sudo tee "$destination" >/dev/null + fi + done +} + +darwin_fda_app_name() { #Output: the specific app that needs Full Disk Access for this run, based on how it was launched. + if [ "${WOR_NATIVE_APP:-0}" == 1 ];then + if [ -n "${WOR_APP_BUNDLE_PATH:-}" ];then + printf '%s\n' "WoR-Flasher.app at ${WOR_APP_BUNDLE_PATH}" + return + fi + printf '%s\n' "WoR-Flasher.app" + elif [ "${TERM_PROGRAM:-}" == Apple_Terminal ];then + printf '%s\n' "Terminal.app" + elif [ "${TERM_PROGRAM:-}" == iTerm.app ];then + printf '%s\n' "iTerm.app" + else + printf '%s\n' "the terminal app you launched this from" + fi +} + +darwin_fda_launcher_hint() { #Output: a fallback launcher app that may own TCC for shell-script app runs. + case "${TERM_PROGRAM:-}" in + vscode) printf '%s\n' "Visual Studio Code.app" ;; + Apple_Terminal) printf '%s\n' "Terminal.app" ;; + iTerm.app) printf '%s\n' "iTerm.app" ;; + esac +} + +darwin_diskutil_format_pair_or_die() { #Input: boot and installation partitions. Formats both through one authenticated Disk Arbitration command. + local boot_partition="$1" install_partition="$2" command_output command_status + command_output="$(sudo bash -s -- "$boot_partition" "$install_partition" <<'ROOT_SCRIPT' +set -e +/usr/sbin/diskutil eraseVolume MS-DOS WOR_BOOT "$1" +/usr/sbin/diskutil eraseVolume ExFAT WOR_INSTALL "$2" +ROOT_SCRIPT +)" + command_status=$? + [ "$command_status" == 0 ] && return 0 + if printf '%s' "$command_output" | grep -Eqi 'no password was provided|user canceled|password is required' ;then + error "Failed to format the macOS partitions: the administrator password dialog was canceled or no password was provided. Enter the macOS administrator password in the WoR-Flasher dialog and try again." + fi + if printf '%s' "$command_output" | grep -qi 'operation not permitted' ;then + open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" >/dev/null 2>&1 & + error "Failed to format the macOS partitions: macOS returned 'Operation not permitted' through diskutil. In the System Settings window that just opened (Privacy & Security > Full Disk Access), enable the exact WoR-Flasher.app copy and its launcher if requested, then quit this app completely and try again." + fi + error "Failed to format the macOS partitions.${command_output:+ ($command_output)}" +} + +darwin_prepare_disk_or_die() { #Input: device, sgdisk path, partition sizes, and partition paths. Performs all privileged disk preparation in one sudo session. + local device="$1" sgdisk_bin="$2" boot_size_mb="$3" install_size_mb="$4" part1="$5" part2="$6" output output_file output_status + output_file="$(mktemp)" || error "Failed to create a disk-preparation log." + if sudo bash -s -- "$device" "$sgdisk_bin" "$boot_size_mb" "$install_size_mb" "$part1" "$part2" > "$output_file" 2>&1 <<'ROOT_SCRIPT' +set -e +device="$1" +sgdisk_bin="$2" +boot_size_mb="$3" +install_size_mb="$4" +part1="$5" +part2="$6" +raw_device="/dev/r${device#/dev/}" + +darwin_unmount_disk_retry() { + local dev="$1" + local i + for i in 1 2 3 4 5; do + /usr/sbin/diskutil unmountDisk force "$dev" >/dev/null 2>&1 && return 0 + sleep 1 + done + /usr/sbin/diskutil unmountDisk force "$dev" +} + +darwin_unmount_disk_retry "$device" +"$sgdisk_bin" --zap-all "$raw_device" +"$sgdisk_bin" -og \ + -n "1:0:+${boot_size_mb}M" -t 1:ef00 -c 1:WOR_BOOT \ + -n "2:0:+${install_size_mb}M" -t 2:0700 -c 2:WOR_INSTALL \ + -A 1:set:63 -A 2:set:63 "$raw_device" +darwin_unmount_disk_retry "$device" +for attempt in $(seq 1 15);do + [ -e "$part1" ] && [ -e "$part2" ] && break + sleep 1 +done +[ -e "$part1" ] && [ -e "$part2" ] +/usr/sbin/diskutil eraseVolume MS-DOS WOR_BOOT "$part1" +/usr/sbin/diskutil eraseVolume ExFAT WOR_INSTALL "$part2" +ROOT_SCRIPT + then + output="$(cat "$output_file")" + rm -f "$output_file" + [ -z "$output" ] || printf '%s\n' "$output" >&2 + return 0 + fi + output_status=$? + output="$(cat "$output_file")" + rm -f "$output_file" + [ -z "$output" ] || printf '%s\n' "$output" >&2 + if printf '%s' "$output" | grep -Eqi 'no password was provided|user canceled|password is required' ;then + error "Administrator authentication was canceled or unavailable while preparing $device. Enter the macOS administrator password in the WoR-Flasher dialog and try again." + fi + if printf '%s' "$output" | grep -qi 'operation not permitted' ;then + open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" >/dev/null 2>&1 & + error "macOS denied disk preparation for $device with 'Operation not permitted'. Enable Full Disk Access for the exact WoR-Flasher.app copy, then quit and reopen it before trying again." + fi + error "Failed to prepare $device.${output:+ ($output)}" +} + +darwin_mount_partition_or_die() { #Input: partition device. Waits for macOS to settle after formatting, then mounts it. + local partition="$1" attempt output='' info='' + for attempt in $(seq 1 30) ;do + if output="$(/usr/sbin/diskutil mount "$partition" 2>&1)";then + return 0 + fi + sleep 1 + done + info="$(/usr/sbin/diskutil info "$partition" 2>&1 || true)" + error "Failed to mount $partition after formatting. macOS may still be settling the partition table or may have refused to mount the new volume.${output:+ Last mount output: $output}${info:+ diskutil info: $info}" +} + +darwin_start_partition_finalizer_or_die() { #Input: device, sgdisk path, optional Pi3 patch. Starts a root helper for late disk writes. + #Without a terminal, sudo caches credentials by parent PID. Launch the external command + #directly so a background shell running the GUI sudo wrapper cannot change that parent. + local device="$1" sgdisk_bin="$2" raw_device="/dev/r${1#/dev/}" pi3_patch="${3:-}" finalizer_dir finalizer_script attempt output worker_status=0 + [ -z "$pi3_patch" ] || { [ -s "$pi3_patch" ] && [ -r "$pi3_patch" ]; } \ + || error "The Pi3 GPT patch is missing or unreadable: $pi3_patch" + finalizer_dir="$(mktemp -d)" || error "Failed to create a partition-finalization workspace." + DARWIN_FINALIZE_GO="$finalizer_dir/go" + DARWIN_FINALIZE_DONE="$finalizer_dir/done" + DARWIN_FINALIZE_LOG="$finalizer_dir/log" + DARWIN_FINALIZE_READY="$finalizer_dir/ready" + DARWIN_PI3_PATCH_GO="$finalizer_dir/patch-go" + DARWIN_PI3_PATCH_DONE="$finalizer_dir/patch-done" + : > "$DARWIN_FINALIZE_DONE" || error "Failed to create the partition-finalization result file." + : > "$DARWIN_FINALIZE_READY" || error "Failed to create the partition-finalization readiness file." + : > "$DARWIN_PI3_PATCH_DONE" || error "Failed to create the Pi3 patch result file." + register_file_cleanup "$DARWIN_FINALIZE_GO" + register_file_cleanup "$DARWIN_FINALIZE_DONE" + register_file_cleanup "$DARWIN_FINALIZE_LOG" + register_file_cleanup "$DARWIN_FINALIZE_READY" + register_file_cleanup "$DARWIN_PI3_PATCH_GO" + register_file_cleanup "$DARWIN_PI3_PATCH_DONE" + #the helper exits when this shell does, so an aborted flash never retags a half-written drive + finalizer_script="$(cat <<'ROOT_SCRIPT' +device="$1" +sgdisk_bin="$2" +raw_device="$3" +go_file="$4" +done_file="$5" +parent_pid="$6" +ready_file="$7" +pi3_patch="$8" +patch_go_file="$9" +patch_done_file="${10}" +patch_completed=0 +printf 'ready\n' > "$ready_file" || exit 1 +while [ ! -e "$go_file" ];do + if ! kill -0 "$parent_pid" 2>/dev/null;then + printf 'Partition finalizer stopped because installer process %s is unavailable.\n' "$parent_pid" >&2 + exit 1 + fi + if [ -n "$pi3_patch" ] && [ "$patch_completed" == 0 ] && [ -e "$patch_go_file" ];then + patch_status=0 + dd if="$pi3_patch" of="$raw_device" conv=fsync || patch_status=$? + printf '%s\n' "$patch_status" > "$patch_done_file" || exit 1 + [ "$patch_status" == 0 ] || exit "$patch_status" + patch_completed=1 + fi + sleep 1 +done +if [ -n "$pi3_patch" ] && [ "$patch_completed" != 1 ];then + printf 'Partition finalizer refused to retag before the Pi3 GPT patch completed.\n' >&2 + exit 1 +fi +finalize_status=0 +/usr/sbin/diskutil unmountDisk force "$device" >/dev/null 2>&1 || true +"$sgdisk_bin" -t 1:ef00 -c 1:WOR_BOOT -t 2:0700 -c 2:WOR_INSTALL "$raw_device" || finalize_status=$? +[ "$finalize_status" != 0 ] || "$sgdisk_bin" -A 1:clear:63 -A 2:clear:63 "$raw_device" || finalize_status=$? +printf '%s\n' "$finalize_status" > "$done_file" +ROOT_SCRIPT + )" + command sudo -n bash -c "$finalizer_script" wor-partition-finalizer "$device" "$sgdisk_bin" "$raw_device" "$DARWIN_FINALIZE_GO" "$DARWIN_FINALIZE_DONE" "$$" "$DARWIN_FINALIZE_READY" "$pi3_patch" "$DARWIN_PI3_PATCH_GO" "$DARWIN_PI3_PATCH_DONE" \ + > "$DARWIN_FINALIZE_LOG" 2>&1 < /dev/null & + DARWIN_FINALIZE_PID=$! + for attempt in $(seq 1 10) ;do + if [ -s "$DARWIN_FINALIZE_READY" ] && kill -0 "$DARWIN_FINALIZE_PID" 2>/dev/null;then + return 0 + fi + kill -0 "$DARWIN_FINALIZE_PID" 2>/dev/null || break + sleep 1 + done + if kill -0 "$DARWIN_FINALIZE_PID" 2>/dev/null;then + error "Partition finalizer did not become ready after $attempt readiness checks for $device." + fi + wait "$DARWIN_FINALIZE_PID" 2>/dev/null || worker_status=$? + output="$(cat "$DARWIN_FINALIZE_LOG" 2>/dev/null)" + error "Partition finalizer failed to start for $device (worker exit $worker_status).${output:+ ($output)}" +} + +darwin_apply_pi3_gpt_patch_or_die() { #Signals the already-authorized worker before written-image verification. + local patch_status output worker_status=0 + : > "$DARWIN_PI3_PATCH_GO" || error "Failed to signal the Pi3 GPT patch on $DEVICE." + while [ ! -s "$DARWIN_PI3_PATCH_DONE" ] && kill -0 "$DARWIN_FINALIZE_PID" 2>/dev/null ;do + sleep 1 + done + patch_status="$(cat "$DARWIN_PI3_PATCH_DONE" 2>/dev/null)" \ + || error "The Pi3 GPT patch did not leave a readable result on $DEVICE." + [ "$patch_status" == 0 ] && return 0 + output="$(cat "$DARWIN_FINALIZE_LOG" 2>/dev/null)" || output='Partition-finalization log is unavailable.' + if [ -z "$patch_status" ] && ! kill -0 "$DARWIN_FINALIZE_PID" 2>/dev/null;then + wait "$DARWIN_FINALIZE_PID" 2>/dev/null || worker_status=$? + error "The Pi3 GPT patch worker exited with status $worker_status without a result on $DEVICE.${output:+ ($output)}" + fi + error "Failed to apply the Pi3 GPT partition-table fix to $DEVICE (status $patch_status).${output:+ ($output)}" +} + +darwin_finalize_partition_types_or_die() { #Signals the pre-authorized root helper to retag the finished media, then waits for it. + local output finalize_status worker_status=0 + : > "$DARWIN_FINALIZE_GO" || error "Failed to signal partition finalization on $DEVICE." + while [ ! -s "$DARWIN_FINALIZE_DONE" ] && kill -0 "$DARWIN_FINALIZE_PID" 2>/dev/null ;do + sleep 1 + done + wait "$DARWIN_FINALIZE_PID" 2>/dev/null || worker_status=$? + output="$(cat "$DARWIN_FINALIZE_LOG" 2>/dev/null)" || output='Partition-finalization log is unavailable.' + [ -z "$output" ] || printf '%s\n' "$output" >&2 + finalize_status="$(cat "$DARWIN_FINALIZE_DONE" 2>/dev/null)" \ + || error "Partition finalizer exited with status $worker_status without a readable result on $DEVICE.${output:+ ($output)}" + [ "$finalize_status" == 0 ] || error "Failed to finalize partition types on $DEVICE.${output:+ ($output)}" +} + +darwin_verify_final_partition_types_or_die() { #Input: boot and install partitions. Verifies final GPT roles after late retagging. + local boot_content install_content + boot_content="$(darwin_device_value "$1" '.Content')" || error "Failed to read final partition type for $1." + install_content="$(darwin_device_value "$2" '.Content')" || error "Failed to read final partition type for $2." + [ "$boot_content" == EFI ] || error "Final media verification failed: partition 1 is '$boot_content', not EFI." + [ "$install_content" == "Microsoft Basic Data" ] || error "Final media verification failed: partition 2 is '$install_content', not Microsoft Basic Data." +} + +darwin_removable_volume_error() { #Input: description and optional command output. Explain macOS removable-volume privacy denial. + local description="$1" user_output="${2:-}" + open "x-apple.systempreferences:com.apple.preference.security?Privacy_FilesAndFolders" >/dev/null 2>&1 \ + || open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" >/dev/null 2>&1 \ + || true + error "Failed to $description: macOS denied removable-volume access to the /bin/bash process that writes the target. In System Settings > Privacy & Security > Files and Folders, allow Removable Volumes for bash when it is listed. If macOS does not offer that narrower permission, open Full Disk Access, click +, press Shift-Command-G, enter /bin/bash, click Open, and enable its toggle. Then quit WoR-Flasher completely and try again.${user_output:+ ($user_output)}" +} + +darwin_require_mounted_volume_access() { #Input: mounted target and description. Fail before copying if macOS TCC blocks removable media. + local target="$1" description="$2" probe="$1/.wor-flasher-write-probe" user_output + rm -f "$probe" 2>/dev/null + if user_output="$(touch "$probe" 2>&1)";then + rm -f "$probe" 2>/dev/null + return 0 + fi + rm -f "$probe" 2>/dev/null + if printf '%s' "$user_output" | grep -Eqi 'operation not permitted|not permitted';then + darwin_removable_volume_error "$description" "$user_output" + fi + error "Failed to $description.${user_output:+ ($user_output)}" +} + +darwin_report_copy_failure() { #Input: mounted target, operation description, and copy mode. Distinguishes volume access, sudo, and copy failures without prompting. + local target="$1" description="$2" mode="${3:-user}" probe="$1/.wor-flasher-write-probe" user_output sudo_output copy_context + copy_context="${COPY_WITH_PROGRESS_ERROR:+ Last copy error: $COPY_WITH_PROGRESS_ERROR}" + if printf '%s' "$COPY_WITH_PROGRESS_ERROR" | grep -qi 'input/output error' ;then + error "Failed to $description: the target volume reported an Input/output error while writing. This usually means the USB drive, SD card, adapter, or filesystem stopped accepting writes. Reinsert the target media and try again; if it repeats, use a different drive/card/adapter.$copy_context" + fi + rm -f "$probe" 2>/dev/null + if [ "$mode" == admin ];then + sudo_output="$(sudo -n touch "$probe" 2>&1)" + if sudo -n rm -f "$probe" >/dev/null 2>&1;then + error "Failed to $description: the administrator probe works, but the privileged directory copy was denied. macOS may still be blocking ditto access to this removable volume; allow bash/ditto in Privacy & Security, then quit and reopen WoR-Flasher.$copy_context" + fi + if printf '%s' "$sudo_output" | grep -Eqi 'password is required|no password|terminal is required';then + error "Failed to $description: sudo is not authenticated. Complete the WoR-Flasher administrator-password dialog and retry." + fi + error "Failed to $description.$copy_context${sudo_output:+ ($sudo_output)}" + fi + if user_output="$(touch "$probe" 2>&1)";then + rm -f "$probe" 2>/dev/null + error "Failed to $description: the volume is writable, but the normal file copy was denied.$copy_context Check that the source files are readable and that macOS has allowed bash to access the removable volume." + fi + rm -f "$probe" 2>/dev/null + if printf '%s' "$user_output" | grep -Eqi 'operation not permitted|not permitted';then + if is_macos;then + darwin_removable_volume_error "$description" "$user_output" + fi + sudo_output="$(sudo -n touch "$probe" 2>&1)" + if sudo -n rm -f "$probe" >/dev/null 2>&1;then + error "Failed to $description: normal access to the removable volume was denied, but administrator access works. Choose Allow when macOS asks whether bash may access the removable volume, then quit and reopen WoR-Flasher." + fi + if printf '%s' "$sudo_output" | grep -Eqi 'password is required|no password|terminal is required';then + error "Failed to $description: macOS denied normal access to the removable volume, and sudo is not authenticated. Complete the WoR-Flasher administrator-password dialog and choose Allow if macOS asks whether bash may access the removable volume." + fi + error "Failed to $description: macOS denied normal access to the removable volume.$copy_context${user_output:+ ($user_output)}" + fi + #rm -f succeeds on a path that was never created, so the touch output is the only proof it worked + sudo_output="$(sudo -n touch "$probe" 2>&1)" + if [ -z "$sudo_output" ] && sudo -n rm -f "$probe" >/dev/null 2>&1;then + error "Failed to $description: administrator access works, but the normal file copy failed.$copy_context${user_output:+ ($user_output)}" + fi + if printf '%s' "$user_output$sudo_output" | grep -qi 'no such file or directory';then + error "Failed to $description: the target volume is no longer mounted. macOS unmounted it during the copy. Reinsert the drive and run $WOR_APP_TITLE again.$copy_context" + fi + if printf '%s' "$sudo_output" | grep -Eqi 'operation not permitted|not permitted';then + error "Failed to $description: macOS denied access to the removable volume. Choose Allow when macOS asks whether bash may access the removable volume, then quit and reopen WoR-Flasher." + fi + if printf '%s' "$sudo_output" | grep -Eqi 'password is required|no password|terminal is required';then + error "Failed to $description: the normal copy was denied and sudo is not authenticated. Complete the WoR-Flasher administrator-password dialog; if macOS also asks whether bash may access the removable volume, choose Allow, then retry." + fi + error "Failed to $description.$copy_context${sudo_output:+ ($sudo_output)}" +} + +darwin_format_or_die() { #Input: human-readable description, then the format command and its args. Detects a Full Disk Access denial specifically and opens the settings pane for it, instead of just failing. + local description="$1" command_output command_status fda_app launcher_hint launcher_guidance='' + shift + command_output="$("$@" 2>&1 >/dev/null)" + command_status=$? + [ "$command_status" == 0 ] && return 0 + if printf '%s' "$command_output" | grep -qi 'operation not permitted' ;then + fda_app="$(darwin_fda_app_name)" + launcher_hint="$(darwin_fda_launcher_hint)" + if [ -n "$launcher_hint" ] && [ "$launcher_hint" != "$fda_app" ];then + launcher_guidance=" If that exact app is already enabled and this still fails, also enable $launcher_hint; macOS can attribute shell-script app disk access to the launcher/interpreter chain." + fi + #jump straight to the right settings pane instead of making the user hunt for it + open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles" >/dev/null 2>&1 & + #macOS cannot grant this itself; only a person clicking the toggle satisfies TCC, and the app + #is often not even listed yet, so the + button is the actual first step + error "Failed to $description: macOS blocked writing to the raw disk device with 'Operation not permitted'. In the System Settings window that just opened (Privacy & Security > Full Disk Access), find $fda_app and turn it on. If it is not in the list yet, click the + button (bottom-left of the list) and add that exact app; if an older WoR-Flasher entry is already there, remove it and add this copy again.$launcher_guidance Then quit this app completely and try again." + fi + error "Failed to $description.${command_output:+ ($command_output)}" +} + +darwin_mount_point_or_die() { #Input: partition. Output: its mount point now, remounting first if macOS dropped it. + #FSKit can unmount a freshly formatted FAT/exFAT volume part-way through the copy, so a mount point + #resolved earlier goes stale and the next write fails with "No such file or directory" + local partition="$1" mount_point registered + mount_point="$(darwin_device_value "$partition" '.MountPoint')" + if [ -z "$mount_point" ] || [ ! -d "$mount_point" ];then + darwin_mount_partition_or_die "$partition" + mount_point="$(darwin_device_value "$partition" '.MountPoint')" + fi + [ -n "$mount_point" ] && [ -d "$mount_point" ] \ + || error "Failed to determine the mount point for $partition after remounting it." + for registered in "${CLEANUP_MOUNTS[@]}" ;do + [ "$registered" == "$mount_point" ] && { printf '%s\n' "$mount_point"; return 0; } + done + register_mount_cleanup "$mount_point" + printf '%s\n' "$mount_point" +} + +darwin_flash_device() { + is_safe_target_device "$DEVICE" || error "Refusing to overwrite $DEVICE. Choose an external, physical, writable whole disk that is not the current boot drive." + local boot_payload_kb boot_size_mb install_size_mb sgdisk_bin raw_device copy_attempt pi3_patch='' + sgdisk_bin="$(command -v sgdisk)" || error "sgdisk is required to partition $DEVICE correctly. Install it with 'brew install gptfdisk', then run this script again." + #GUI mode authenticated at startup, while a dialog could still reach the front; prompting from here + #would put it behind the progress window, where it can never be answered + if [ "$RUN_MODE" != gui ] && ! command sudo -n -v >/dev/null 2>&1 && ! sudo -v >/dev/null 2>&1;then + error "Administrator authentication failed or was canceled. Enter the correct macOS password and try again." + fi + raw_device="/dev/r${DEVICE#/dev/}" + boot_payload_kb="$(du -sk "$PWD/$winfiles/bootpart" "$PWD/peinstaller/winpe/2" "$PWD/peinstaller/efi" 2>/dev/null | awk '{total += $1} END {print total + 0}')" + boot_size_mb=$((boot_payload_kb / 1024 + 512)) + [ "$boot_size_mb" -lt 1536 ] && boot_size_mb=1536 + [ "$CAN_INSTALL_ON_SAME_DRIVE" == 1 ] && install_size_mb=18000 || install_size_mb=6000 + phase "Partitioning and formatting $DEVICE" + printf ' There is no turning back now.\n' 1>&2 + status " Creating WOR_BOOT (${boot_size_mb} MB) and WOR_INSTALL (${install_size_mb} MB)" + PART1="${DEVICE}s1" + PART2="${DEVICE}s2" + [ "$RPI_MODEL" != 3 ] || pi3_patch="$PWD/peinstaller/pi3/gptpatch.img" + #Authenticate in this shell before helpers capture their output in subshells; otherwise the + #GUI prompt state does not survive to the final privileged disk operation. + sudo -v || error "Administrator authentication failed or was canceled. Enter the macOS password in the WoR-Flasher dialog and try again." + darwin_start_partition_finalizer_or_die "$DEVICE" "$sgdisk_bin" "$pi3_patch" + emit_gui_progress "DISK_WRITE"$'\t'"1"$'\t'"$DEVICE" + darwin_prepare_disk_or_die "$DEVICE" "$sgdisk_bin" "$boot_size_mb" "$install_size_mb" "$PART1" "$PART2" + gui_start_sudo_keepalive + + darwin_mount_partition_or_die "$PART1" + darwin_mount_partition_or_die "$PART2" + boot_mount="$(darwin_device_value "$PART1" '.MountPoint')" || error "Failed to determine the boot partition mount point." + win_mount="$(darwin_device_value "$PART2" '.MountPoint')" || error "Failed to determine the installation partition mount point." + register_mount_cleanup "$boot_mount" + register_mount_cleanup "$win_mount" + if is_macos;then + status " Checking removable-volume access" + darwin_require_mounted_volume_access "$boot_mount" "write to $boot_mount" + darwin_require_mounted_volume_access "$win_mount" "write to $win_mount" + fi + + phase "Copying files to $DEVICE:" + report_copy_task 0 "Startup environment" + boot_mount="$(darwin_mount_point_or_die "$PART1")" + copy_startup_environment_with_progress "$PWD/$winfiles/bootpart" "$boot_mount" device \ + || darwin_report_copy_failure "$boot_mount" "copy startup files to $boot_mount" user + report_copy_task 15 "Installation files" + #fskitd unmounts an idle exFAT volume on its own ("Unmounting /Volumes/WOR_INSTALL how 02"), so + #WOR_INSTALL can disappear while boot.wim is still being written to WOR_BOOT. Remount and retry. + for copy_attempt in 1 2 3 ;do + win_mount="$(darwin_mount_point_or_die "$PART2")" + copy_local_file_with_progress install.wim "$PWD/$winfiles/install.wim" "$win_mount/install.wim" && break + #the volume is still mounted, so this is a real copy failure rather than a vanished mount point + { [ -d "$win_mount" ] || [ "$copy_attempt" == 3 ]; } \ + && darwin_report_copy_failure "$win_mount" "copy installation files to $win_mount" user + status " $PART2 was unmounted mid-copy; remounting and retrying" + done + report_copy_task 30 "EFI files" + boot_mount="$(darwin_mount_point_or_die "$PART1")" + mkdir -p "$boot_mount/efi" \ + && cp -R "$PWD/peinstaller/efi/." "$boot_mount/efi" \ + || darwin_report_copy_failure "$boot_mount" "copy EFI files to $boot_mount" user + report_copy_task 45 "PE installer" + configure_pe_settings_ini + configure_pe_prefinalize + boot_mount="$(darwin_mount_point_or_die "$PART1")" + wimupdate "$boot_mount/sources/boot.wim" 2 --command="add peinstaller/winpe/2 /" || error "The wimupdate command failed to add $PWD/peinstaller to boot.wim" + + if [ "$RPI_MODEL" == 5 ];then + report_copy_task 60 "ARM64 drivers" + : > "$PWD/critical" + wimupdate "$boot_mount/sources/boot.wim" 2 --command="add critical /drivers/critical" || error "The wimupdate command failed to add $PWD/critical to boot.wim" + rm "$PWD/critical" + else + report_copy_task 60 "ARM64 drivers" + wimupdate "$boot_mount/sources/boot.wim" 2 --command="add driverpackage /drivers" || error "The wimupdate command failed to add $PWD/driverpackage to boot.wim" + fi + + report_copy_task 75 "Windows Setup configuration" + boot_mount="$(darwin_mount_point_or_die "$PART1")" + win_mount="$(darwin_mount_point_or_die "$PART2")" + install_windows_setup_configuration "$boot_mount" "$win_mount" || error "Failed to install the Windows Setup configuration." + + report_copy_task 90 "UEFI firmware" + cp -RX "$PWD/pi${RPI_MODEL}-uefipackage"/* "$boot_mount" || error "Failed to copy UEFI firmware to $boot_mount" + [ -z "$CONFIG_TXT" ] || [ "$APPLY_CUSTOM_CONFIG_TXT" != 1 ] || printf '%s\n' "$CONFIG_TXT" > "$boot_mount/config.txt" + [ "$RPI_MODEL" != 3 ] || darwin_apply_pi3_gpt_patch_or_die + + if [ "$SKIP_IMAGE_VERIFICATION" == 1 ];then + echo_red "Skipping written-image verification (SKIP_IMAGE_VERIFICATION=1). This is not recommended." + else + boot_mount="$(darwin_mount_point_or_die "$PART1")" + win_mount="$(darwin_mount_point_or_die "$PART2")" + verify_written_image "$DEVICE" "$PART1" "$PART2" "$boot_mount" "$win_mount" "$PWD/$winfiles/install.wim" + fi + #Retag last: macOS will not remount WOR_BOOT once partition 1 is EFI, and clearing GPT attribute + #bit 63 is what lets WoR-PE assign drive letters to both partitions. + darwin_finalize_partition_types_or_die + darwin_verify_final_partition_types_or_die "$PART1" "$PART2" + diskutil unmountDisk "$DEVICE" || echo_red "Warning: failed to unmount $DEVICE" + diskutil eject "$DEVICE" || echo_red "Warning: failed to eject $DEVICE" + emit_gui_progress "DISK_WRITE"$'\t'"0"$'\t'"$DEVICE" + phase "$WOR_APP_TITLE script has completed." + cli_pause +} + +get_file_size() { #Input: file. Output: size in bytes, portable across GNU and BSD userlands + wc -c < "$1" | tr -d ' ' +} + +get_esd_catalog_entry() { #Input: catalog text, language. Output: the language's first file entry before the Languages section. + awk -v language="$2" ' + /<Languages>/ { exit } + index($0, "<LanguageCode>" language) == 1 { found = 1 } + found { print } + found && /^<\/File>$/ { exit } + ' <<<"$1" +} + +set_hash_command() { #Input: SHA bit length. Sets WOR_HASH_COMMAND to the host's hashing tool and its arguments. + #GNU coreutils ships shaNsum; BSD userlands ship only shasum -a N + if command -v "sha${1}sum" >/dev/null ;then + WOR_HASH_COMMAND=("sha${1}sum") + else + WOR_HASH_COMMAND=(shasum -a "$1") + fi +} + +hash_file() { #Input: SHA bit length, file. Output: hash + set_hash_command "$1" + "${WOR_HASH_COMMAND[@]}" "$2" | awk '{print $1}' +} + +sha1_file() { #Input: file. Output: SHA1 hash + hash_file 1 "$1" +} + +remark_pe_cache() { #Re-records the manifest after editing cached PE payload, or the next run treats the cache as corrupt and re-downloads. + local token + token="$(cat "$PWD/peinstaller/.wor-flasher-version" 2>/dev/null)" + [ -n "$token" ] && mark_cache "$PWD/peinstaller" "$token" + return 0 +} + +configure_pe_settings_ini() { #Sets HideEmptyDrives in the cached WoR-PE settings.ini before it's added to boot.wim. + local settings_ini="$PWD/peinstaller/winpe/2/settings.ini" tmp_ini + [ -f "$settings_ini" ] || return 0 + tmp_ini="$(mktemp)" || return 1 + if grep -q '^HideEmptyDrives=' "$settings_ini";then + awk -v val="$HIDE_EMPTY_DRIVES" '{ if ($0 ~ /^HideEmptyDrives=/) print "HideEmptyDrives=" val; else print }' "$settings_ini" > "$tmp_ini" + else + cat "$settings_ini" > "$tmp_ini" + printf 'HideEmptyDrives=%s\n' "$HIDE_EMPTY_DRIVES" >> "$tmp_ini" + fi + mv "$tmp_ini" "$settings_ini" + remark_pe_cache + return 0 +} + +configure_pe_prefinalize() { #Stages the answer file and WoR-PE's prefinalize hook, which is what actually delivers it. + #WoR-PE applies install.wim with DISM instead of running Windows Setup's media flow, so nothing ever + #performs the implicit answer-file search that would find Autounattend.xml at the root of the media. + #Its documented prefinalize.cmd hook runs with the applied Windows partition still mounted, which is + #the only point where the answer file can be put somewhere the installed OS will read it. + local app_dir="$PWD/peinstaller/winpe/2" scripts_dir answer_needed=0 shell_needed=0 + [ -d "$app_dir" ] || return 0 + scripts_dir="$app_dir/scripts" + + #a stale hook left in the cache would keep applying settings the user has since turned off + rm -rf "$scripts_dir" + if unattend_xml >/dev/null 2>&1;then answer_needed=1; fi + if [ "$RPI_MODEL" == 4 ] && [ "$PI4_UEFI_SHELL_UNLOCK" == 1 ];then shell_needed=1; fi + [ "$answer_needed" == 1 ] || [ "$shell_needed" == 1 ] || { remark_pe_cache; return 0; } + + mkdir -p "$scripts_dir" || return 1 + if [ "$answer_needed" == 1 ];then + unattend_xml > "$scripts_dir/unattend.xml" || return 1 + fi + #the specialize action runs on the installed OS, so its script has to travel there too + if [ "$RPI_MODEL" == 4 ] && [ "$PI4_AUTO_DISABLE_3GB" == 1 ];then + read_config_template pi4-ram-unlock.ps1 > "$scripts_dir/Pi4Disable3GB.ps1" || return 1 + fi + if [ "$shell_needed" == 1 ];then + prepare_uefi_shell || return 1 + cp "$PWD/uefi-shell/Shell.efi" "$scripts_dir/Shell.efi" || return 1 + fi + #batch files are parsed by cmd.exe, which needs CRLF line endings + read_config_template prefinalize.cmd | sed 's/$/\r/' > "$scripts_dir/prefinalize.cmd" || return 1 + remark_pe_cache + return 0 +} + +prepare_uefi_shell() { #Downloads and verifies the ARM64 UEFI Shell used by the one-time Pi 4 handoff. + local package="$PWD/uefi-shell-package.zip" extracted="$PWD/uefi-shell" temporary expected + expected="$WOR_DEFAULT_UEFI_SHELL_PACKAGE_SHA256" + mkdir -p "$extracted" || return 1 + if [ -s "$extracted/Shell.efi" ] && [ "$(sha256_file "$extracted/Shell.efi")" == "$(cat "$extracted/Shell.efi.sha256" 2>/dev/null)" ];then + return 0 + fi + temporary="$(mktemp)" || return 1 + wget -qO "$temporary" "$WOR_DEFAULT_UEFI_SHELL_PACKAGE_URL" || { rm -f "$temporary"; return 1; } + [ "$(sha256_file "$temporary")" == "$expected" ] || { rm -f "$temporary"; return 1; } + unzip -p "$temporary" 'ShellBinPkg/UefiShell/AArch64/Shell.efi' > "$extracted/Shell.efi" || { rm -f "$temporary"; return 1; } + rm -f "$temporary" + sha256_file "$extracted/Shell.efi" > "$extracted/Shell.efi.sha256" +} + +sha256_file() { #Input: file. Output: SHA256 hash + hash_file 256 "$1" +} + +copy_with_progress() { #Input: write mode (sudo or local), progress label, source file, destination file + local mode="$1" label="$2" source="$3" destination="$4" size destination_dir destination_size progress_error_file copy_error_output copy_status + local -a writer pipe_status + COPY_WITH_PROGRESS_ERROR="" + copy_error_output="" + if [ ! -f "$source" ];then + COPY_WITH_PROGRESS_ERROR="source file does not exist: $source" + echo_red "$COPY_WITH_PROGRESS_ERROR" + return 1 + fi + if [ ! -r "$source" ];then + COPY_WITH_PROGRESS_ERROR="source file is not readable: $source" + echo_red "$COPY_WITH_PROGRESS_ERROR" + return 1 + fi + size="$(get_file_size "$source")" || return 1 + destination_dir="$(dirname "$destination")" + if [ ! -d "$destination_dir" ];then + COPY_WITH_PROGRESS_ERROR="destination directory does not exist: $destination_dir" + echo_red "$COPY_WITH_PROGRESS_ERROR" + return 1 + fi + rm -f "$destination" 2>/dev/null || true + if [ "$mode" == sudo ];then + writer=(sudo tee) + with_progress_capture pv -f -N "$label" -s "$size" "$source" | "${writer[@]}" "$destination" >/dev/null + pipe_status=("${PIPESTATUS[@]}") + else + progress_error_file="$(mktemp "${TMPDIR:-/tmp}/wor-copy-progress.XXXXXX")" || return 1 + with_progress_capture pv -f -N "$label" -s "$size" "$source" > "$destination" 2> >(tee "$progress_error_file" >&2) + copy_status=$? + copy_error_output="$(cat "$progress_error_file" 2>/dev/null)" + rm -f "$progress_error_file" + pipe_status=("$copy_status" 0) + fi + if [ "${pipe_status[0]}" != 0 ];then + if printf '%s' "$copy_error_output" | grep -qi 'input/output error' ;then + COPY_WITH_PROGRESS_ERROR="target reported Input/output error while copying $source to $destination" + else + COPY_WITH_PROGRESS_ERROR="failed to copy $source to $destination" + fi + echo_red "$COPY_WITH_PROGRESS_ERROR" + return 1 + fi + if [ "${pipe_status[1]}" != 0 ];then + COPY_WITH_PROGRESS_ERROR="failed to write destination file: $destination" + echo_red "$COPY_WITH_PROGRESS_ERROR" + return 1 + fi + destination_size="$(get_file_size "$destination")" || destination_size=0 + if [ "$destination_size" != "$size" ];then + COPY_WITH_PROGRESS_ERROR="short copy: wrote $destination_size of $size bytes to $destination" + echo_red "$COPY_WITH_PROGRESS_ERROR" + rm -f "$destination" 2>/dev/null || true + return 1 + fi + COPY_WITH_PROGRESS_ERROR="" +} + +copy_file_with_progress() { #Input: progress label, source file, destination file. Writes as root. + copy_with_progress sudo "$@" +} + +copy_local_file_with_progress() { #Input: progress label, source file, destination file + copy_with_progress local "$@" +} + +copy_mounted_file_with_progress() { #Input: progress label, source file, destination file. macOS mounts removable volumes as the user; Linux mounts them as root. + if is_macos;then + copy_with_progress local "$@" + else + copy_with_progress sudo "$@" + fi +} + +copy_startup_environment_with_progress() { #Input: source boot-media root, destination root, optional local flag + local source="$1" destination="$2" mode="${3:-device}" + if [ "$mode" == local ];then + cp -R "$source/boot" "$source/efi" "$destination" || return 1 + mkdir -p "$destination/sources" || return 1 + copy_local_file_with_progress boot.wim "$source/sources/boot.wim" "$destination/sources/boot.wim" + else + if is_macos;then + mkdir -p "$destination/boot" "$destination/efi" || return 1 + cp -R "$source/boot/." "$destination/boot" || return 1 + cp -R "$source/efi/." "$destination/efi" || return 1 + else + sudo cp -R "$source/boot" "$source/efi" "$destination" || return 1 + fi + if is_macos;then + mkdir -p "$destination/sources" || return 1 + else + sudo mkdir -p "$destination/sources" || return 1 + fi + copy_mounted_file_with_progress boot.wim "$source/sources/boot.wim" "$destination/sources/boot.wim" fi } -error() { #Input: error message - echo -e "\e[91m$1\e[0m" 1>&2 - [ "$RUN_MODE" == gui ] && gui_error_dialog "$1" - exit 1 +mounted_test() { #Input: test arguments. macOS removable volumes are user-owned; Linux mountpoints are root-owned. + if is_macos;then + test "$@" + else + sudo test "$@" + fi } -status() { #blue text to indicate what is happening - - #detect if a flag was passed, and if so, pass it on to the echo command - if [[ "$1" == '-'* ]] && [ ! -z "$2" ];then - echo -e $1 "\e[96m$2\e[0m" 1>&2 +mounted_cmp() { #Input: cmp arguments for mounted target files. + if is_macos;then + cmp "$@" else - echo -e "\e[96m$1\e[0m" 1>&2 + sudo cmp "$@" fi } -echo_green() { #announce the success of a major action - echo -e "\e[92m$1\e[0m" 1>&2 +mounted_grep() { #Input: grep arguments for mounted target files. + if is_macos;then + grep "$@" + else + sudo grep "$@" + fi } -echo_red() { #announce the failure of a nonfatal action - echo -e "\e[91m$1\e[0m" 1>&2 +mounted_wimdir() { #Input: wimdir arguments for mounted target files. + if is_macos;then + wimdir "$@" + else + sudo wimdir "$@" + fi } -resolve_path() { #Input: path. Output: absolute path, using GNU or BSD tools when available - [ -z "$1" ] && return 1 - if command -v realpath >/dev/null ;then - realpath "$1" && return 0 - fi - if readlink -f "$1" >/dev/null 2>&1 ;then - readlink -f "$1" && return 0 - fi - if [ -d "$1" ];then - (cd "$1" && pwd -P) +mounted_wimverify() { #Input: wimverify arguments for mounted target files. + if is_macos;then + with_progress_capture wimverify "$@" else - local dir - local base - dir="$(dirname "$1")" - base="$(basename "$1")" - printf '%s/%s\n' "$(cd "$dir" && pwd -P)" "$base" + with_progress_capture sudo wimverify "$@" fi } -require_linux_host() { - [ "$(uname -s)" == Linux ] && return 0 - error "WoR-Flasher currently supports Linux hosts only. -This host is $(uname -s). The flashing path needs Linux block-device and filesystem tools such as lsblk, findmnt, parted, mkfs.fat, mkfs.exfat, mount.exfat-fuse, modprobe and the /sys device tree. -On macOS, use a Linux VM or Raspberry Pi OS/Debian/Ubuntu host with USB drive passthrough." +hash_file_with_progress() { #Input: SHA bit length, progress label, file. Output: hash + local bits="$1" label="$2" file="$3" size + size="$(get_file_size "$file")" || return 1 + set_hash_command "$bits" + (set -o pipefail; with_progress_capture pv -f -N "$label" -s "$size" "$file" | "${WOR_HASH_COMMAND[@]}" | awk '{print $1}') } -get_file_size() { #Input: file. Output: size in bytes, portable across GNU and BSD userlands - wc -c < "$1" | tr -d ' ' +sha1_file_with_progress() { #Input: progress label, file. Output: SHA1 hash + hash_file_with_progress 1 "$1" "$2" } -sha1_file() { #Input: file. Output: SHA1 hash - if command -v sha1sum >/dev/null ;then - sha1sum "$1" | awk '{print $1}' +sha256_file_with_progress() { #Input: progress label, file. Output: SHA256 hash + hash_file_with_progress 256 "$1" "$2" +} + +verify_written_image() { #Input: device, boot partition, install partition, boot mount, install mount, source install.wim + local device="$1" boot_partition="$2" install_partition="$3" boot_mount="$4" install_mount="$5" source_install="$6" + local partition_count boot_content install_content boot_filesystem install_filesystem boot_label install_label source_hash written_hash + local device_size install_offset install_size trailing_free geometry + + phase "Verifying the written image" + report_verification_task 0 "Checking partition layout" + sync + + if is_macos ;then + partition_count="$(darwin_plist_json diskutil list -plist "$device" | jq '[.AllDisksAndPartitions[0].Partitions[]?] | length')" + boot_content="$(darwin_device_value "$boot_partition" '.Content')" + install_content="$(darwin_device_value "$install_partition" '.Content')" + boot_filesystem="$(darwin_device_value "$boot_partition" '.FilesystemType')" + install_filesystem="$(darwin_device_value "$install_partition" '.FilesystemType')" + boot_label="$(darwin_device_value "$boot_partition" '.VolumeName')" + install_label="$(darwin_device_value "$install_partition" '.VolumeName')" + device_size="$(darwin_device_value "$device" '.TotalSize // .Size')" + install_offset="$(darwin_device_value "$install_partition" '.PartitionMapPartitionOffset')" + install_size="$(darwin_device_value "$install_partition" '.Size')" else - shasum -a 1 "$1" | awk '{print $1}' + geometry="$(sudo parted -ms "$device" unit B print)" || error "Written-image verification failed: could not read partition geometry from $device." + partition_count="$(awk -F: '$1 ~ /^[0-9]+$/ {count++} END {print count + 0}' <<<"$geometry")" + boot_content="$(awk -F: '$1 == 1 {print $7}' <<<"$geometry")" + install_content="$(awk -F: '$1 == 2 {print $7}' <<<"$geometry")" + device_size="$(awk -F: 'NR == 2 {sub(/B$/, "", $2); print $2}' <<<"$geometry")" + install_offset="$(awk -F: '$1 == 2 {sub(/B$/, "", $2); print $2}' <<<"$geometry")" + install_size="$(awk -F: '$1 == 2 {sub(/B$/, "", $4); print $4}' <<<"$geometry")" + boot_filesystem="$(sudo blkid -s TYPE -o value "$boot_partition")" + install_filesystem="$(sudo blkid -s TYPE -o value "$install_partition")" + boot_label="$(sudo blkid -s LABEL -o value "$boot_partition")" + install_label="$(sudo blkid -s LABEL -o value "$install_partition")" fi -} -sha256_file() { #Input: file. Output: SHA256 hash - if command -v sha256sum >/dev/null ;then - sha256sum "$1" | awk '{print $1}' + [ "$partition_count" == 2 ] || error "Written-image verification failed: expected exactly 2 partitions on $device, found $partition_count." + if is_macos;then + [ "$boot_content" == EFI ] || [ "$boot_content" == "Microsoft Basic Data" ] \ + || error "Written-image verification failed: partition 1 is '$boot_content', not a mountable FAT boot partition." else - shasum -a 256 "$1" | awk '{print $1}' + [ "$boot_content" == EFI ] || [[ "$boot_content" == *esp* ]] || error "Written-image verification failed: partition 1 is not an EFI System Partition." + fi + [ "$install_content" == "Microsoft Basic Data" ] || [[ "$install_content" == *msftdata* ]] || error "Written-image verification failed: partition 2 is not Microsoft Basic Data." + [ "$boot_filesystem" == msdos ] || [ "$boot_filesystem" == vfat ] || error "Written-image verification failed: partition 1 is not FAT32." + [ "$install_filesystem" == exfat ] || error "Written-image verification failed: partition 2 is not ExFAT." + [ "$boot_label" == WOR_BOOT ] || error "Written-image verification failed: partition 1 is labeled '$boot_label', not 'WOR_BOOT'." + [ "$install_label" == WOR_INSTALL ] || error "Written-image verification failed: partition 2 is labeled '$install_label', not 'WOR_INSTALL'." + [[ "$device_size" =~ ^[0-9]+$ ]] && [[ "$install_offset" =~ ^[0-9]+$ ]] && [[ "$install_size" =~ ^[0-9]+$ ]] \ + || error "Written-image verification failed: could not determine unallocated space on $device." + trailing_free=$((device_size - install_offset - install_size)) + [ "$trailing_free" -ge $((1024*1024*1024)) ] \ + || error "Written-image verification failed: less than 1 GiB remains unallocated for the Windows target partition." + + report_verification_task 15 "Checking required boot artifacts" + status " Checking required boot artifacts" + mounted_test -s "$boot_mount/RPI_EFI.fd" || error "Written-image verification failed: RPI_EFI.fd is missing or empty." + case "$RPI_MODEL" in + 3) + mounted_test -s "$boot_mount/bootcode.bin" || error "Written-image verification failed: bootcode.bin is missing or empty." + mounted_test -s "$boot_mount/start.elf" || error "Written-image verification failed: start.elf is missing or empty." + ;; + 4) + mounted_test -s "$boot_mount/start4.elf" || error "Written-image verification failed: start4.elf is missing or empty." + mounted_test -s "$boot_mount/fixup4.dat" || error "Written-image verification failed: fixup4.dat is missing or empty." + ;; + 5) + mounted_test -s "$boot_mount/bcm2712-rpi-5-b.dtb" || error "Written-image verification failed: bcm2712-rpi-5-b.dtb is missing or empty." + ;; + esac + mounted_test -s "$boot_mount/EFI/BOOT/BOOTAA64.EFI" || error "Written-image verification failed: EFI/BOOT/BOOTAA64.EFI is missing or empty." + mounted_test -s "$boot_mount/EFI/Microsoft/Boot/bcd" || error "Written-image verification failed: EFI/Microsoft/Boot/bcd is missing or empty." + mounted_test -s "$boot_mount/sources/boot.wim" || error "Written-image verification failed: sources/boot.wim is missing or empty." + mounted_test -s "$install_mount/install.wim" || error "Written-image verification failed: install.wim is missing or empty." + if [ "$OOBE_NETWORK_BYPASS" == 1 ] || { [ "$RPI_MODEL" == 4 ] && [ "$PI4_AUTO_DISABLE_3GB" == 1 ]; };then + mounted_cmp -s "$boot_mount/Autounattend.xml" "$install_mount/Autounattend.xml" \ + || error "Written-image verification failed: the answer file differs between the media partitions." + fi + if [ "$OOBE_NETWORK_BYPASS" == 1 ];then + mounted_grep -qF '<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>' "$boot_mount/Autounattend.xml" \ + || error "Written-image verification failed: the OOBE network bypass is missing from the boot partition." fi + if [ "$RPI_MODEL" == 4 ] && [ "$PI4_AUTO_DISABLE_3GB" == 1 ];then + mounted_grep -qF '<WillReboot>Always</WillReboot>' "$boot_mount/Autounattend.xml" \ + || error "Written-image verification failed: the automatic Pi 4 RAM unlock is missing from the answer file." + mounted_grep -qF 'SetFirmwareEnvironmentVariableEx' "$boot_mount/Pi4Disable3GB.ps1" \ + || error "Written-image verification failed: the automatic Pi 4 RAM unlock script is missing." + mounted_cmp -s "$boot_mount/Pi4Disable3GB.ps1" "$install_mount/Pi4Disable3GB.ps1" \ + || error "Written-image verification failed: the Pi 4 RAM unlock script differs between the media partitions." + fi + + if [ "$RPI_MODEL" == 4 ];then + report_verification_task 30 "Checking Pi 4 Setup drivers" + status " Checking Pi 4 Setup drivers" + mounted_wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/bcmgenet/bcmgenet.inf >/dev/null \ + || error "Written-image verification failed: the Pi 4 Ethernet driver is missing from boot.wim." + mounted_wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/mcci_dwchsotg/mcci_dwchsotg_hcd.inf >/dev/null \ + || error "Written-image verification failed: the Pi 4 USB host driver is missing from boot.wim." + mounted_wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/mcci_dwchsotg/mcci_dwchsotg_hub.inf >/dev/null \ + || error "Written-image verification failed: the Pi 4 USB hub driver is missing from boot.wim." + mounted_wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/rpiuxflt/rpiuxflt.inf >/dev/null \ + || error "Written-image verification failed: the Pi 4 USB DMA filter driver is missing from boot.wim." + fi + + report_verification_task 45 "Verifying boot.wim integrity" + status " Verifying boot.wim integrity" + mounted_wimverify "$boot_mount/sources/boot.wim" || error "Written-image verification failed: boot.wim is invalid or corrupted." + report_verification_task 60 "Verifying install.wim integrity" + status " Verifying install.wim integrity" + mounted_wimverify "$install_mount/install.wim" || error "Written-image verification failed: install.wim is invalid or corrupted." + + report_verification_task 75 "Hashing source install.wim" + status " Comparing install.wim with its source" + source_hash="$(sha256_file_with_progress source "$source_install")" || error "Written-image verification failed: could not hash source install.wim." + report_verification_task 88 "Hashing written install.wim" + written_hash="$(sha256_file_with_progress written "$install_mount/install.wim")" || error "Written-image verification failed: could not hash written install.wim." + [ "$source_hash" == "$written_hash" ] || error "Written-image verification failed: install.wim does not match its source." + report_verification_task 100 "Written image verified" + echo_green "Written image verified successfully" } wget() { #Intercept all wget commands. When possible, uses aria2c. local file='' local url='' - #determine the download manager to use local use=aria2c - #determine if being run silently (if the '-q' flag was passed) local quiet=0 - - #use these flags for aria2c local check_cert=true + local -a aria2_flags [ "$VERIFY_TLS" == 0 ] && check_cert=false - aria2_flags=(-x 16 -s 16 --max-tries=10 --retry-wait=30 --max-file-not-found=5 --http-no-cache=true --check-certificate=$check_cert \ + aria2_flags=(-x 16 -s 16 --max-tries=10 --retry-wait=30 --max-file-not-found=5 --http-no-cache=true "--check-certificate=$check_cert" \ --allow-overwrite=true --auto-file-renaming=false --remove-control-file --auto-save-interval=0 \ --console-log-level=error --show-console-readout=false --summary-interval=1) - #convert wget arguments to newline-separated list local IFS=$'\n' - local opts="$(IFS=$'\n'; echo "$*")" + local opts + opts="$(IFS=$'\n'; echo "$*")" for opt in $opts ;do - - #check if this argument to wget begins with '--' if [[ "$opt" == '--'* ]];then if [ "$opt" == '--quiet' ];then quiet=1 @@ -185,8 +1605,11 @@ wget() { #Intercept all wget commands. When possible, uses aria2c. #now, perform the download using the chosen method if [ "$use" == wget ];then #run the true wget binary with all this function's args - - command wget --progress=bar:force:noscroll "$@" + if [ "$RUN_MODE" == gui ];then + command wget --progress=bar:force:noscroll "$@" 2> >(gui_percent_stream) + else + command wget --progress=bar:force:noscroll "$@" + fi local exitcode=$? elif [ "$use" == aria2c ];then @@ -200,10 +1623,12 @@ wget() { #Intercept all wget commands. When possible, uses aria2c. #suppress output if -q flag passed if [ "$quiet" == 1 ];then aria2c --quiet "${aria2_flags[@]}" - local exitcode=$? + local exitcode + exitcode=$? else #run aria2c without quietness and format download-progress output - local terminal_width="$(tput cols 2>/dev/null || :)" + local terminal_width + terminal_width="$(tput cols 2>/dev/null || :)" [[ "$terminal_width" =~ ^[0-9]+$ ]] || terminal_width=80 #run aria2c and reduce its output. @@ -232,6 +1657,7 @@ wget() { #Intercept all wget commands. When possible, uses aria2c. #get progress percentage from aria2c output percent="$(grep -o '(.*)' <<<"$line" | tr -d '()%')" + [ -n "$percent" ] && emit_gui_substep "$percent" #echo "percent: $percent" #echo "available_width: $available_width" @@ -239,8 +1665,10 @@ wget() { #Intercept all wget commands. When possible, uses aria2c. #determine how many characters in progress bar to light up progress_characters=$(((percent*available_width)/100)) - statsline+="\e[92m\e[1m$(for ((i=0; i<$progress_characters; i++)); do printf "—"; done)\e[39m" # other possible characters to put here: █🭸 - echo -ne "\e[0K${statsline}\r\033\e[0m" 1>&2 #clear and print over previous line + progress_bar="$(for ((i=0; i<$progress_characters; i++)); do printf "—"; done)" + printf -v colored_progress_bar '\033[92m\033[1m%s\033[39m' "$progress_bar" + statsline+="$colored_progress_bar" # other possible characters to put here: █🭸 + printf '\033[0K%s\r\033[0m' "$statsline" 1>&2 #clear and print over previous line #reduce the line and print over the previous line, like: "1.1GiB/1.1GiB(98%) DL:18MiB" #echo "$line" | awk '{print $2 " " $4 " " substr($5, 1, length($5)-1)}' | tr -d '\n' @@ -265,7 +1693,7 @@ wget() { #Intercept all wget commands. When possible, uses aria2c. #display "done" message if [ "$use" == aria2c ];then local progress_characters=$(($terminal_width - 5)) - echo -e "\e[0KDone \e[92m\e[1m$(for ((i=0; i<$progress_characters; i++)); do printf "—"; done)\e[39m\e[0m" 1>&2 #clear and print over previous line + printf '\033[0KDone \033[92m\033[1m%s\033[39m\033[0m\n' "$(for ((i=0; i<$progress_characters; i++)); do printf "—"; done)" 1>&2 #clear and print over previous line else echo echo_green "Done" 1>&2 @@ -284,12 +1712,13 @@ cache_downloader() { #returns contents of url, using cached output from a previo [ -z "$1" ] && error "cache_downloader(): no url specified!" [ -z "$DIRECTORY" ] && error "cache_downloader(): DIRECTORY variable not set!" local output + mkdir -p "$WOR_CACHE_DIR" output="$(wget -qO- "$1")" if [ -z "$output" ];then - output="$(cat "$DIRECTORY/cache/$(basename "$1")" 2>/dev/null)" || error "Unable to download $1" + output="$(cat "$WOR_CACHE_DIR/$(basename "$1")" 2>/dev/null)" || error "Unable to download $1" else - echo "$output" > "$DIRECTORY/cache/$(basename "$1")" + echo "$output" > "$WOR_CACHE_DIR/$(basename "$1")" fi echo "$output" } @@ -327,28 +1756,30 @@ package_installed() { #exit 0 if $1 package is installed, otherwise exit 1 grep "^Package: $package$" /var/lib/dpkg/status -A 1 | tail -n 1 | grep -q 'Status: install ok installed' } -install_packages() { #input: space-separated list of apt packages to install - [ -z "$1" ] && error "install_packages(): requires a list of apt packages to install" - local dependencies="$1" - local install_list='' +install_packages() { #input: one or more apt packages to install + [ "$#" -eq 0 ] && error "install_packages(): requires a list of apt packages to install" + if is_macos ;then + command -v brew >/dev/null || error "macOS support requires Homebrew. Install it from https://brew.sh, then run this script again." + local formula + for formula in "${WOR_MACOS_BREW_FORMULAE[@]}"; do + brew list --formula "$formula" >/dev/null 2>&1 || brew install "$formula" || error "Failed to install Homebrew dependency '$formula'." + done + return 0 + fi + local install_list=() local package - local IFS=' ' - for package in $dependencies ;do + for package in "$@" ;do if ! package_installed "$package" ;then - #if the currently-checked package is not installed, add it to the list of packages to install - if [ -z "$install_list" ];then - install_list="$package" - else - install_list="$install_list $package" - fi + #Keep package names as distinct arguments. + install_list+=("$package") fi done - if [ ! -z "$install_list" ];then - status "Installing packages: $install_list" + if [ "${#install_list[@]}" -gt 0 ];then + status "Installing packages: ${install_list[*]}" sudo apt update || error "Failed to run 'sudo apt update'! This is not an error in WoR-flasher." - sudo apt install -yf $install_list --no-install-recommends || error "Failed to install dependency packages! This is not an error in WoR-flasher." + sudo apt install -yf "${install_list[@]}" --no-install-recommends || error "Failed to install dependency packages! This is not an error in WoR-flasher." fi } @@ -356,7 +1787,12 @@ get_partition() { #Input: device & partition number. Output: partition /dev entr [ -z "$1" ] && error "get_partition(): no /dev device specified as"' $1' [ -z "$2" ] && error "get_partition(): no partition number specified as"' $2' [ ! -b "$1" ] && error "get_partition(): $1 is not a valid block device!" - command -v lsblk >/dev/null || error "get_partition(): lsblk is required. WoR-Flasher's flashing path currently supports Linux hosts only." + if is_macos ;then + [ "$2" == all ] && error "get_partition(): macOS does not support listing all partitions through this helper." + darwin_plist_json diskutil list -plist "$1" | jq -er ".AllDisksAndPartitions[0].Partitions[$(($2 - 1))].DeviceIdentifier" | sed 's+^+/dev/+' + return + fi + command -v lsblk >/dev/null || error "get_partition(): lsblk is required." if [ "$2" == 'all' ];then #special mode: return every partition if $2 is 'all' @@ -373,6 +1809,11 @@ get_device_name() { #get human-readable name of storage device: manufacturer and [ -z "$1" ] && error "get_device_name(): requires an argument" [ ! -b "$1" ] && error "get_device_name(): Specified block device '$1' does not exist!" + if is_macos ;then + darwin_device_value "$1" '.MediaName // .DeviceIdentifier' + return + fi + sys_path="$(find /sys/devices/platform -type d -name "$(basename "$1")")" #sys_path may be: /sys/devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb2/2-2/2-2:1.0/host0/target0:0:0/0:0:0:0/block/sda @@ -401,7 +1842,11 @@ get_device_name() { #get human-readable name of storage device: manufacturer and } get_size_raw() { #Input: device. Output: total size of device in bytes - command -v lsblk >/dev/null || error "get_size_raw(): lsblk is required. WoR-Flasher's flashing path currently supports Linux hosts only." + if is_macos ;then + darwin_device_value "$1" '.DiskSize // .TotalSize // .Size' + return + fi + command -v lsblk >/dev/null || error "get_size_raw(): lsblk is required." lsblk -b --output SIZE -n -d "$1" } @@ -439,7 +1884,7 @@ validate_install_mode() { #Input: drive capability. Validates CAN_INSTALL_ON_SAM fi } -get_space_free() { #Input: folder to check. Output: show many bytes can fit before the disk is full +get_space_free() { #Input: folder to check. Output: how many bytes can fit before the disk is full. if df -B 1 "$1" --output=avail >/dev/null 2>&1 ;then df -B 1 "$1" --output=avail | tail -1 | tr -d ' ' else @@ -447,25 +1892,85 @@ get_space_free() { #Input: folder to check. Output: show many bytes can fit befo fi } +require_free_space() { #Input: minimum required bytes, path. Abort with a clear error if the local disk is too full. + local required_bytes="$1" + local target_path="${2:-$DL_DIR}" + local free_bytes required_human free_human shortage_human + + [ -n "$required_bytes" ] || return 0 + free_bytes="$(get_space_free "$target_path")" || return 0 + if [ "$free_bytes" -lt "$required_bytes" ];then + required_human="$(human_size "$required_bytes")" + free_human="$(human_size "$free_bytes")" + shortage_human="$(human_size $((required_bytes - free_bytes)))" + error "Not enough free space in $target_path to download the required Windows files. +Required for this download set: $required_human +Available now: $free_human +Please free up at least $shortage_human and try again, or choose a different download directory." + fi +} + +cache_manifest() { #Input: folder. Output: stable SHA-256 manifest of cached payload files. + ( + local file hash + cd "$1" || return 1 + find . -type f ! -name '.wor-flasher-version' ! -name '.wor-flasher-sha256' ! -name '.wor-flasher-sha256.tmp' -print \ + | LC_ALL=C sort \ + | while IFS= read -r file ;do + hash="$(sha256_file "$file")" || exit 1 + [ -n "$hash" ] || exit 1 + printf '%s %s\n' "$hash" "$file" + done + ) +} + +cache_contents_are_current() { #Input: folder. Exit 0 if its payload matches the recorded manifest. + [ -s "$1/.wor-flasher-sha256" ] || return 1 + cache_manifest "$1" | cmp -s - "$1/.wor-flasher-sha256" +} + cache_is_current() { #Input: folder, version token. Exit 0 if the cached folder can be reused. local folder="$1" local token="$2" [ ! -d "$folder" ] && return 1 [ "$USE_CACHE" == 2 ] && return 0 #trust the cache without checking anything - [ "$USE_CACHE" == 1 ] && [ "$(cat "${folder}/.wor-flasher-version" 2>/dev/null)" == "$token" ] && return 0 + [ "$USE_CACHE" == 1 ] \ + && [ "$(cat "${folder}/.wor-flasher-version" 2>/dev/null)" == "$token" ] \ + && cache_contents_are_current "$folder" \ + && return 0 return 1 } -mark_cache() { #Input: folder, version token. Records what was downloaded so cache_is_current() can compare later. - echo "$2" > "${1}/.wor-flasher-version" +mark_cache() { #Input: folder, version token. Records payload integrity and source version. + local manifest="${1}/.wor-flasher-sha256" + rm -f "${1}/.wor-flasher-version" + cache_manifest "$1" > "${manifest}.tmp" || { + rm -f "${manifest}.tmp" + return 1 + } + mv "${manifest}.tmp" "$manifest" || return 1 + printf '%s\n' "$2" > "${1}/.wor-flasher-version" } -list_devs() { #Output: human-readable, colorized list of valid block devices to write to. Omits /dev/loop* and the root device. Returns code 1 if no drives found +list_dev_paths() { #Output: whole-disk paths that may be written to. Omits /dev/loop* and the root device. + if is_macos ;then + darwin_list_device_paths + return + fi [ -z "$ROOT_DEV" ] && detect_root_dev + lsblk -I 8,179,259 -dno NAME | sed 's+^+/dev/+g' | grep -v loop | grep -vx "$ROOT_DEV" +} + +list_devs() { #Output: human-readable, colorized list of valid block devices to write to. Omits /dev/loop* and the root device. Returns code 1 if no drives found + if is_macos ;then + darwin_list_devices + return + fi local IFS=$'\n' - local exitcode=1 - for device in $(lsblk -I 8,179,259 -dno NAME | sed 's+^+/dev/+g' | grep -v loop | grep -vx "$ROOT_DEV") ;do - if [ $(lsblk -dnbo SIZE "$device") -gt 0 ];then + local device size exitcode=1 + for device in $(list_dev_paths) ;do + size="$(lsblk -dnbo SIZE "$device")" || continue + if [[ "$size" =~ ^[0-9]+$ ]] && [ "$size" -gt 0 ];then echo -e "\e[1m\e[97m${device}\e[0m - \e[92m$(lsblk -dno SIZE "$device")B\e[0m - \e[36m$(get_device_name "$device")\e[0m" exitcode=0 fi @@ -474,7 +1979,11 @@ list_devs() { #Output: human-readable, colorized list of valid block devices to } detect_root_dev() { #Set ROOT_DEV to the Linux block device backing / - command -v findmnt >/dev/null || error "findmnt is required to detect the current boot drive. WoR-Flasher's flashing path currently supports Linux hosts only." + if is_macos ;then + ROOT_DEV="/dev/$(darwin_device_value / '.ParentWholeDisk // .DeviceIdentifier')" + return + fi + command -v findmnt >/dev/null || error "findmnt is required to detect the current boot drive." command -v lsblk >/dev/null || error "lsblk is required to detect the current boot drive. WoR-Flasher's flashing path currently supports Linux hosts only." local root_source local root_parent @@ -493,7 +2002,24 @@ lt-lt:Lithuanian (Lithuania)\nlv-lv:Latvian (Latvia)\nnb-no:Norwegian Bokmål (N sl-si:Slovenian (Slovenia)\nsr-latn-rs:Serbian (Latin, Serbia)\nsv-se:Swedish (Sweden)\nth-th:Thai (Thailand)\ntr-tr:Turkish (Turkey)\nuk-ua:Ukrainian (Ukraine)\nzh-cn:Chinese (Simplified, China)\nzh-tw:Chinese (Traditional, Taiwan)" } +default_win_lang() { #Output: the host locale's matching Windows language code, or en-us. + local host_locale language_code + if is_macos ;then + host_locale="$(defaults read -g AppleLocale 2>/dev/null)" + else + host_locale="${LC_ALL:-${LC_MESSAGES:-${LANG:-}}}" + fi + language_code="$(printf '%s' "$host_locale" | sed 's/@.*//' | sed 's/\..*//' | tr '_' '-' | tr '[:upper:]' '[:lower:]')" + if list_langs | awk -F: '{print $1}' | grep -qFx "$language_code" ;then + printf '%s\n' "$language_code" + else + printf '%s\n' 'en-us' + fi +} + list_bids() { #input: '10' or '11', Output: build IDs for ESD releases. Format: "$BID ($date)" + local LC_ALL=C + export LC_ALL if [ -z "$versions" ];then #Get list of major Windows ESD versions from worproject.com versions="$(cache_downloader 'https://worproject.com/dldserv/esd/getversions.php')" || return 1 @@ -514,7 +2040,8 @@ list_bids() { #input: '10' or '11', Output: build IDs for ESD releases. Format: cpu_supports_bid() { #input: build id. Exit 0 if the target Pi's CPU can run this Windows build. #Pi3 (Cortex-A53) and Pi4 (Cortex-A72) are ARMv8.0. Builds past ARMV80_MAX_BUILD need ARMv8.1 atomics. - local major="$(echo "$1" | awk -F. '{print $1}')" + local major + major="$(echo "$1" | awk -F. '{print $1}')" [ -z "$major" ] && return 0 if [ "$RPI_MODEL" == 3 ] || [ "$RPI_MODEL" == 4 ];then [ "$major" -gt "$ARMV80_MAX_BUILD" ] && return 1 @@ -542,17 +2069,258 @@ get_os_name() { #input: build id, Output: either "Windows 10 build $BID" or "Win fi } +uefi_pinned_version() { #Output: the pinned UEFI firmware version for the selected RPI_MODEL. + case "$RPI_MODEL" in + 3) echo "$UEFI_VER_PI3" ;; + 4) echo "$UEFI_VER_PI4" ;; + 5) echo "$UEFI_VER_PI5" ;; + esac +} + +cache_mode_label() { #Input: USE_CACHE value. Output: how that mode reads on a summary or confirmation screen. + case "$1" in + 0) echo 'Re-download everything' ;; + 2) echo 'Trust the cache without checking' ;; + *) echo 'Reuse when they still match' ;; + esac +} + +install_mode_label() { #Input: CAN_INSTALL_ON_SAME_DRIVE value. Output: how that mode reads on a summary screen. + case "$1" in + 1) echo 'Install Windows onto this drive' ;; + *) echo 'Recovery drive for another >16 GB drive' ;; + esac +} + +default_config_txt() { #Input: Pi model. Output: that model's shipped config.txt, formatted as CONFIG_TXT holds it. + case "$1" in + 3 | 4 | 5) printf '\n\n%s' "$(read_config_template "pi$1.config.txt")" ;; + esac +} + +set_default_config_txt() { #Sets CONFIG_TXT from the selected model's shipped template, unless the caller already supplied one. + local previous_default + if [ -n "$CONFIG_TXT" ];then + #stepping back and choosing another model must not flash the previous model's boot config, but a + #config.txt the user has edited is theirs and is never overwritten + [ -n "${CONFIG_TXT_MODEL:-}" ] || return 0 + [ "$CONFIG_TXT_MODEL" == "$RPI_MODEL" ] && return 0 + previous_default="$(default_config_txt "$CONFIG_TXT_MODEL")" + [ "$CONFIG_TXT" == "$previous_default" ] || return 0 + fi + CONFIG_TXT="$(default_config_txt "$RPI_MODEL")" + [ -n "$CONFIG_TXT" ] && CONFIG_TXT_MODEL="$RPI_MODEL" + return 0 +} + +describe_device() { #Input: device. Output: the path plus its size and model when those can be read. + local size name detail + [ -z "$1" ] && return 0 + size="$(get_size_raw "$1" 2>/dev/null)" + name="$(get_device_name "$1" 2>/dev/null)" + #a summary line must never be the thing that stops a run, so unreadable details are simply omitted + if [ -n "$size" ] && [ "$size" -gt 0 ] 2>/dev/null ;then + detail="$(human_size "$size")" + fi + [ -n "$name" ] && detail="${detail:+$detail }$name" + [ -n "$detail" ] && printf '%s (%s)\n' "$1" "$detail" || printf '%s\n' "$1" +} + +wor_log_file() { #Output: where this run's log is kept. Resolved on use, since the Linux GUI can still change DL_DIR. + printf '%s\n' "${WOR_LOG_FILE:-$DL_DIR/$WOR_DEFAULT_LOG_DIRNAME/wor-flasher-$WOR_RUN_ID.log}" +} + +wor_last_log_file() { #Output: stable support path pointing at the most recent saved log. + printf '%s\n' "$DL_DIR/last-run.log" +} + +settings_summary() { #Output: tab-separated "label<TAB>value" lines describing this run. One source of truth for the CLI banner and both GUI confirmation screens. + local display_win_lang + display_win_lang="$(windows_locale_from_language_code "$WIN_LANG")" + printf '%s version\t%s\n' "$WOR_FLASHER_NAME" "$WOR_FLASHER_VERSION" + printf 'Target drive\t%s\n' "$(describe_device "$DEVICE")" + printf 'Target hardware\tRaspberry Pi %s\n' "$RPI_MODEL" + printf 'Operating system %s\n' "$(get_os_name "$BID" | sed "s/ build / ($display_win_lang) arm64 build /g")" + printf 'Installation mode\t%s\n' "$(install_mode_label "$CAN_INSTALL_ON_SAME_DRIVE")" + [ -n "$SOURCE_FILE" ] && printf 'Windows source\t%s\n' "$SOURCE_FILE" + printf 'Offline OOBE\t%s\n' "$([ "$OOBE_NETWORK_BYPASS" == 1 ] && echo 'Allowed' || echo 'Disabled')" + printf 'Windows local account\t%s\n' "$([ "$WINDOWS_ACCOUNT_SETUP" == 1 ] && printf 'Administrator (%s)' "$WINDOWS_ACCOUNT_USERNAME" || echo 'Windows setup will ask')" + printf 'Windows keyboard and regional settings\t%s\n' "$([ "$WINDOWS_LOCALE_SETUP" == 1 ] && echo "$WINDOWS_LOCALE" || echo 'Windows setup defaults')" + [ "$RPI_MODEL" == 4 ] && printf 'Pi 4 RAM unlock\t%s\n' "$([ "$PI4_AUTO_DISABLE_3GB" == 1 ] && echo 'Enabled' || echo 'Disabled')" + printf 'UEFI firmware\t%s\n' "$([ "$UEFI_USE_LATEST" == 1 ] && echo 'Latest' || echo "Pinned ($(uefi_pinned_version))")" + printf 'Windows ARM64 drivers\t%s\n' "$([ "$DRIVERS_USE_LATEST" == 1 ] && echo 'Latest' || echo "Pinned ($DRIVER_VER)")" + printf 'Custom config.txt\t%s\n' "$([ "$APPLY_CUSTOM_CONFIG_TXT" == 1 ] && echo 'Applied' || echo "Using the firmware default")" + printf 'Hide empty drives\t%s\n' "$([ "$HIDE_EMPTY_DRIVES" == 1 ] && echo 'Yes' || echo 'No')" + printf 'Verify written image\t%s\n' "$([ "$SKIP_IMAGE_VERIFICATION" == 1 ] && echo 'No (skipped)' || echo 'Yes')" + printf 'Downloaded files\t%s\n' "$(cache_mode_label "$USE_CACHE")" + printf 'Dry run\t%s\n' "$([ "$DRY_RUN" == 1 ] && echo 'Yes (no changes will be written)' || echo 'No')" + printf 'Download directory\t%s\n' "$DL_DIR" + printf 'Log file\t%s\n' "$(wor_log_file)" + return 0 +} + +validate_iso_file() { #Input: path to a Windows ISO. Output: why it is unusable, or nothing. Exit 0 when it can be used. + local iso="$1" + if [ ! -f "$iso" ];then + echo "This file does not exist. Check spelling and try again." + elif [[ "$iso" != *'.ISO' ]] && [[ "$iso" != *'.iso' ]];then + echo "This file does not have a .ISO file extension." + elif [ "$(get_file_size "$iso")" -lt $((3*1024*1024*1024)) ];then + echo "This file is smaller than 3GB and is probably incomplete." + else + return 0 + fi + return 1 +} + +bid_from_iso_name() { #Input: ISO path. Output: the Windows build number in its filename, if there is one. + basename "$1" | tr '_ -' '\n' | grep -E -m 1 '^[0-9]{5}' +} + +lang_from_iso_name() { #Input: ISO path. Output: the Windows language code in its filename, if there is one. + basename "$1" | tr '_ ' '\n' | grep -io -m 1 "$(list_langs | awk -F: '{print $1}' | tr '\n' ';' | sed 's/;/\\|/g' | sed 's/\\|$/\n/g')" | tr '[A-Z]' '[a-z]' +} + +is_known_win_lang() { #Input: language code. Exit 0 if it appears in the published language list. + list_langs | awk -F: '{print $1}' | grep -qFx "$1" +} + +list_langs_preferred() { #Output: list_langs with en-us first, then the other English variants, then the rest. + list_langs | grep '^en-us:' + list_langs | grep '^en-' | grep -v '^en-us:' + list_langs | grep -v '^en-' +} + +windows_locale_from_language_code() { #Input: Windows language code. Output: Windows locale casing. + awk -F- '{ + out = tolower($1) + for (i = 2; i <= NF; i++) { + part = tolower($i) + if (length(part) == 4) { + part = toupper(substr(part, 1, 1)) substr(part, 2) + } else { + part = toupper(part) + } + out = out "-" part + } + print out + }' <<<"$1" +} + +default_windows_locale() { #Output: the current host locale in Windows casing, or en-US. + windows_locale_from_language_code "$(default_win_lang)" +} + +list_wim_locale_codes() { #Input: install.wim. Output: locale codes declared by the image, when wimlib exposes them. + local image="$1" + [ -f "$image" ] && command -v wiminfo >/dev/null 2>&1 && command -v iconv >/dev/null 2>&1 || return 1 + #wiminfo emits one-line UTF-16LE XML. Feeding those NUL-bearing bytes to BSD sed fails under + #the macOS UTF-8 locale, and matching the whole line is ambiguous when every image repeats tags. + wiminfo --xml "$image" 2>/dev/null \ + | iconv -f UTF-16LE -t UTF-8 \ + | tr '<>' '\n\n' \ + | awk 'previous == "LANGUAGE" || previous == "DEFAULT" {print tolower($0)} {previous=$0}' \ + | grep -E '^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$' \ + | awk '!seen[$0]++' +} + +list_windows_locale_options() { #Output: tab-separated locale and label choices for Windows regional settings. + local image code name locale + { + for image in \ + "$DL_DIR/winfiles_from_iso_${BID}_${WIN_LANG}/install.wim" \ + "$DL_DIR/winfiles_${BID}_${WIN_LANG}/install.wim" ;do + [ -f "$image" ] || continue + while IFS= read -r code ;do + [ -n "$code" ] || continue + locale="$(windows_locale_from_language_code "$code")" + name="$(list_langs | awk -F: -v wanted="$code" '$1 == wanted {print $2; exit}')" + [ -n "$name" ] || name="$locale" + printf '%s\t%s\n' "$locale" "$name ($locale)" + done < <(list_wim_locale_codes "$image") + done + list_langs_preferred | while IFS=: read -r code name ;do + locale="$(windows_locale_from_language_code "$code")" + printf '%s\t%s (%s)\n' "$locale" "$name" "$locale" + done + } | awk -F'\t' '!seen[$1]++' +} + +list_cached_winfiles() { #Input: optional directory, default DL_DIR. Output: names of winfiles folders that finished extracting. + find "${1:-$DL_DIR}" -maxdepth 2 -type f -name 'alldone' 2>/dev/null \ + | sed 's+/alldone$++' | xargs -I{} basename {} 2>/dev/null \ + | grep -E '^winfiles(_from_iso)?_' | sort -r +} + +bid_from_winfiles_dir() { #Input: winfiles folder name. Output: the build ID it holds. + echo "$1" | sed 's/^winfiles_from_iso_//g; s/^winfiles_//g' | awk -F_ '{print $1}' +} + +lang_from_winfiles_dir() { #Input: winfiles folder name. Output: the Windows language it holds. + echo "$1" | sed 's/^winfiles_from_iso_//g; s/^winfiles_//g' | awk -F_ '{print $2}' +} + +flash_files_already_prepared() { #Exit 0 if a previous run finished every download and extraction the disk step needs. Sets winfiles. + #Deliberately stricter than the per-component cache checks: this decides whether the whole + #preparation half of the run may be skipped, so every file the disk step reads must exist. + [ -n "$BID" ] && [ -n "$WIN_LANG" ] && [ -n "$RPI_MODEL" ] || return 1 + if [ -f "$PWD/winfiles_from_iso_${BID}_${WIN_LANG}/alldone" ];then + winfiles="winfiles_from_iso_${BID}_${WIN_LANG}" + elif [ -f "$PWD/winfiles_${BID}_${WIN_LANG}/alldone" ];then + winfiles="winfiles_${BID}_${WIN_LANG}" + else + return 1 + fi + [ -s "$PWD/$winfiles/install.wim" ] || return 1 + [ -d "$PWD/$winfiles/bootpart" ] || return 1 + [ -d "$PWD/peinstaller/efi" ] || return 1 + [ -d "$PWD/peinstaller/winpe/2" ] || return 1 + [ -d "$PWD/pi${RPI_MODEL}-uefipackage" ] || return 1 + [ "$RPI_MODEL" == 5 ] || [ -d "$PWD/driverpackage" ] || return 1 + return 0 +} + +settings_summary_plain() { #Input: optional printf format taking the label then the value. Output: one plain line per setting. + local format="${1:-%s %s\n}" label value + settings_summary | while IFS=$'\t' read -r label value ;do + #shellcheck disable=SC2059 + printf "$format" "$label:" "$value" + done +} + +settings_summary_markup() { #Output: one pango-markup line per setting, for a yad --text window. + local label value + settings_summary | while IFS=$'\t' read -r label value ;do + #yad renders its text as markup, so a value carrying <, > or & would corrupt the whole window + printf -- '- %s: <b>%s</b>\n' "$label" "$(printf '%s' "$value" | sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g')" + done +} + +#Every setting a GUI front-end collects and the installer subprocess has to see. Kept in one place so +#the macOS and Linux front-ends can never drift into exporting different subsets of the same run. +WOR_INSTALLER_SETTINGS=(DIRECTORY DL_DIR RPI_MODEL BID WIN_LANG DEVICE CAN_INSTALL_ON_SAME_DRIVE SOURCE_FILE + CONFIG_TXT APPLY_CUSTOM_CONFIG_TXT PI4_AUTO_DISABLE_3GB OOBE_NETWORK_BYPASS WINDOWS_ACCOUNT_SETUP WINDOWS_ACCOUNT_USERNAME WINDOWS_ACCOUNT_PASSWORD WINDOWS_LOCALE_SETUP WINDOWS_LOCALE UEFI_USE_LATEST DRIVERS_USE_LATEST + PI4_UEFI_SHELL_UNLOCK SKIP_IMAGE_VERIFICATION HIDE_EMPTY_DRIVES USE_CACHE DRY_RUN WOR_APP_TITLE WOR_RUN_ID) + +export_installer_settings() { #Exports every collected setting, so the installer subprocess runs exactly what was confirmed. + export "${WOR_INSTALLER_SETTINGS[@]}" +} + +gui_preauthenticate() { #Signals that setup/preparation may start; sudo is requested at the destructive boundary. + #The front-end waits for this before opening its progress window. Authentication is intentionally + #deferred until Step 5 so a long download/prep phase cannot expire the only password prompt. + if [ -n "$WOR_GUI_AUTH_MARKER" ];then + touch "$WOR_GUI_AUTH_MARKER" 2>/dev/null + sync 2>/dev/null || true + fi +} + setup() { #run safety checks and install packages require_linux_host - #check for internet connection - echo -n "Checking for internet connection... " - local errors - errors="$(command wget --spider github.com 2>&1)" - if [ $? != 0 ];then - error "No internet connection!\ngithub.com failed to respond.\nErrors: $errors" + if is_macos ;then + require_macos_tools fi - echo Done if [ "$(id -u)" == 0 ];then status "WoR-Flasher is not designed to be run as root.\nDoing so is known to cause problems." @@ -569,146 +2337,453 @@ setup() { #run safety checks and install packages fi #Make sure modules exist for the running kernel - otherwise a kernel upgrade occurred and the user needs to reboot. See https://github.com/Botspot/wor-flasher/issues/35 - if [ ! -d /lib/modules/$(uname -r) ];then + if [ "$HOST_OS" == Linux ] && [ ! -d "/lib/modules/$(uname -r)" ];then error "The running kernel ($(uname -r)) does not match any directory in /lib/modules. Usually this means you have not yet rebooted since upgrading the kernel. Try rebooting. If this error persists, contact Botspot - the WoR-flasher developer." fi - #install dependencies + #install dependencies before using them for setup checks if [ "$SKIP_PACKAGE_INSTALL" != 1 ];then - install_packages 'yad aria2 cabextract wimtools chntpw genisoimage exfat-fuse wget udftools bc parted dosfstools unzip git' || exit 1 + install_packages "${WOR_LINUX_PACKAGES[@]}" || exit 1 #install exfat partition manipulation utility. exfatprogs replaces exfat-utils, but they cannot both be installed at once. - if package_available exfatprogs && ! package_installed exfat-utils ;then - install_packages exfatprogs || exit 1 - else - install_packages exfat-utils || exit 1 + if [ "$HOST_OS" == Linux ];then + if package_available exfatprogs && ! package_installed exfat-utils ;then + install_packages exfatprogs || exit 1 + else + install_packages exfat-utils || exit 1 + fi fi fi + if ! command -v wget >/dev/null ;then + error "Missing required dependency: wget. +WoR-Flasher needs wget to verify GitHub connectivity and download files. +Install wget, or leave SKIP_PACKAGE_INSTALL unset so WoR-Flasher can install dependencies automatically." + fi + + #check for internet connection + echo -n "Checking for internet connection... " + local errors + errors="$(command wget --spider github.com 2>&1)" + if [ $? != 0 ];then + error "Could not reach github.com. +Check your internet connection, DNS/proxy settings, or firewall, then run this script again. +Errors: $errors" + fi + echo Done + [ -z "$ROOT_DEV" ] && detect_root_dev return 0 } +load_config_json() { #Input: optional config file path. Output: populates unset environment variables. + local config_file="${1:-${WOR_CONFIG_FILE:-}}" + if [ -z "$config_file" ];then + if [ -n "${DIRECTORY:-}" ] && [ -f "$DIRECTORY/config.json" ];then + config_file="$DIRECTORY/config.json" + elif [ -f "$PWD/config.json" ];then + config_file="$PWD/config.json" + elif [ -n "${DIRECTORY:-}" ] && [ -f "$DIRECTORY/config-templates/config.json" ];then + config_file="$DIRECTORY/config-templates/config.json" + elif [ -f "$PWD/config-templates/config.json" ];then + config_file="$PWD/config-templates/config.json" + fi + fi + [ -n "$config_file" ] && [ -f "$config_file" ] || return 0 + command -v jq >/dev/null 2>&1 || return 0 + + val_from_json() { + jq -r "$1" "$config_file" 2>/dev/null + } + + set_if_unset() { + local var_name="$1" + local jq_expr="$2" + local current="${!var_name:-}" + if [ -z "$current" ];then + local json_val + json_val="$(val_from_json "$jq_expr")" + if [ -n "$json_val" ] && [ "$json_val" != "null" ];then + printf -v "$var_name" '%s' "$json_val" + fi + fi + } + + set_bool_if_unset() { + local var_name="$1" + local jq_expr="$2" + local current="${!var_name:-}" + if [ -z "$current" ];then + local json_val + json_val="$(val_from_json "$jq_expr")" + if [ "$json_val" == "true" ] || [ "$json_val" == "1" ];then + printf -v "$var_name" '%s' "1" + elif [ "$json_val" == "false" ] || [ "$json_val" == "0" ];then + printf -v "$var_name" '%s' "0" + fi + fi + } + + set_check_updates_if_unset() { + local current="${NO_UPDATE:-}" + if [ -z "$current" ];then + local json_val env_val="${CHECK_FOR_UPDATES:-}" + if [ "$env_val" == "true" ] || [ "$env_val" == "1" ];then + printf -v NO_UPDATE '%s' "0" + return + elif [ "$env_val" == "false" ] || [ "$env_val" == "0" ];then + printf -v NO_UPDATE '%s' "1" + return + fi + json_val="$(val_from_json '.execution.checkForUpdates // .checkForUpdates // .CHECK_FOR_UPDATES // empty')" + if [ "$json_val" == "true" ] || [ "$json_val" == "1" ];then + printf -v NO_UPDATE '%s' "0" + elif [ "$json_val" == "false" ] || [ "$json_val" == "0" ];then + printf -v NO_UPDATE '%s' "1" + fi + fi + } + + set_if_unset "RPI_MODEL" '.target.rpiModel // .rpiModel // .RPI_MODEL // empty' + set_if_unset "DEVICE" '.target.device // .device // .DEVICE // empty' + set_bool_if_unset "CAN_INSTALL_ON_SAME_DRIVE" '.target.canInstallOnSameDrive // .canInstallOnSameDrive // .CAN_INSTALL_ON_SAME_DRIVE // empty' + set_if_unset "WIN_LANG" '.media.winLang // .winLang // .WIN_LANG // empty' + set_if_unset "BID" '.media.bid // .bid // .BID // empty' + set_if_unset "SOURCE_FILE" '.media.sourceFile // .sourceFile // .SOURCE_FILE // empty' + set_if_unset "DL_DIR" '.media.downloadDir // .downloadDir // .DL_DIR // empty' + set_if_unset "USE_CACHE" '.media.useCache // .useCache // .USE_CACHE // empty' + + set_bool_if_unset "APPLY_CUSTOM_CONFIG_TXT" '.customization.applyCustomConfigTxt // .applyCustomConfigTxt // .APPLY_CUSTOM_CONFIG_TXT // empty' + set_if_unset "CONFIG_TXT" '.customization.configTxt // .configTxt // .CONFIG_TXT // empty' + set_bool_if_unset "OOBE_NETWORK_BYPASS" '.customization.oobeNetworkBypass // .oobeNetworkBypass // .OOBE_NETWORK_BYPASS // empty' + set_bool_if_unset "PI4_AUTO_DISABLE_3GB" '.customization.pi4AutoDisable3Gb // .pi4AutoDisable3Gb // .PI4_AUTO_DISABLE_3GB // empty' + set_bool_if_unset "PI4_UEFI_SHELL_UNLOCK" '.customization.pi4UefiShellUnlock // .pi4UefiShellUnlock // .PI4_UEFI_SHELL_UNLOCK // empty' + set_bool_if_unset "UEFI_USE_LATEST" '.customization.uefiUseLatest // .uefiUseLatest // .UEFI_USE_LATEST // empty' + set_bool_if_unset "DRIVERS_USE_LATEST" '.customization.driversUseLatest // .driversUseLatest // .DRIVERS_USE_LATEST // empty' + set_bool_if_unset "HIDE_EMPTY_DRIVES" '.customization.hideEmptyDrives // .hideEmptyDrives // .HIDE_EMPTY_DRIVES // empty' + + set_bool_if_unset "WINDOWS_ACCOUNT_SETUP" '.userAccount.setupAccount // .windowsAccountSetup // .WINDOWS_ACCOUNT_SETUP // empty' + set_if_unset "WINDOWS_ACCOUNT_USERNAME" '.userAccount.username // .windowsAccountUsername // .WINDOWS_ACCOUNT_USERNAME // empty' + set_if_unset "WINDOWS_ACCOUNT_PASSWORD" '.userAccount.password // .windowsAccountPassword // .WINDOWS_ACCOUNT_PASSWORD // empty' + set_bool_if_unset "WINDOWS_LOCALE_SETUP" '.userAccount.localeSetup // .windowsLocaleSetup // .WINDOWS_LOCALE_SETUP // empty' + set_if_unset "WINDOWS_LOCALE" '.userAccount.locale // .windowsLocale // .WINDOWS_LOCALE // empty' + + set_bool_if_unset "DRY_RUN" '.execution.dryRun // .dryRun // .DRY_RUN // empty' + set_bool_if_unset "SKIP_IMAGE_VERIFICATION" '.execution.skipImageVerification // .skipImageVerification // .SKIP_IMAGE_VERIFICATION // empty' + set_bool_if_unset "VERIFY_TLS" '.execution.verifyTls // .verifyTls // .VERIFY_TLS // empty' + set_check_updates_if_unset + set_bool_if_unset "NO_UPDATE" '.execution.noUpdate // .noUpdate // .NO_UPDATE // empty' + set_if_unset "WOR_LOG_FILE" '.execution.logFile // .logFile // .WOR_LOG_FILE // empty' + + set_bool_if_unset "PLAY_SOUND" '.notifications.playSound // .playSound // .PLAY_SOUND // empty' + set_bool_if_unset "SHOW_NOTIFICATION" '.notifications.showNotification // .showNotification // .SHOW_NOTIFICATION // empty' + #the sound is named per platform, so a config shared between a Mac and a Linux box suits both + if is_macos ;then + set_if_unset "COMPLETION_SOUND" '.notifications.sounds.macos // .completionSound // .COMPLETION_SOUND // empty' + else + set_if_unset "COMPLETION_SOUND" '.notifications.sounds.linux // .completionSound // .COMPLETION_SOUND // empty' + fi + + set_if_unset "PE_INSTALLER_URL" '.system.peInstallerUrl // .peInstallerUrl // .PE_INSTALLER_URL // empty' + set_if_unset "PE_INSTALLER_SHA256" '.system.peInstallerSha256 // .peInstallerSha256 // .PE_INSTALLER_SHA256 // empty' + set_if_unset "UEFI_VER_PI3" '.system.uefiVerPi3 // .uefiVerPi3 // .UEFI_VER_PI3 // empty' + set_if_unset "UEFI_VER_PI4" '.system.uefiVerPi4 // .uefiVerPi4 // .UEFI_VER_PI4 // empty' + set_if_unset "UEFI_VER_PI5" '.system.uefiVerPi5 // .uefiVerPi5 // .UEFI_VER_PI5 // empty' + set_if_unset "DRIVER_VER" '.system.driverVer // .driverVer // .DRIVER_VER // empty' + set_if_unset "ARMV80_MAX_BUILD" '.system.armv80MaxBuild // .armv80MaxBuild // .ARMV80_MAX_BUILD // empty' + set_if_unset "WIN11_MIN_BUILD" '.system.win11MinBuild // .win11MinBuild // .WIN11_MIN_BUILD // empty' + set_if_unset "WIN10_OLDEST_BUILD" '.system.win10OldestBuild // .win10OldestBuild // .WIN10_OLDEST_BUILD // empty' + set_if_unset "EXAMPLE_BID" '.system.exampleBid // .exampleBid // .EXAMPLE_BID // empty' + set_if_unset "ARMV80_SAFE_BID" '.system.armv80SafeBid // .armv80SafeBid // .ARMV80_SAFE_BID // empty' + + [[ "$DL_DIR" == \~* ]] && DL_DIR="$HOME${DL_DIR#\~}" + [[ "$SOURCE_FILE" == \~* ]] && SOURCE_FILE="$HOME${SOURCE_FILE#\~}" + [[ "$WOR_LOG_FILE" == \~* ]] && WOR_LOG_FILE="$HOME${WOR_LOG_FILE#\~}" +} + # ######## END OF FUNCTIONS, BEGINNING OF SCRIPT # -#Determine the directory to download windows component files to +#Load configuration values from config.json if present and variables are unset +load_config_json [ -z "$DL_DIR" ] && DL_DIR="$HOME/wor-flasher-files" +#Where a failed run's log is kept. Left unset so it follows DL_DIR even if the GUI changes that later; +#set it to an absolute path to put the log somewhere else. + #UEFI firmware selection. -#Set UEFI_USE_LATEST=0 to use the pinned versions below instead of querying GitHub for the newest release. -[ -z "$UEFI_USE_LATEST" ] && UEFI_USE_LATEST=1 +#Set UEFI_USE_LATEST=1 to query GitHub for the newest release instead of using the pinned versions below. +[ -z "$UEFI_USE_LATEST" ] && UEFI_USE_LATEST=0 + +#Raspberry Pi 4 only; this setting is ignored for every other model. +#Disable the pftf 3 GB RAM limit during specialize after WoR-PE reboots. +[ -z "$PI4_AUTO_DISABLE_3GB" ] && PI4_AUTO_DISABLE_3GB=1 +case "$PI4_AUTO_DISABLE_3GB" in + 0 | 1) ;; + *) error "Unknown value for PI4_AUTO_DISABLE_3GB. Expected '0' or '1'.";; +esac + +#Set to 1 to perform a one-time UEFI Shell handoff after Windows Setup, persistently disabling the Pi 4 RAM limit. +#Off by default because it temporarily replaces the EFI fallback loader and requires a tested ARM64 Shell binary. +[ -z "$PI4_UEFI_SHELL_UNLOCK" ] && PI4_UEFI_SHELL_UNLOCK=0 +case "$PI4_UEFI_SHELL_UNLOCK" in + 0 | 1) ;; + *) error "Unknown value for PI4_UEFI_SHELL_UNLOCK. Expected '0' or '1'.";; +esac + +#Set to 1 to hide the Windows OOBE network and online-account screens, or 0 to require the standard flow. Adjustable in the GUI's Advanced Options window. +[ -z "$OOBE_NETWORK_BYPASS" ] && OOBE_NETWORK_BYPASS=1 +if [ "$OOBE_NETWORK_BYPASS" != 0 ] && [ "$OOBE_NETWORK_BYPASS" != 1 ];then + error "Unknown value for OOBE_NETWORK_BYPASS. Expected '0' or '1'." +fi -#Pinned versions. Used when UEFI_USE_LATEST=0, or as a fallback when the GitHub API is unreachable. -[ -z "$UEFI_VER_PI3" ] && UEFI_VER_PI3='v1.39' -[ -z "$UEFI_VER_PI4" ] && UEFI_VER_PI4='v1.52' -[ -z "$UEFI_VER_PI5" ] && UEFI_VER_PI5='v0.3' +#Optional Windows setup customization. Passwords are written to Autounattend.xml only when explicitly enabled. +[ -z "$WINDOWS_ACCOUNT_SETUP" ] && WINDOWS_ACCOUNT_SETUP=0 +[ -z "$WINDOWS_ACCOUNT_USERNAME" ] && WINDOWS_ACCOUNT_USERNAME='' +[ -z "$WINDOWS_ACCOUNT_PASSWORD" ] && WINDOWS_ACCOUNT_PASSWORD='' +[ -z "$WINDOWS_LOCALE_SETUP" ] && WINDOWS_LOCALE_SETUP=1 +[ -z "$WINDOWS_LOCALE" ] && WINDOWS_LOCALE="$(default_windows_locale)" +case "$WINDOWS_ACCOUNT_SETUP" in + 0) ;; + 1) [ -n "$WINDOWS_ACCOUNT_USERNAME" ] && [ -n "$WINDOWS_ACCOUNT_PASSWORD" ] || error "Windows account setup requires a username and password." ;; + *) error "Unknown value for WINDOWS_ACCOUNT_SETUP. Expected '0' or '1'." ;; +esac +case "$WINDOWS_LOCALE_SETUP" in + 0) ;; + 1) [[ "$WINDOWS_LOCALE" =~ ^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8}){0,2}$ ]] || error "WINDOWS_LOCALE must look like en-US, en-GB or sr-Latn-RS." ;; + *) error "Unknown value for WINDOWS_LOCALE_SETUP. Expected '0' or '1'." ;; +esac + +#Pinned versions. Used by default, or as a fallback when the GitHub API is unreachable. +[ -z "$UEFI_VER_PI3" ] && UEFI_VER_PI3="$WOR_DEFAULT_UEFI_VER_PI3" +#Pi 4 stays on v1.50: it is the only release where both the Ethernet MAC and microSD boot work. +#v1.51 and v1.52 report a MAC of 00:00:00:00:00:00 (pftf/RPi4#283), leaving Windows on an APIPA +#address with no DHCP. v1.53 fixed that but still will not boot from microSD, as do v1.52 (pftf/RPi4#285). +[ -z "$UEFI_VER_PI4" ] && UEFI_VER_PI4="$WOR_DEFAULT_UEFI_VER_PI4" +[ -z "$UEFI_VER_PI5" ] && UEFI_VER_PI5="$WOR_DEFAULT_UEFI_VER_PI5" #Windows driver package. The upstream project is archived, so v0.17 is the final release. [ -z "$DRIVERS_USE_LATEST" ] && DRIVERS_USE_LATEST=1 -[ -z "$DRIVER_VER" ] && DRIVER_VER='v0.17' +[ -z "$DRIVER_VER" ] && DRIVER_VER="$WOR_DEFAULT_DRIVER_VER" + +#Set to 1 to run every step except writing to the drive. +[ -z "$DRY_RUN" ] && DRY_RUN=0 +case "$DRY_RUN" in + 0 | 1) ;; + *) error "Unknown value for DRY_RUN. Expected '0' or '1'.";; +esac + +#Set to 1 to skip the post-flash written-image verification (partition, boot file, and checksum checks). Not recommended. +[ -z "$SKIP_IMAGE_VERIFICATION" ] && SKIP_IMAGE_VERIFICATION=0 +case "$SKIP_IMAGE_VERIFICATION" in + 0 | 1) ;; + *) error "Unknown value for SKIP_IMAGE_VERIFICATION. Expected '0' or '1'.";; +esac + +#Set to 0 to leave the UEFI firmware package's own default config.txt in place instead of writing the CONFIG_TXT variable. +[ -z "$APPLY_CUSTOM_CONFIG_TXT" ] && APPLY_CUSTOM_CONFIG_TXT=1 +case "$APPLY_CUSTOM_CONFIG_TXT" in + 0 | 1) ;; + *) error "Unknown value for APPLY_CUSTOM_CONFIG_TXT. Expected '0' or '1'.";; +esac + +#Set to 0 to show empty card-reader slots as selectable drives in WoR-PE. Written to the cached PE settings.ini before each run. +[ -z "$HIDE_EMPTY_DRIVES" ] && HIDE_EMPTY_DRIVES=1 +case "$HIDE_EMPTY_DRIVES" in + 0 | 1) ;; + *) error "Unknown value for HIDE_EMPTY_DRIVES. Expected '0' or '1'.";; +esac #Set to 0 to skip TLS certificate verification, for systems with an outdated CA bundle. [ -z "$VERIFY_TLS" ] && VERIFY_TLS=1 +#Set to 0 for a silent finish. COMPLETION_SOUND names the sound; an unplayable name falls back per platform. +[ -z "$PLAY_SOUND" ] && PLAY_SOUND=1 +case "$PLAY_SOUND" in + 0 | 1) ;; + *) error "Unknown value for PLAY_SOUND. Expected '0' or '1'.";; +esac + +#Set to 0 to finish without a desktop notification. +[ -z "$SHOW_NOTIFICATION" ] && SHOW_NOTIFICATION=1 +case "$SHOW_NOTIFICATION" in + 0 | 1) ;; + *) error "Unknown value for SHOW_NOTIFICATION. Expected '0' or '1'.";; +esac + #Cache mode: 0 downloads components again every run, 1 reuses them while they are still the newest version, 2 reuses them without checking. -[ -z "$USE_CACHE" ] && USE_CACHE=0 +[ -z "$USE_CACHE" ] && USE_CACHE=1 #WoR PE-based installer. worproject.com redirects to a versioned asset on their GitHub mirror. [ -z "$PE_USE_LATEST" ] && PE_USE_LATEST=1 -[ -z "$PE_INSTALLER_URL" ] && PE_INSTALLER_URL='https://github.com/worproject/dldserv-mirror/releases/download/13%2F02%2F2024/WoR-PE_Package_1.1.0.zip' -[ -z "$PE_INSTALLER_SHA256" ] && PE_INSTALLER_SHA256='A039E28FE7E39147899B0634C15E336C3B26A6F76201092EBB9732474CD43D0A' +[ -z "$PE_INSTALLER_URL" ] && PE_INSTALLER_URL="$WOR_DEFAULT_PE_INSTALLER_URL" +[ -z "$PE_INSTALLER_SHA256" ] && PE_INSTALLER_SHA256="$WOR_DEFAULT_PE_INSTALLER_SHA256" #Last Windows build that boots on the ARMv8.0 Pi3/Pi4; newer ones use ARMv8.1 atomics. #Source: https://worproject.com/faq "Does Windows 11 work?" -[ -z "$ARMV80_MAX_BUILD" ] && ARMV80_MAX_BUILD=25163 +[ -z "$ARMV80_MAX_BUILD" ] && ARMV80_MAX_BUILD="$WOR_DEFAULT_ARMV80_MAX_BUILD" #Windows build reference points. -[ -z "$WIN11_MIN_BUILD" ] && WIN11_MIN_BUILD=22000 #builds at or above this are Windows 11, below are Windows 10 -[ -z "$WIN10_OLDEST_BUILD" ] && WIN10_OLDEST_BUILD='17134.112' #marks the end of the Windows 10 section of worproject.com's version list -[ -z "$EXAMPLE_BID" ] && EXAMPLE_BID='22621.525' #shown to the user as an example of the expected build-number format -[ -z "$ARMV80_SAFE_BID" ] && ARMV80_SAFE_BID='22631.2861' #newest Windows 11 build suggested for the ARMv8.0 Pi3/Pi4 +[ -z "$WIN11_MIN_BUILD" ] && WIN11_MIN_BUILD="$WOR_DEFAULT_WIN11_MIN_BUILD" #builds at or above this are Windows 11, below are Windows 10 +[ -z "$WIN10_OLDEST_BUILD" ] && WIN10_OLDEST_BUILD="$WOR_DEFAULT_WIN10_OLDEST_BUILD" #marks the end of the Windows 10 section of worproject.com's version list +[ -z "$EXAMPLE_BID" ] && EXAMPLE_BID="$WOR_DEFAULT_EXAMPLE_BID" #shown to the user as an example of the expected build-number format +[ -z "$ARMV80_SAFE_BID" ] && ARMV80_SAFE_BID="$WOR_DEFAULT_ARMV80_SAFE_BID" #newest Windows 11 build suggested for the ARMv8.0 Pi3/Pi4 #Determine the directory that contains this script [ -z "$DIRECTORY" ] && DIRECTORY="$(resolve_path "$(dirname "$0")")" +#Shared UI artwork for README-linked branding, both GUIs and the macOS app icon. +[ -z "$WOR_ASSETS_DIR" ] && WOR_ASSETS_DIR="$DIRECTORY/$WOR_ASSETS_DIRNAME" +[ -z "$WOR_LOGO_PATH" ] && WOR_LOGO_PATH="$WOR_ASSETS_DIR/$WOR_LOGO_FILENAME" + #clear the variable storing path to this script, if the folder does not contain a file named 'install-wor.sh' [ ! -f "${DIRECTORY}/install-wor.sh" ] && DIRECTORY='' +[ -z "$WOR_CACHE_DIR" ] && WOR_CACHE_DIR="$DIRECTORY/cache" IFS=$'\n' -#Self-updater target: which repo/ref this script compares its local git commit against, and pulls from. -[ -z "$UPDATE_REPO_URL" ] && UPDATE_REPO_URL='https://github.com/Botspot/wor-flasher' -[ -z "$UPDATE_REF" ] && UPDATE_REF='HEAD' #the branch/ref on UPDATE_REPO_URL to compare against, e.g. HEAD or refs/heads/main - -#Set NO_UPDATE=0 to opt in to source-checkout updates. Packaged releases should use signed release updates instead. -[ -z "$NO_UPDATE" ] && NO_UPDATE=1 - -{ #check for updates and auto-update unless disabled via NO_UPDATE -if [ -e "$DIRECTORY" ] && [ "$NO_UPDATE" != 1 ] && command -v git >/dev/null && git -C "$DIRECTORY" rev-parse --git-dir >/dev/null 2>&1 ;then - prepwd="$PWD" - cd "$DIRECTORY" - local_commit="$(git rev-parse HEAD)" #commit this local checkout at $DIRECTORY is on - remote_commit="$(git ls-remote "$UPDATE_REPO_URL" "$UPDATE_REF" | awk 'NR == 1 {print $1}')" #latest commit on UPDATE_REPO_URL/UPDATE_REF - - if [ "$local_commit" != "$remote_commit" ] && [ ! -z "$remote_commit" ] && [ ! -z "$local_commit" ];then - if ! git diff --quiet || ! git diff --cached --quiet ;then - status "Skipping automatic update because this checkout has uncommitted changes." - else - status "Auto-updating wor-flasher for the latest features and improvements..." - status "To disable this next time, set NO_UPDATE=1" - sleep 1 - - if git fetch --quiet "$UPDATE_REPO_URL" "$UPDATE_REF" && git merge --ff-only FETCH_HEAD ;then - status "Update finished. Reloading script..." - NO_UPDATE=1 "$0" "$@" - exit $? - else - warning "Automatic update failed. Continuing..." - fi - fi - fi - cd "$prepwd" +#Release update check. Deliberately read-only: WoR-Flasher never rewrites its own installation, +#so an interrupted or hostile check can never leave a half-updated disk flasher behind. +#Set CHECK_FOR_UPDATES=0 or NO_UPDATE=1 to skip it. +if [ -z "${NO_UPDATE:-}" ] && [ -n "${CHECK_FOR_UPDATES:-}" ];then + case "$CHECK_FOR_UPDATES" in + 1 | true | TRUE) NO_UPDATE=0 ;; + 0 | false | FALSE) NO_UPDATE=1 ;; + esac +fi +[ -z "$NO_UPDATE" ] && NO_UPDATE=0 + +{ #report a newer published release unless disabled via NO_UPDATE +if [ "$NO_UPDATE" != 1 ] && [ -n "$DIRECTORY" ] && [ -f "$DIRECTORY/src/updater.mjs" ] && command -v node >/dev/null 2>&1 ;then + emit_gui_progress "STATUS Checking for WoR-Flasher updates..." + emit_gui_substep 5 + update_res="$(node "$DIRECTORY/src/updater.mjs" --check-release "--repo-dir=$DIRECTORY" 2>/dev/null)" + emit_gui_substep 100 + case "$update_res" in + UPDATE_AVAILABLE*) + update_ver="$(awk '{print $2}' <<<"$update_res")" + update_url="$(awk '{print $3}' <<<"$update_res")" + status "WoR-Flasher $update_ver is available; this is v$WOR_FLASHER_VERSION." + [ -n "$update_url" ] && status "Release notes: $update_url" + status "To disable this check next time, set CHECK_FOR_UPDATES=0" + ;; + esac fi } -mkdir -p "${DIRECTORY}/cache" +read_config_template() { #Input: path relative to config-templates/. Output: file contents. + local path="$DIRECTORY/config-templates/$1" + #continuing without one of these silently produces a blank config.txt or answer file, which is worse than stopping + [ -s "$path" ] || error "Missing config-templates/$1 +This file ships with WoR-Flasher and is required to write a bootable drive. +Update or re-clone your WoR-Flasher checkout, then run this script again." + cat "$path" +} [ "$1" == 'source' ] && return 0 #If being sourced, exit here at this point in the script #past this point, this script is being run, not sourced. +mkdir -p "$WOR_CACHE_DIR" + +#A single entry point, without guessing: --gui hands over to the graphical front-end, which then runs +#this script again to do the work. Never auto-detect a display - DISPLAY is also set over SSH and in CI, +#and a tool that erases a disk must do exactly what it was asked to do. +while [ $# -gt 0 ]; do + case "$1" in + --config=*) + WOR_CONFIG_FILE="${1#*=}" + load_config_json "$WOR_CONFIG_FILE" + shift + ;; + --config) + [ -n "${2:-}" ] || error "--config requires a file path argument." + WOR_CONFIG_FILE="$2" + load_config_json "$WOR_CONFIG_FILE" + shift 2 + ;; + gui|--gui|-g) + shift + [ -x "$DIRECTORY/install-wor-gui.sh" ] || error "No script found named install-wor-gui.sh +Both scripts must be in the same directory." + exec "$DIRECTORY/install-wor-gui.sh" "$@" + ;; + --version|-V) + printf '%s %s\n' "$WOR_FLASHER_NAME" "$WOR_FLASHER_VERSION" + exit 0 + ;; + --help|-h) + cat <<HELP +$WOR_FLASHER_NAME $WOR_FLASHER_VERSION +Usage: $(basename "$0") [OPTIONS] [gui] + + (no arguments) run the interactive text-mode installer + gui, --gui, -g run the graphical front-end instead + --config <file> load configuration settings from a JSON file + --version print the version and exit + --help show this message + +Every setting can also be supplied as an environment variable or via config.json; see the README. +HELP + exit 0 + ;; + *) + error "Unknown argument '$1'. Run '$(basename "$0") --help' for usage." + ;; + esac +done + require_linux_host #Ensure this script's parent directory is valid [ ! -e "$DIRECTORY" ] && error "$(basename "$0"): Failed to determine the directory that contains this script. Try running this script with full paths." +#Guarantee a clean stop with no further steps on Ctrl+C or a termination signal. +trap 'handle_interrupt' INT TERM + LANG=C LC_ALL=C LANGUAGE=C +ANSI_CYAN=$'\e[96m' +ANSI_RESET=$'\e[0m' + +#Let the GUI progress window open before setup/download/preparation. The single GUI sudo prompt is +#deferred until Step 5, immediately before the first destructive disk operation. +[ "$RUN_MODE" == gui ] && gui_preauthenticate setup || exit 1 #Create folder to download everything to mkdir -p "$DL_DIR" -cd "$DL_DIR" +cd "$DL_DIR" || error "Failed to open the download directory: $DL_DIR" + +#We are about to download the PE installer, drivers, firmware, and likely a multi-GB Windows image. +#Fail now with a clear message instead of wasting time and ending up with a partial, unusable cache. +required_download_space=$((2 * 1024 * 1024 * 1024)) +if [ ! -d "$PWD/peinstaller" ] || [ "$USE_CACHE" != 2 ];then + required_download_space=$((required_download_space + 256 * 1024 * 1024)) +fi +if [ ! -d "$PWD/driverpackage" ] || [ "$USE_CACHE" != 2 ];then + required_download_space=$((required_download_space + 256 * 1024 * 1024)) +fi +if [ ! -d "$PWD/pi${RPI_MODEL}-uefipackage" ] || [ "$USE_CACHE" != 2 ];then + required_download_space=$((required_download_space + 192 * 1024 * 1024)) +fi +require_free_space "$required_download_space" "$DL_DIR" #unless specified otherwise, run this script in cli mode [ -z "$RUN_MODE" ] && RUN_MODE=cli #RUN_MODE=gui +cli_intro if [ "$USE_CACHE" != 0 ] && [ "$USE_CACHE" != 1 ] && [ "$USE_CACHE" != 2 ];then error "Unknown value for USE_CACHE. Expected '0', '1' or '2'." fi if [ "$USE_CACHE" == 0 ];then - status "USE_CACHE=0: deleting cached downloads so everything is fetched again" - rm -rf "$PWD/peinstaller" "$PWD/driverpackage" "$PWD"/pi[345]-uefipackage - if [ ! -z "$DIRECTORY" ];then - rm -rf "${DIRECTORY}/cache" - mkdir -p "${DIRECTORY}/cache" - fi + status "USE_CACHE=0: clearing cached components so everything is fetched again" + clear_cached_components fi { #choose windows version @@ -748,7 +2823,7 @@ Enter \e[96m1\e[0m, \e[96m2\e[0m or \e[96m3\e[0m: " #Discover past extracted ISO files so user does not need to keep ISO num_opts=3 #default number of options already in the "Additional options" menu add_options='' #Store additional options to display to the user - available_extracted_isos="$(find "$PWD" -maxdepth 2 -type f -name 'alldone' | grep -o "/winfiles_from_iso.*/" | sed 's+/$++' | sed 's+/winfiles_from_iso_++g' | sort)" + available_extracted_isos="$(list_cached_winfiles "$PWD" | grep '^winfiles_from_iso_' | sed 's/^winfiles_from_iso_//g' | sort)" for folder in $available_extracted_isos ;do BID="$(echo "$folder" | awk -F_ '{print $1}')" @@ -771,8 +2846,8 @@ $([ $num_opts == 3 ] && echo 'Enter \e[96m1\e[0m, \e[96m2\e[0m or \e[96m3\e[0m: #versions='' list_bids 10 >/dev/null #set $versions globally so it is not downloaded twice - list_bids_supported 11 | sed 's/ /'$(echo -e '\e[0m')' /g' | sed 's/^/Windows 11 '$(echo -e '\e[96m')'/g' - list_bids_supported 10 | sed 's/ /'$(echo -e '\e[0m')' /g' | sed 's/^/Windows 10 '$(echo -e '\e[96m')'/g' + list_bids_supported 11 | sed "s/ /${ANSI_RESET} /g" | sed "s/^/Windows 11 ${ANSI_CYAN}/g" + list_bids_supported 10 | sed "s/ /${ANSI_RESET} /g" | sed "s/^/Windows 10 ${ANSI_CYAN}/g" read -p $'\nFrom the list above, enter a Windows version number: ' BID if (list_bids_supported 11 ; list_bids_supported 10) | awk '{print $1}' | grep -qFx "$BID" ;then @@ -787,29 +2862,23 @@ $([ $num_opts == 3 ] && echo 'Enter \e[96m1\e[0m, \e[96m2\e[0m or \e[96m3\e[0m: read -p $'\nEnter the full path to a Windows 10/11 ARM64 ISO file: ' SOURCE_FILE if [ -z "$SOURCE_FILE" ];then break #exit ISO file menu - elif [ ! -f "$SOURCE_FILE" ];then - echo_red "This file does not exist. Check spelling and try again." - SOURCE_FILE='' - elif [[ "$SOURCE_FILE" != *'.ISO' ]] && [[ "$SOURCE_FILE" != *'.iso' ]];then - echo_red "This file does not have a .ISO file extension." - SOURCE_FILE='' - elif [ "$(get_file_size "$SOURCE_FILE")" -lt $((3*1024*1024*1024)) ];then - echo_red "This file is smaller than 3GB and is probably incomplete." + elif ! iso_problem="$(validate_iso_file "$SOURCE_FILE")" ;then + echo_red "$iso_problem" SOURCE_FILE='' else #ISO file looks good #Infer Build ID based on filename of ISO - BID="$(basename "$SOURCE_FILE" | tr '_ -' '\n' | grep -E -m 1 '^[0-9]{5}')" + BID="$(bid_from_iso_name "$SOURCE_FILE")" if [ -z "$BID" ];then read -p $'\nTo store files from this ISO, this script needs to know the Windows build number of this ISO.\nPlease enter it now: (example: '"$EXAMPLE_BID"') ' BID [ -z "$BID" ] && error "Cannot proceed without a build number for your ISO file." fi #Infer language based on filename of ISO - WIN_LANG="$(basename "$SOURCE_FILE" | tr '_ ' '\n' | grep -io -m 1 "$(list_langs | awk -F: '{print $1}' | tr '\n' ';' | sed 's/;/\\|/g' | sed 's/\\|$/\n/g')" | tr '[A-Z]' '[a-z]')" + WIN_LANG="$(lang_from_iso_name "$SOURCE_FILE")" if [ -z "$WIN_LANG" ];then read -p $'\nTo store files from this ISO, this script needs to know the language of this Windows ISO.\nPlease enter it now: (example: en-us) ' WIN_LANG if [ -z "$WIN_LANG" ];then error "Cannot proceed without a language for your ISO file." - elif ! list_langs | awk -F: '{print $1}' | grep -q "$WIN_LANG" ;then + elif ! is_known_win_lang "$WIN_LANG" ;then error "Language code was not found in the list!\n$(list_langs | awk '{print $1}')" fi fi @@ -821,7 +2890,7 @@ $([ $num_opts == 3 ] && echo 'Enter \e[96m1\e[0m, \e[96m2\e[0m or \e[96m3\e[0m: break #exit "more options" menu fi ;; - $num_opts) + "$num_opts") #go back break ;; @@ -849,14 +2918,7 @@ else #Verify SOURCE_FILE value provided to script if [ ! -z "$SOURCE_FILE" ];then - if [ ! -f "$SOURCE_FILE" ];then - error "Specified ISO file '$SOURCE_FILE' does not exist." - elif [[ "$SOURCE_FILE" != *'.ISO' ]] && [[ "$SOURCE_FILE" != *'.iso' ]];then - error "Specified ISO file '$SOURCE_FILE' does not have a .ISO file extension." - elif [ "$(get_file_size "$SOURCE_FILE")" -lt $((3*1024*1024*1024)) ];then - error "Specified ISO file '$SOURCE_FILE' is smaller than 3GB and is probably incomplete." - fi - + iso_problem="$(validate_iso_file "$SOURCE_FILE")" || error "Specified ISO file '$SOURCE_FILE': $iso_problem" #Verify BID value provided to script elif [ "$using_esd" == true ] && ! (list_bids 10 ; list_bids 11) | awk '{print $1}' | grep -Fqx "$BID" ;then error "Build ID '$BID' not found on list of available ones." @@ -866,14 +2928,16 @@ fi { #choose language if [ -z "$WIN_LANG" ];then + default_language="$(default_win_lang)" #list languages and highlight the language codes echo - list_langs | sed 's/^/'$(echo -e '\e[96m')'/g' | sed 's/:/'$(echo -e '\e[0m')' - /g' | sort + list_langs | sed "s/^/${ANSI_CYAN}/g" | sed "s/:/${ANSI_RESET} - /g" | sort while true; do - read -p $'\nFrom the list above, enter a language: ' WIN_LANG + read -p $'\nFrom the list above, enter a language ['"$default_language"']: ' WIN_LANG + [ -z "$WIN_LANG" ] && WIN_LANG="$default_language" - if list_langs | awk -F: '{print $1}' | grep -qFx "$WIN_LANG" ;then + if is_known_win_lang "$WIN_LANG" ;then #if selected language matches line in language list break else @@ -883,7 +2947,7 @@ if [ -z "$WIN_LANG" ];then done #Verify WIN_LANG value provided to script -elif ! list_langs | awk -F: '{print $1}' | grep -qFx "$WIN_LANG" ;then +elif ! is_known_win_lang "$WIN_LANG" ;then error "Invalid WIN_LANG value '$WIN_LANG'.\nAvailable languages:\n$(list_langs | awk -F: '{print $1}')" fi } @@ -934,7 +2998,7 @@ if [ -z "$DEVICE" ];then echo "Available devices:" list_devs || echo -e "\e[93mNone found - please insert a storage device and press Enter\e[0m" read -p "Choose a device to flash the Windows setup files to: " DEVICE - if [ "$DEVICE" == "$ROOT_DEV" ];then + if ! is_safe_target_device "$DEVICE";then echo_red "Device $DEVICE is your current boot drive! You cannot overwrite this drive." elif [ -b "$DEVICE" ];then break #exit loop @@ -947,8 +3011,8 @@ if [ -z "$DEVICE" ];then elif [ ! -b "$DEVICE" ];then error "Invalid value for DEVICE: block device $DEVICE does not exist. Available devices:\n$(list_devs)" -elif [ "$DEVICE" == "$ROOT_DEV" ];then - error "Refusing to overwrite current boot drive $DEVICE." +elif ! is_safe_target_device "$DEVICE";then + error "Refusing to overwrite $DEVICE. Choose an external, physical, writable whole disk that is not the current boot drive." fi } @@ -985,22 +3049,35 @@ elif [ -z "$CAN_INSTALL_ON_SAME_DRIVE" ];then fi } -echo " -Input configuration: -DL_DIR: $DL_DIR -RUN_MODE: $RUN_MODE -RPI_MODEL: $RPI_MODEL -DEVICE: $DEVICE -CAN_INSTALL_ON_SAME_DRIVE: $CAN_INSTALL_ON_SAME_DRIVE" -[ ! -z "$SOURCE_FILE" ] && echo "SOURCE_FILE: $SOURCE_FILE" -echo "BID: $BID -WIN_LANG: $WIN_LANG" -[ ! -z "$CONFIG_TXT" ] && echo "CONFIG_TXT: ⤵ -$(echo "$CONFIG_TXT" | grep . | sed 's/^/ > /g') -CONFIG_TXT: ⤴" -[ ! -z "$DRY_RUN" ] && echo "DRY_RUN: $DRY_RUN" + +#fail fast, before any downloads, if macOS partitioning can't proceed later +is_macos && { command -v sgdisk >/dev/null || error "sgdisk is required to partition $DEVICE correctly. Install it with 'brew install gptfdisk', then run this script again."; } + +#the GUI already picked one and exported it; a CLI run gets the same shipped template, so both write identical media +set_default_config_txt + +printf '\n\033[96m%s\033[0m - starting installation\n' "$WOR_APP_TITLE" +settings_summary_plain ' %-24s %s\n' echo +STEP_NUM=0 +[ "$RPI_MODEL" == 5 ] && STEP_TOTAL=7 || STEP_TOTAL=8 + +#A GUI password retry starts this script again. Nothing before the first sudo call touches the disk, +#so those steps have already finished; repeating their downloads, hash checks and extraction would +#make a mistyped password cost as much as a whole run. Resume at the step that asked for the password. +RESUME_AT_FLASH=0 +if [ "${WOR_RESUME_AT_FLASH:-0}" == 1 ] && flash_files_already_prepared ;then + RESUME_AT_FLASH=1 + #the skipped steps still count toward STEP_TOTAL, so the progress bar continues instead of restarting + [ "$RPI_MODEL" == 5 ] && STEP_NUM=3 || STEP_NUM=4 + status "Resuming at the administrator password step - the files prepared before it are still ready" +fi + +if [ "$RESUME_AT_FLASH" != 1 ];then +#Not indented: this wraps the whole preparation half of the run so a resume can skip it as one unit. + +phase "Preparing the WoR PE-based installer" if [ "$USE_CACHE" == 2 ] && [ -d "$PWD/peinstaller" ];then echo "Not downloading $PWD/peinstaller - using cache without checking for updates" else @@ -1038,22 +3115,23 @@ else error "The unzip command failed to extract $PWD/WoR-PE_Package.zip" fi rm -f "$PWD/WoR-PE_Package.zip" - mark_cache "$PWD/peinstaller" "$EXPECTED_SHA256" + mark_cache "$PWD/peinstaller" "$EXPECTED_SHA256" || error "Failed to record PE installer cache integrity." echo fi fi if [ "$RPI_MODEL" != 5 ];then + phase "Preparing ARM64 drivers" if [ "$USE_CACHE" == 2 ] && [ -d "$PWD/driverpackage" ];then echo "Not downloading $PWD/driverpackage - using cache without checking for updates" else #from: https://github.com/worproject/RPi-Windows-Drivers/releases URL='' if [ "$DRIVERS_USE_LATEST" == 1 ];then - URL="$(wget -qO- https://api.github.com/repos/worproject/RPi-Windows-Drivers/releases/latest 2>/dev/null | grep '"browser_download_url":'".*RPi${RPI_MODEL}_Windows_ARM64_Drivers_.*\.zip" | sed 's/^.*browser_download_url": "//g' | sed 's/"$//g')" + URL="$(wget -qO- "https://api.github.com/repos/${WOR_DEFAULT_DRIVERS_REPO}/releases/latest" 2>/dev/null | grep '"browser_download_url":'".*RPi${RPI_MODEL}_Windows_ARM64_Drivers_.*\.zip" | sed 's/^.*browser_download_url": "//g' | sed 's/"$//g')" [ -z "$URL" ] && echo_red "Failed to query the latest driver release. Falling back to pinned version ${DRIVER_VER}." fi - [ -z "$URL" ] && URL="https://github.com/worproject/RPi-Windows-Drivers/releases/download/${DRIVER_VER}/RPi${RPI_MODEL}_Windows_ARM64_Drivers_${DRIVER_VER}.zip" + [ -z "$URL" ] && URL="https://github.com/${WOR_DEFAULT_DRIVERS_REPO}/releases/download/${DRIVER_VER}/RPi${RPI_MODEL}_Windows_ARM64_Drivers_${DRIVER_VER}.zip" if cache_is_current "$PWD/driverpackage" "$URL" ;then echo "Not downloading $PWD/driverpackage - cached copy is up to date" @@ -1069,29 +3147,48 @@ if [ "$RPI_MODEL" != 5 ];then fi rm -f "$PWD/RPi${RPI_MODEL}_Windows_ARM64_Drivers.zip" - mark_cache "$PWD/driverpackage" "$URL" + mark_cache "$PWD/driverpackage" "$URL" || error "Failed to record driver cache integrity." echo fi fi + + if [ "$RPI_MODEL" == 4 ];then + for driver_file in \ + bcmgenet/bcmgenet.inf \ + bcmgenet/bcmgenet.cat \ + mcci_dwchsotg/mcci_dwchsotg_hcd.inf \ + mcci_dwchsotg/mcci_dwchsotg_hcd.cat \ + mcci_dwchsotg/mcci_dwchsotg_hub.inf \ + mcci_dwchsotg/mcci_dwchsotg_hub.cat \ + mcci_dwchsotg/arm64/mcci_dwchsotg_hcd.sys \ + mcci_dwchsotg/arm64/mcci_dwchsotg_hub.sys \ + rpiuxflt/rpiuxflt.inf \ + rpiuxflt/rpiuxflt.cat \ + rpiuxflt/rpiuxflt.sys + do + [ -s "$PWD/driverpackage/$driver_file" ] || error "Pi 4 driver package is incomplete: $driver_file is missing or empty. Clear the driver cache and run WoR-Flasher again." + done + fi fi +phase "Preparing Pi${RPI_MODEL} UEFI firmware" if [ "$USE_CACHE" == 2 ] && [ -d "$PWD/pi${RPI_MODEL}-uefipackage" ];then echo "Not downloading $PWD/pi${RPI_MODEL}-uefipackage - using cache without checking for updates" else #from: https://github.com/pftf/RPi4/releases case "$RPI_MODEL" in 5) - UEFI_REPO='worproject/rpi5-uefi' + UEFI_REPO="$WOR_DEFAULT_UEFI_REPO_PI5" UEFI_VER="$UEFI_VER_PI5" PINNED_URL="https://github.com/${UEFI_REPO}/releases/download/${UEFI_VER}/RPi5_UEFI_Release_${UEFI_VER}.zip" ;; 4) - UEFI_REPO='pftf/RPi4' + UEFI_REPO="$WOR_DEFAULT_UEFI_REPO_PI4" UEFI_VER="$UEFI_VER_PI4" PINNED_URL="https://github.com/${UEFI_REPO}/releases/download/${UEFI_VER}/RPi4_UEFI_Firmware_${UEFI_VER}.zip" ;; 3) - UEFI_REPO='pftf/RPi3' + UEFI_REPO="$WOR_DEFAULT_UEFI_REPO_PI3" UEFI_VER="$UEFI_VER_PI3" PINNED_URL="https://github.com/${UEFI_REPO}/releases/download/${UEFI_VER}/RPi3_UEFI_Firmware_${UEFI_VER}.zip" ;; @@ -1120,13 +3217,14 @@ else fi rm -f "$PWD/RPi${RPI_MODEL}_UEFI_Firmware.zip" - mark_cache "$PWD/pi${RPI_MODEL}-uefipackage" "$URL" + mark_cache "$PWD/pi${RPI_MODEL}-uefipackage" "$URL" || error "Failed to record UEFI cache integrity." echo fi fi { #Download Windows ESD if an ISO was not provided and one has not already been extracted +phase "Preparing the Windows image" if [ ! -z "$SOURCE_FILE" ];then echo "Not downloading ESD image - using your ISO instead" @@ -1152,7 +3250,7 @@ else #Download and extract ESD fi #Shorten catalog to only show the ESD for this language - catalog="$(echo "$catalog" | sed 's/></>\n</g' | sed -n '/<Languages>/q;p' | sed -n '/^<LanguageCode>'"${WIN_LANG}"'/,${p;/^<\/File>/q}')" + catalog="$(get_esd_catalog_entry "$(echo "$catalog" | sed 's/></>\n</g')" "$WIN_LANG")" #Get download link, size, and SHA1 hash for ESD URL="$(echo "$catalog" | grep '<FilePath>' -m 1 | sed 's/<FilePath>//g' | sed 's/<\/FilePath>//g')" @@ -1177,28 +3275,31 @@ else #Download and extract ESD error "One of URL, SIZE, or SHA1/SHA256 variables is empty!\nURL: $URL\nSIZE: $SIZE\nSHA1: $SHA1\nSHA256: $SHA256\nHere's the full catalog output: '$catalog'" fi - if [ -f "$SOURCE_FILE" ] && [ ! -z "$SHA1" ] && [ "$SHA1" == "$(echo " - Checking validity of already downloaded image.esd" 1>&2 ; sha1_file "$SOURCE_FILE")" ];then + #The ESD is usually the largest single download in the set, so do the exact free-space check here. + require_free_space $((SIZE + 768 * 1024 * 1024)) "$DL_DIR" + + if [ -f "$SOURCE_FILE" ] && [ ! -z "$SHA1" ] && [ "$SHA1" == "$(sha1_file_with_progress cached-esd "$SOURCE_FILE")" ];then echo "Not downloading $SOURCE_FILE - file exists" - elif [ -f "$SOURCE_FILE" ] && [ ! -z "$SHA256" ] && [ "$SHA256" == "$(echo " - Checking validity of already downloaded image.esd" 1>&2 ; sha256_file "$SOURCE_FILE")" ];then + elif [ -f "$SOURCE_FILE" ] && [ ! -z "$SHA256" ] && [ "$SHA256" == "$(sha256_file_with_progress cached-esd "$SOURCE_FILE")" ];then echo "Not downloading $SOURCE_FILE - file exists" else status "Downloading Windows ESD image" wget "$URL" -O "$PWD/$winfiles/image.esd" || error "Failed to download ESD image" - status -n "Verifying download... " + status "Verifying downloaded image" if [ ! -z "$SHA1" ];then - LOCAL_SHA1="$(sha1_file "$SOURCE_FILE")" + LOCAL_SHA1="$(sha1_file_with_progress downloaded-esd "$SOURCE_FILE")" if [ "$SHA1" != "$LOCAL_SHA1" ];then rm -f "$SOURCE_FILE" error "\nSuccessfully downloaded ESD image $SOURCE_FILE, but it appears to be corrupted. Please run this script again.\n(Expected SHA1 hash is $SHA1, but downloaded file has SHA1 hash $LOCAL_SHA1" fi elif [ ! -z "$SHA256" ];then - LOCAL_SHA256="$(sha256_file "$SOURCE_FILE")" + LOCAL_SHA256="$(sha256_file_with_progress downloaded-esd "$SOURCE_FILE")" if [ "$SHA256" != "$LOCAL_SHA256" ];then rm -f "$SOURCE_FILE" error "\nSuccessfully downloaded ESD image $SOURCE_FILE, but it appears to be corrupted. Please run this script again.\n(Expected SHA256 hash is $SHA256, but downloaded file has SHA256 hash $LOCAL_SHA256" fi fi - echo_green "Done" + echo_green "Download verified" fi fi } @@ -1209,23 +3310,23 @@ if [[ "$SOURCE_FILE" == *'.ESD' ]] || [[ "$SOURCE_FILE" == *'.esd' ]];then status "Extracting $(basename "$SOURCE_FILE") to $PWD" #Extract first volume containing boot files - errors="$(wimextract "$SOURCE_FILE" 1 boot efi --dest-dir="$PWD/bootpart" 2>&1)" || error "Failed to extract first partition of $SOURCE_FILE\nErrors:\n$errors" + with_progress_capture wimextract "$SOURCE_FILE" 1 boot efi --dest-dir="$PWD/bootpart" || error "Failed to extract first partition of $SOURCE_FILE" #Create boot.wim file mkdir "$PWD/bootpart/sources" #Export WinPE & Setup editions to non-solid boot.wim - errors="$(wimexport "$SOURCE_FILE" 2 "$PWD/bootpart/sources/boot.wim" --compress=LZX 2>&1)" || error "Failed to export WinPE edition to $PWD/bootpart/sources/boot.wim\nErrors:\n$errors" - errors="$(wimexport "$SOURCE_FILE" 3 "$PWD/bootpart/sources/boot.wim" --compress=LZX --boot 2>&1)" || error "Failed to export Setup edition to $PWD/bootpart/sources/boot.wim\nErrors:\n$errors" + with_progress_capture wimexport "$SOURCE_FILE" 2 "$PWD/bootpart/sources/boot.wim" --compress=LZX || error "Failed to export WinPE edition to $PWD/bootpart/sources/boot.wim" + with_progress_capture wimexport "$SOURCE_FILE" 3 "$PWD/bootpart/sources/boot.wim" --compress=LZX --boot || error "Failed to export Setup edition to $PWD/bootpart/sources/boot.wim" #If using an external ESD file, make a copy before modifying it if [ "$SOURCE_FILE" != "$PWD/image.esd" ];then - cp "$SOURCE_FILE" "$PWD/image.esd" || error "Failed to copy the ESD to $PWD/image.esd" + copy_local_file_with_progress image.esd "$SOURCE_FILE" "$PWD/image.esd" || error "Failed to copy the ESD to $PWD/image.esd" SOURCE_FILE="$PWD/image.esd" fi #Remove first 3 partitions from ESD file - errors="$(wimdelete "$SOURCE_FILE" 1 --soft 2>&1)" || error "Failed to remove a partition from $SOURCE_FILE\nErrors:\n$errors" - errors="$(wimdelete "$SOURCE_FILE" 1 --soft 2>&1)" || error "Failed to remove a partition from $SOURCE_FILE\nErrors:\n$errors" - errors="$(wimdelete "$SOURCE_FILE" 1 --soft 2>&1)" || error "Failed to remove a partition from $SOURCE_FILE\nErrors:\n$errors" #remove --soft for this last one to minimize filesize + with_progress_capture wimdelete "$SOURCE_FILE" 1 --soft || error "Failed to remove a partition from $SOURCE_FILE" + with_progress_capture wimdelete "$SOURCE_FILE" 1 --soft || error "Failed to remove a partition from $SOURCE_FILE" + with_progress_capture wimdelete "$SOURCE_FILE" 1 || error "Failed to remove a partition from $SOURCE_FILE" #remove --soft for this last one to minimize filesize mv -f "$SOURCE_FILE" "$PWD/install.wim" || error "Failed to rename $SOURCE_FILE to install.wim" touch "$PWD/alldone" #mark this folder of microsoft stuff as complete @@ -1237,40 +3338,41 @@ elif [[ "$SOURCE_FILE" == *'.ISO' ]] || [[ "$SOURCE_FILE" == *'.iso' ]];then cd "$PWD/$winfiles" || error "Failed to access $PWD/$winfiles folder" status "Mounting $(basename "$SOURCE_FILE")" - mkdir -p "$PWD/isomount" || error "Failed to make $PWD/isomount folder" - sudo umount "$PWD/isomount" 2>/dev/null - sudo mount "$SOURCE_FILE" "$PWD/isomount" 2>/dev/null - if [ $? != 0 ];then - status "Failed to mount the ISO file. Trying again after loading the 'udf' kernel module." - sudo modprobe udf - + isomount="$PWD/isomount" + if is_macos ;then + darwin_mount_iso "$SOURCE_FILE" || error "Failed to mount ISO file $SOURCE_FILE with hdiutil." + isomount="$ISO_MOUNTPOINT" + register_device_cleanup "$ISO_DEVICE" + else + mkdir -p "$isomount" || error "Failed to make $isomount folder" + sudo umount "$isomount" 2>/dev/null + sudo mount "$SOURCE_FILE" "$isomount" 2>/dev/null if [ $? != 0 ];then - modprobe_failed=1 - else - modprobe_failed=0 - fi + status "Failed to mount the ISO file. Trying again after loading the 'udf' kernel module." + sudo modprobe udf - sudo mount "$SOURCE_FILE" "$PWD/isomount" - if [ $? != 0 ];then - if [ "$modprobe_failed" == 1 ] && [ ! -d "/lib/modules/$(uname -r)" ];then - error "The 'udf' kernel module is required to mount the ISO file (uupdump/$(basename $(echo "$PWD/uupdump"/*.ISO))), but all kernel modules are missing! Most likely, you upgraded kernel packages and have not rebooted yet. Try rebooting." + if [ $? != 0 ];then + modprobe_failed=1 else - error "Failed to mount ISO file ($(echo "$PWD/uupdump"/*.ISO)) to $PWD/isomount" + modprobe_failed=0 + fi + + sudo mount "$SOURCE_FILE" "$isomount" + if [ $? != 0 ];then + if [ "$modprobe_failed" == 1 ] && [ ! -d "/lib/modules/$(uname -r)" ];then + error "The 'udf' kernel module is required to mount $SOURCE_FILE, but all kernel modules are missing. Most likely, you upgraded kernel packages and have not rebooted yet. Try rebooting." + else + error "Failed to mount ISO file $SOURCE_FILE to $isomount" + fi fi fi + register_mount_cleanup "$isomount" fi - #unmount on exit - register_mount_cleanup "$PWD/isomount" mkdir -p "$PWD"/bootpart status "Copying files from ISO file to $PWD:" - echo " - Boot files" - cp -r "$PWD/isomount/boot" "$PWD"/bootpart || error "Failed to copy $PWD/isomount/boot to $PWD/bootpart" - echo " - EFI files" - cp -r "$PWD/isomount/efi" "$PWD"/bootpart || error "Failed to copy $PWD/isomount/efi to $PWD/bootpart" - mkdir -p "$PWD"/bootpart/sources || error "Failed to make folder: $PWD/bootpart/sources" - echo " - boot.wim" - cp "$PWD/isomount/sources/boot.wim" "$PWD"/bootpart/sources || error "Failed to copy $PWD/isomount/sources/boot.wim to $PWD/bootpart/sources" + echo " - Startup environment" + copy_startup_environment_with_progress "$isomount" "$PWD/bootpart" local || error "Failed to copy the startup environment from $isomount" if [ -f "$PWD/isomount/sources/install.wim" ];then install_image="$PWD/isomount/sources/install.wim" elif [ -f "$PWD/isomount/sources/install.esd" ];then @@ -1279,22 +3381,29 @@ elif [[ "$SOURCE_FILE" == *'.ISO' ]] || [[ "$SOURCE_FILE" == *'.iso' ]];then error "The ISO file does not contain sources/install.wim or sources/install.esd. Use an official Windows ARM64 ISO." fi echo " - $(basename "$install_image")" - cp "$install_image" "$PWD/install.wim" || error "Failed to copy $install_image to $PWD/install.wim" + copy_local_file_with_progress "$(basename "$install_image")" "$install_image" "$PWD/install.wim" || error "Failed to copy $install_image to $PWD/install.wim" touch "$PWD/alldone" #mark this folder of microsoft stuff as complete echo "All necessary files have been copied out. Your ISO file will not be needed for future flashes." status "Unmounting ISO file" - sudo umount "$PWD/isomount" || echo_red "Warning: failed to unmount $PWD/isomount" #failure is non-fatal - rmdir "$PWD/isomount" #remove mountpoint + if is_macos ;then + hdiutil detach "$ISO_DEVICE" || echo_red "Warning: failed to detach $ISO_DEVICE" + else + sudo umount "$isomount" || echo_red "Warning: failed to unmount $isomount" #failure is non-fatal + rmdir "$isomount" #remove mountpoint + fi #Change working directory back to $DL_DIR cd .. fi +fi #end of the preparation half a password retry skips + if [ "$DRY_RUN" == 1 ];then - status "Exiting $(basename "$0") script now because the DRY_RUN variable was set to '1'." + status "Exiting the $WOR_APP_TITLE script now because DRY_RUN=1 was set." + cli_pause exit 0 fi @@ -1303,17 +3412,27 @@ if [ ! -b "$DEVICE" ];then error "Device $DEVICE is not a valid block device! Available devices:\n$(list_devs)" fi +if is_macos ;then + darwin_flash_device + exit $? +fi + echo -status "Formatting $DEVICE - \e[93mThere is no turning back now." +phase "Partitioning and formatting $DEVICE" +printf ' There is no turning back now.\n' 1>&2 sync -sudo umount -ql $(get_partition "$DEVICE" all) +partitions=() +while IFS= read -r partition ;do + [ -n "$partition" ] && partitions+=("$partition") +done < <(get_partition "$DEVICE" all) +[ "${#partitions[@]}" -eq 0 ] || sudo umount -ql "${partitions[@]}" sync status "Creating partition table" sudo parted -s "$DEVICE" mklabel gpt || error "Failed to make GPT partition table on ${DEVICE}!" sync status "Generating partitions" sudo parted -s "$DEVICE" mkpart primary 1MB 1000MB || error "Failed to make 1GB primary partition 1 on ${DEVICE}!" -sudo parted -s "$DEVICE" set 1 msftdata on || error "Failed to enable msftdata flag on $DEVICE partition 1" +sudo parted -s "$DEVICE" set 1 esp on || error "Failed to enable the EFI System Partition flag on $DEVICE partition 1" sync if [ $CAN_INSTALL_ON_SAME_DRIVE == 1 ];then sudo parted -s "$DEVICE" mkpart primary 1000MB 19000MB || error "Failed to make 19GB primary partition 2 on ${DEVICE}!" @@ -1322,14 +3441,24 @@ else fi sudo parted -s "$DEVICE" set 2 msftdata on || error "Failed to enable msftdata flag on $DEVICE partition 2" sync +sudo partprobe "$DEVICE" || error "Failed to refresh the partition table on $DEVICE" status "Generating filesystems" -PART1="$(get_partition "$DEVICE" 1)" -PART2="$(get_partition "$DEVICE" 2)" +PART1='' +PART2='' +partition_wait_attempt=0 +while [ "$partition_wait_attempt" -lt 50 ] ;do + PART1="$(get_partition "$DEVICE" 1)" + PART2="$(get_partition "$DEVICE" 2)" + [ -b "$PART1" ] && [ -b "$PART2" ] && break + sleep 0.1 + partition_wait_attempt=$((partition_wait_attempt+1)) +done +[ -b "$PART1" ] && [ -b "$PART2" ] || error "Partition devices for $DEVICE did not become available" echo "Partition 1: $PART1, Partition 2: $PART2" -errors="$(sudo mkfs.fat -F 32 "$PART1" 2>&1)" || error "Failed to create FAT partition on $PART1\nErrors:\n$errors" -errors="$(sudo mkfs.exfat "$PART2" 2>&1)" || error "Failed to create EXFAT partition on $PART2\nErrors:\n$errors" +errors="$(sudo mkfs.fat -F 32 -n WOR_BOOT "$PART1" 2>&1)" || error "Failed to create FAT partition on $PART1\nErrors:\n$errors" +errors="$(sudo mkfs.exfat -n WOR_INSTALL "$PART2" 2>&1)" || error "Failed to create EXFAT partition on $PART2\nErrors:\n$errors" mntpnt="/media/$USER/wor-flasher" status "Mounting ${DEVICE} device to $mntpnt" @@ -1362,32 +3491,37 @@ fi register_mount_cleanup "$mntpnt/bootpart" register_mount_cleanup "$mntpnt/winpart" -status "Copying files to $DEVICE:" -echo " - Startup environment" -sudo cp -r "$PWD/$winfiles/bootpart"/* "$mntpnt"/bootpart || error "Failed to copy $PWD/$winfiles/bootpart to $mntpnt/bootpart" -echo " - Installation files" -sudo cp "$PWD/$winfiles/install.wim" "$mntpnt"/winpart || error "Failed to copy $PWD/$winfiles/install.wim to $mntpnt/winpart" -echo " - EFI files" +phase "Copying files to $DEVICE:" +report_copy_task 0 "Startup environment" +copy_startup_environment_with_progress "$PWD/$winfiles/bootpart" "$mntpnt/bootpart" || error "Failed to copy $PWD/$winfiles/bootpart to $mntpnt/bootpart" +report_copy_task 15 "Installation files" +copy_file_with_progress install.wim "$PWD/$winfiles/install.wim" "$mntpnt/winpart/install.wim" || error "Failed to copy $PWD/$winfiles/install.wim to $mntpnt/winpart" +report_copy_task 30 "EFI files" sudo cp -r "$PWD/peinstaller/efi" "$mntpnt"/bootpart || error "Failed to copy $PWD/peinstaller/efi to $mntpnt/bootpart" -echo " - PE installer" -errors="$(sudo wimupdate "$mntpnt"/bootpart/sources/boot.wim 2 --command="add peinstaller/winpe/2 /" 2>&1)" || error "The wimupdate command failed to add $PWD/peinstaller to boot.wim\nErrors:\n$errors" +report_copy_task 45 "PE installer" +configure_pe_settings_ini +configure_pe_prefinalize +sudo wimupdate "$mntpnt"/bootpart/sources/boot.wim 2 --command="add peinstaller/winpe/2 /" || error "The wimupdate command failed to add $PWD/peinstaller to boot.wim" if [ "$RPI_MODEL" == 5 ];then #no wor drivers available for pi5, so make a dummy file to allow boot - echo " - ARM64 drivers" + report_copy_task 60 "ARM64 drivers" echo -n > "$PWD/critical" - errors="$(sudo wimupdate "$mntpnt"/bootpart/sources/boot.wim 2 --command="add critical /drivers/critical" 2>&1)" || error "The wimupdate command failed to add $PWD/critical to boot.wim\nErrors:\n$errors" + sudo wimupdate "$mntpnt"/bootpart/sources/boot.wim 2 --command="add critical /drivers/critical" || error "The wimupdate command failed to add $PWD/critical to boot.wim" rm "$PWD/critical" else - echo " - ARM64 drivers" - errors="$(sudo wimupdate "$mntpnt"/bootpart/sources/boot.wim 2 --command="add driverpackage /drivers" 2>&1)" || error "The wimupdate command failed to add $PWD/driverpackage to boot.wim\nErrors:\n$errors" + report_copy_task 60 "ARM64 drivers" + sudo wimupdate "$mntpnt"/bootpart/sources/boot.wim 2 --command="add driverpackage /drivers" || error "The wimupdate command failed to add $PWD/driverpackage to boot.wim" fi -echo " - UEFI firmware" +report_copy_task 75 "Windows Setup configuration" +install_windows_setup_configuration "$mntpnt/bootpart" "$mntpnt/winpart" || error "Failed to install the Windows Setup configuration." + +report_copy_task 90 "UEFI firmware" sudo cp -r "$PWD/pi${RPI_MODEL}-uefipackage"/* "$mntpnt"/bootpart || error "Failed to copy $PWD/pi${RPI_MODEL}-uefipackage to $mntpnt/bootpart" -if [ ! -z "$CONFIG_TXT" ];then +if [ ! -z "$CONFIG_TXT" ] && [ "$APPLY_CUSTOM_CONFIG_TXT" == 1 ];then status "Customizing config.txt according to the CONFIG_TXT variable" echo "$CONFIG_TXT" | sudo tee "$mntpnt"/bootpart/config.txt >/dev/null fi @@ -1399,9 +3533,11 @@ if [ $RPI_MODEL == 3 ];then sudo dd if=$PWD/peinstaller/pi3/gptpatch.img of="$DEVICE" conv=fsync || error "The 'dd' command failed to flash $PWD/peinstaller/pi3/gptpatch.img to $DEVICE" fi -status -n "Allowing pending writes to finish... " -sync -echo_green "Done" +if [ "$SKIP_IMAGE_VERIFICATION" == 1 ];then + echo_red "Skipping written-image verification (SKIP_IMAGE_VERIFICATION=1). This is not recommended." +else + verify_written_image "$DEVICE" "$PART1" "$PART2" "$mntpnt/bootpart" "$mntpnt/winpart" "$PWD/$winfiles/install.wim" +fi status "Ejecting drive $DEVICE" sudo umount "$PART1" || echo_red "Warning: the umount command failed to unmount all partitions within $DEVICE" @@ -1410,4 +3546,5 @@ sudo umount -q "$mntpnt"/bootpart &>/dev/null sudo umount -q "$mntpnt"/winpart &>/dev/null sudo eject "$DEVICE" &>/dev/null sudo rmdir "$mntpnt"/bootpart "$mntpnt"/winpart || echo_red "Warning: Failed to remove the mountpoint folder: $mntpnt" -status "$(basename "$0") script has completed." +phase "$WOR_APP_TITLE script has completed." +cli_pause diff --git a/logo.png b/logo.png deleted file mode 100644 index ab4a9a0..0000000 Binary files a/logo.png and /dev/null differ diff --git a/next-steps.svg b/next-steps.svg deleted file mode 100644 index 3e393b7..0000000 --- a/next-steps.svg +++ /dev/null @@ -1,75 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<svg viewBox="-49.646 -84.94 1723.738 640.797" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:bx="https://boxy-svg.com"> - <defs> - <style bx:fonts="Product Sans Medium" bx:pinned="true">@font-face { font-family: "Product Sans Medium"; font-style: normal; font-weight: normal; font-stretch: normal; font-unicode-range: U+0-10FFFF; font-variant: normal; font-feature-settings: normal; src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAASAQAABAAgRkZUTYCuMfUAAWh8AAAAHEdERUYOQRBJAAEVJAAAAEpHUE9TV02NVgABHVAAAEsqR1NVQqDDbjsAARVwAAAH3k9TLzJpZB4uAAABqAAAAGBjbWFwzb+/YAAAC8gAAAOuY3Z0ICFGFdYAACGIAAAA/mZwZ224slEGAAAPeAAAD3NnYXNwAAcAGwABFRgAAAAMZ2x5ZqbN4XgAACdsAADZnGhlYWQWYN8FAAABLAAAADZoaGVhD+cNmAAAAWQAAAAkaG10eJV7WJgAAAIIAAAJwGxvY2E/PAeiAAAiiAAABOJtYXhwBeYCAwAAAYgAAAAgbmFtZcVCJ5QAAQEIAAACqXBvc3TaWWHUAAEDtAAAEWRwcmVwDY+z/QAAHuwAAAKaAAEAAAABAk4NmPsEXw889QAfA+gAAAAA01Z0RwAAAADXsyTS/1D+9wzUA8IAAAAIAAIAAAAAAAAAAQAAA8D/AwAADNT/UP9QDNQAAQAAAAAAAAAAAAAAAAAAAnAAAQAAAnAA5QAKAF0ABAACABAALwCaAAACugCPAAMAAQAEAjUB9AAFAAACigJYAAAASwKKAlgAAAFeADIBJgAAAgsFAwMFAgQCA4AAAAcAAAAAAAAAAAAAAABHT09HAYAADSIVA8D/AwAAA8YBMiAAAZ8AAAAAAeoCzAAAACAAEAI6AEgAAAAAAAAAAAAAAAABTQAAANwAAAD7ADABXQAsAoUAFwIiABgDOwAgAngAJAC9ACwBWABAAVgAFAHCACgCLwA4APwALAG5ADgA+wAwAcoAGwKKACwBlAAoAhQAKAIiACQCWgAoAjQAKAIuACgCFgAoAjAAKAIuACgA+wAwAPwALAIHADgCOwA4AgcAPAH3ACQDaQAsArQADAJjAEgC3AAlAsEASAIwAEgCHABIAyYAJQK9AEgBAQBIAhwAGAKFAEgCBABIA0sASALFAEgDMAAlAkIASAMwACUCVwBIAjQAGAIxABgCjwA4ApAADAOqABAClAAQAmcADAJEACABSgBIAcoAGwFKACAB7QAMAekAAAGlAGACCwAcAkwAOAIVABwCTAAcAiIAHAF/ABgCSwAcAi4AOADvAC0A7//RAfoAOADfADgDYgA4Ai4AOAI4ABwCSAA0AkgAHAGBADgB1QAYAXoAGAIuADQCDwAEAwsABAHtAAgCDgAEAdUAIAFIABgA9gBIAUgAIAIeACAA3AAAAPsAMAISABwCEAAgAlsAPAJaAAwA9gBIAiMAKAGqADsCFAAkAeYAKAJHACgCQgAwAbkAOAIUACQBmAA8AUUAIAIvADgBNwAYAUkAGAGlAJgCLgA0AnkAGADDABQBlQBHAQEAGAINACQCRwA8AvEAMAMYADADRAAwAewAJQK0AAwCtAAMArQADAK0AAwCtAAMArQADAPWAAQC3AAlAjAASAIwAEgCMABIAjAASAEBAAYBAQBIAQH/5QEB//QC/QA4AsUASAMwACUDMAAlAzAAJQMwACUDMAAlAkEAPAMwACUCjwA4Ao8AOAKPADgCjwA4AmcADAJCAEgCPgBIAgsAHAILABwCCwAcAgsAHAILABwCCwAcA3cAHAIVABwCIgAcAiIAHAIiABwCIgAcAO//+wDvAEAA7//cAO//7QI+ACgCLgA4AjgAHAI4ABwCOAAcAjgAHAI4ABwCLwA4AjgAHAIuADQCLgA0Ai4ANAIuADQCDgAEAkwAOAIOAAQCtAAMAgsAHAK0AAwCCwAcArQADAILABwC3AAlAhUAHALcACUCFQAcAtwAJQIVABwC3AAlAhUAHALBAEgCrgAcAr7/+AJKABwCMABIAiIAHAIwAEgCIgAcAjAASAIiABwCMABIAiIAHAIwAEgCIgAcAyYAJQJLABwDJgAlAksAHAMmACUCSwAcAyYAJQJLABwCvQBIAi4AOAK9AAkCLv/7AQH/1QDv/8wBAf/+AO//6QEB//4A7//1AQEAEgDvAAgBAQBDAO8AQAIcABgA7//RAn0ASAH1ADgB7wA4AgQASADfADgCBABIAN8AMAIEAEgBWgBMAgQASAJTACoBiAAeAsUASAIuADgCxQBIAi4AOALFAEgCLgA4AsUASAIuADgDMAAlAjgAHAMwACUCOAAcAzAAJQI4ABwEewAlA7YAHAJXAEgBgQA4Ak4ASAGBADECVwBIAYEAMAI0ABgB1QAYAjQAGAHVABgCNAAYAdUAGAI0ABgB1QAYAjEAGAF6ABgCMQAYAXoAGAIxABgBegAYAo8AOAIuADQCjwA4Ai4ANAKPADgCLgA0Ao8AOAIuADQCjwA4Ai4ANAKPADgCLgA0A6oAEAMLAAQCZwAMAg4ABAJnAAwCRAAgAdUAIAJEACAB1QAgAkQAIAHVACACTQAnArQADAILABwD1gAEA3cAHAMwACUCOAAcAjQAGAHVABgCMQAYAXoAGADv/9EBrAA5AawAOQGYAEgA+wA7AZQAYAGZACABKAAoASgAKAD8ACwBpQCBAbAAIAK0AAwAwwAUAmz/sQL4/7EBPf+xAxL/sQLN/7EC//+xAO//wQK0AAwCYwBIAgQASAKsAAwCMABIAkQAIAK9AEgDPgAlAQEASAJ5AEgCpAAMA0sASALFAEgCBQAcAzAAJQKmAEgCQgBIAgIAKAIxABgCZwAMAx8AJQKUABAC8QA4AxwAKwEB//ICZwAMAkgAHAH9ACQCLgA4AO8AQAIpADQCSAAcAj4ASAIqAAQCOAAcAesAJAHkABgCLgA4ApsALADvAEAB7wA4Ag4ABAIPAAQCDgAYAjgAHALMABgCTAA4AhEAHAJXABwB0wAYAikANALYACwB7QAIAr0ANAMTABwA7//tAikANAI4ABwCKQA0AxMAHAJ9ADQCcwAEAtj/sQJzAAQDhQAoAjAASAIwAEgDAgAYAhEASALZACUCNAAYAQEASAEB//ICHAAYBDgAGAQNAEgC/AAYAnkASAK9AEgCaAAMAqYASAK0AAwCYwBIAmMASAIEAEgDEwAoAjAASAOhAAICKwAkAr0ASAK9AEgCeQBIAs0AGANjAEgCvQBIAzcAJQKmAEgCQgBIAtwAJQI3ABgCaAAMAy8AJQKUABAC0gBIAlYAKAOTAEgDxQBIAvgAGANGAEgCagBIAtkAJAQ5AEgCbgAhAg0AHAJVADgB9wA4AakAOAKIABwCIgAcAuX//AHrACgCLgA4Ai4AOAHvADgCZQAqAqAANAIyADgCOAAcAiEAOAJIADQCFQAcAbsAGAIOAAQC7wAlAe0ACAJRADgCAgAgAwsAOAM7ADgCUQAYAscAOAH9ADgCDwAYAxYAQAHsAAACHgAcAh4AHAIu//sBlQA4Ag8AHAHVABgA7wAtAO//7QDv/9EDfgAqA04AOAIu//sB7wA4Ai4AOAIOAAQCLwA4AhEASAGpADgCZwAMAgcABAOqABADCwAEA6oAEAMLAAQDqgAQAwsABAJnAAwCDgAEAlkAOAOJADgA+wAwAPsAMAD7ADABwAAwAcAAMAG7ADABmwBQAtgAMAFzACgBcwA8ALX/UAFhABwCMAAYAhUAIAPcACACQgAcAoEAGAIdABgChQAY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ACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQEEAKMAhACFAL0AlgDoAIYAjgCLAJ0AqQCkAQUAigDaAIMAkwDyAPMAjQCXAIgAwwDeAPEAngCqAPUA9AD2AKIArQDJAMcArgBiAGMAkABkAMsAZQDIAMoAzwDMAM0AzgDpAGYA0wDQANEArwBnAPAAkQDWANQA1QBoAOsA7QCJAGoAaQBrAG0AbABuAKAAbwBxAHAAcgBzAHUAdAB2AHcA6gB4AHoAeQB7AH0AfAC4AKEAfwB+AIAAgQDsAO4AugEGAQcBCAEJAQoBCwD9AP4BDAENAQ4BDwD/AQABEAERARIBAQETARQBFQEWARcBGAEZARoBGwEcAR0BHgD4APkBHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgD6ANcBLwEwATEBMgEzATQBNQE2ATcBOAE5AToA4gDjATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSACwALEBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQA5ADlAVUBVgFXAVgBWQFaAVsBXAFdAV4BXwFgAWEBYgFjAWQBZQFmAWcBaAFpAWoAuwFrAWwBbQFuAOYA5wFvAXABcQFyAXMBdAF1AXYBdwF4AXkBegDYAOEA2wDcAN0A2QF7AXwBfQF+AX8BgAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEBkgGTAZQBlQGWAZcBmAGZAZoBmwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQGuAa8BsAGxAbIBswG0AbUAmwG2AbcBuAG5AboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHLAcwBzQHOAc8B0AHRAdIB0wHUAdUB1gHXAdgB2QHaAdsB3AHdAd4B3wHgAeEB4gHjAeQB5QHmAecB6AHpAeoB6wHsAe0B7gHvAfAB8QHyAfMB9AH1AfYB9wH4AfkB+gH7AfwB/QH+Af8CAAIBAgICAwIEAgUCBgIHAggCCQIKAgsCDAINAg4CDwIQAhECEgITAhQCFQIWAhcCGAIZAhoCGwIcAh0CHgIfAiACIQIiAiMCJAIlAiYCJwIoAikCKgIrAiwCLQIuAi8CMAIxAjICMwI0ALIAswC2ALcAxAC0ALUAxQCHAKsAvgC/ALwCNQI2AjcCOAI5AjoCOwI8Aj0CPgI/AkACQQDvAkICQwJEAkUCRgJHAkgCSQJKAksCTAJNAk4CTwJQAlECUgJTAlQCVQJWAlcCWAJZAloCWwJcAl0CXgJfAmACYQJiAmMCZAJlAmYCZwJoAmkCagJrAmwCbQJuAm8CcAJxAnICcwJ0AnUCdgJ3AngCeQJ6AnsCfAJ9An4CfwKAAoECggKDAoQChQKGAocCiAKJAooHdW5pMDAwRAROVUxMB3VuaTAwQTAHdW5pMDBBRAdBbWFjcm9uB2FtYWNyb24GQWJyZXZlBmFicmV2ZQdBb2dvbmVrB2FvZ29uZWsLQ2NpcmN1bWZsZXgLY2NpcmN1bWZsZXgKQ2RvdGFjY2VudApjZG90YWNjZW50BkRjYXJvbgZkY2Fyb24GRGNyb2F0B0VtYWNyb24HZW1hY3JvbgZFYnJldmUGZWJyZXZlCkVkb3RhY2NlbnQKZWRvdGFjY2VudAdFb2dvbmVrB2VvZ29uZWsGRWNhcm9uBmVjYXJvbgtHY2lyY3VtZmxleAtnY2lyY3VtZmxleApHZG90YWNjZW50Cmdkb3RhY2NlbnQHdW5pMDEyMgd1bmkwMTIzC0hjaXJjdW1mbGV4C2hjaXJjdW1mbGV4BEhiYXIEaGJhcgZJdGlsZGUGaXRpbGRlB0ltYWNyb24HaW1hY3JvbgZJYnJldmUGaWJyZXZlB0lvZ29uZWsHaW9nb25lawtKY2lyY3VtZmxleAtqY2lyY3VtZmxleAd1bmkwMTM2B3VuaTAxMzcMa2dyZWVubGFuZGljBkxhY3V0ZQZsYWN1dGUHdW5pMDEzQgd1bmkwMTNDBkxjYXJvbgZsY2Fyb24ETGRvdAZOYWN1dGUGbmFjdXRlB3VuaTAxNDUHdW5pMDE0NgZOY2Fyb24GbmNhcm9uA0VuZwNlbmcHT21hY3JvbgdvbWFjcm9uBk9icmV2ZQZvYnJldmUNT2h1bmdhcnVtbGF1dA1vaHVuZ2FydW1sYXV0BlJhY3V0ZQZyYWN1dGUHdW5pMDE1Ngd1bmkwMTU3BlJjYXJvbgZyY2Fyb24GU2FjdXRlBnNhY3V0ZQtTY2lyY3VtZmxleAtzY2lyY3VtZmxleAd1bmkwMTVFB3VuaTAxNUYHdW5pMDE2Mgd1bmkwMTYzBlRjYXJvbgZ0Y2Fyb24EVGJhcgR0YmFyBlV0aWxkZQZ1dGlsZGUHVW1hY3Jvbgd1bWFjcm9uBlVicmV2ZQZ1YnJldmUFVXJpbmcFdXJpbmcNVWh1bmdhcnVtbGF1dA11aHVuZ2FydW1sYXV0B1VvZ29uZWsHdW9nb25lawtXY2lyY3VtZmxleAt3Y2lyY3VtZmxleAtZY2lyY3VtZmxleAt5Y2lyY3VtZmxleAZaYWN1dGUGemFjdXRlClpkb3RhY2NlbnQKemRvdGFjY2VudAd1bmkwMTkyCkFyaW5nYWN1dGUKYXJpbmdhY3V0ZQdBRWFjdXRlB2FlYWN1dGULT3NsYXNoYWN1dGULb3NsYXNoYWN1dGUHdW5pMDIxOAd1bmkwMjE5B3VuaTAyMUEHdW5pMDIxQgd1bmkwMjM3B3VuaTAzNzQHdW5pMDM3NQd1bmkwMzdFBXRvbm9zDWRpZXJlc2lzdG9ub3MKQWxwaGF0b25vcwlhbm90ZWxlaWEMRXBzaWxvbnRvbm9zCEV0YXRvbm9zCUlvdGF0b25vcwxPbWljcm9udG9ub3MMVXBzaWxvbnRvbm9zCk9tZWdhdG9ub3MRaW90YWRpZXJlc2lzdG9ub3MFQWxwaGEEQmV0YQVHYW1tYQd1bmkwMzk0B0Vwc2lsb24EWmV0YQNFdGEFVGhldGEESW90YQVLYXBwYQZMYW1iZGECTXUCTnUCWGkHT21pY3JvbgJQaQNSaG8FU2lnbWEDVGF1B1Vwc2lsb24DUGhpA0NoaQNQc2kHdW5pMDNBOQxJb3RhZGllcmVzaXMPVXBzaWxvbmRpZXJlc2lzCmFscGhhdG9ub3MMZXBzaWxvbnRvbm9zCGV0YXRvbm9zCWlvdGF0b25vcxR1cHNpbG9uZGllcmVzaXN0b25vcwVhbHBoYQRiZXRhBWdhbW1hBWRlbHRhB2Vwc2lsb24EemV0YQNldGEFdGhldGEEaW90YQVrYXBwYQZsYW1iZGECbnUCeGkHb21pY3JvbgNyaG8HdW5pMDNDMgVzaWdtYQN0YXUHdXBzaWxvbgNwaGkDY2hpA3BzaQVvbWVnYQxpb3RhZGllcmVzaXMPdXBzaWxvbmRpZXJlc2lzDG9taWNyb250b25vcwx1cHNpbG9udG9ub3MKb21lZ2F0b25vcwd1bmkwM0QxB3VuaTAzRDIHdW5pMDNEMwd1bmkwM0Q0B3VuaTAzRDYHdW5pMDQwMAd1bmkwNDAxB3VuaTA0MDIHdW5pMDQwMwd1bmkwNDA0B3VuaTA0MDUHdW5pMDQwNgd1bmkwNDA3B3VuaTA0MDgHdW5pMDQwOQd1bmkwNDBBB3VuaTA0MEIHdW5pMDQwQwd1bmkwNDBEB3VuaTA0MEUHdW5pMDQwRgd1bmkwNDEwB3VuaTA0MTEHdW5pMDQxMgd1bmkwNDEzB3VuaTA0MTQHdW5pMDQxNQd1bmkwNDE2B3VuaTA0MTcHdW5pMDQxOAd1bmkwNDE5B3VuaTA0MUEHdW5pMDQxQgd1bmkwNDFDB3VuaTA0MUQHdW5pMDQxRQd1bmkwNDFGB3VuaTA0MjAHdW5pMDQyMQd1bmkwNDIyB3VuaTA0MjMHdW5pMDQyNAd1bmkwNDI1B3VuaTA0MjYHdW5pMDQyNwd1bmkwNDI4B3VuaTA0MjkHdW5pMDQyQQd1bmkwNDJCB3VuaTA0MkMHdW5pMDQyRAd1bmkwNDJFB3VuaTA0MkYHdW5pMDQzMAd1bmkwNDMxB3VuaTA0MzIHdW5pMDQzMwd1bmkwNDM0B3VuaTA0MzUHdW5pMDQzNgd1bmkwNDM3B3VuaTA0MzgHdW5pMDQzOQd1bmkwNDNBB3VuaTA0M0IHdW5pMDQzQwd1bmkwNDNEB3VuaTA0M0UHdW5pMDQzRgd1bmkwNDQwB3VuaTA0NDEHdW5pMDQ0Mgd1bmkwNDQzB3VuaTA0NDQHdW5pMDQ0NQd1bmkwNDQ2B3VuaTA0NDcHdW5pMDQ0OAd1bmkwNDQ5B3VuaTA0NEEHdW5pMDQ0Qgd1bmkwNDRDB3VuaTA0NEQHdW5pMDQ0RQd1bmkwNDRGB3VuaTA0NTAHdW5pMDQ1MQd1bmkwNDUyB3VuaTA0NTMHdW5pMDQ1NAd1bmkwNDU1B3VuaTA0NTYHdW5pMDQ1Nwd1bmkwNDU4B3VuaTA0NTkHdW5pMDQ1QQd1bmkwNDVCB3VuaTA0NUMHdW5pMDQ1RAd1bmkwNDVFB3VuaTA0NUYHdW5pMDQ5MAd1bmkwNDkxB3VuaTA0QjAHdW5pMDRCMQZXZ3JhdmUGd2dyYXZlBldhY3V0ZQZ3YWN1dGUJV2RpZXJlc2lzCXdkaWVyZXNpcwZZZ3JhdmUGeWdyYXZlB3VuaTIwNzQHdW5pMjBBMwd1bmkyMEE0B3VuaTIwQTkHdW5pMjBBQgRFdXJvB3VuaTIwQUUHdW5pMjBCMQd1bmkyMEI4B3VuaTIwQjkHdW5pMjBCQQd1bmkyMEJEB3VuaTIxMTYHdW5pMjIxNQNmX2kDZl9sB3VuaUY4RkYNb25lLm51bWVyYXRvcg90d28uZGVub21pbmF0b3INdHdvLm51bWVyYXRvcg90aHJlZS5udW1lcmF0b3IQZm91ci5kZW5vbWluYXRvcg5mb3VyLm51bWVyYXRvcgVyLmFsdAd1bmkwM0JDB3VuaTIxMDMHdW5pMjEwOQd1bmkyMTkwB3VuaTIxOTEHdW5pMjE5Mgd1bmkyMTkzB3VuaTIxOTYHdW5pMjE5Nwd1bmkyMTk4B3VuaTIxOTkHdW5pMjYzOQd1bmkyNjNBB3VuaTI3ODAHdW5pMjc4MQd1bmkyNzgyB3VuaTI3ODMHdW5pMjc4NAd1bmkyNzg1B3VuaTI3ODYHdW5pMjc4Nwd1bmkyNzg4B3VuaTI3OEEHdW5pMjc4Qgd1bmkyNzhDB3VuaTI3OEQHdW5pMjc4RQd1bmkyNzhGB3VuaTI3OTAHdW5pMjc5MQd1bmkyNzkyA2ZfYgNmX2YFZl9mX2IFZl9mX2gFZl9mX2kFZl9mX2oFZl9mX2sFZl9mX2wFZl9mX3QDZl9oA2ZfagNmX2sDZl90A3JfZgVyX2ZfZgNyX3QDdF9mA3RfdAtHb29nbGUubG9nbwZHLmxvZ28Gby5sb2dvBmcubG9nbwZsLmxvZ28GZS5sb2dvCWNhcm9uLmFsdA1kb3RhY2NlbnQuY2FwC3VuaTAwMjAwMzI2BUsuYWx0BWEuYWx0DnF1b3RlcmlnaHQuYWx0CXRocmVlLmNhcAABAAIABwAK//8ADwABAAAADAAAADoAQgACAAcAAQInAAECKAIqAAICKwIyAAECMwJmAAICZwJnAAECaAJoAAICaQJvAAEABAAAAAIAAAABAAAAAQAAAAAAAQAAAAoAeAC4AARERkxUABpjeXJsACpncmVrADpsYXRuAEoABAAAAAD//wADAAAAAQACAAQAAAAA//8AAwAAAAEAAgAEAAAAAP//AAMAAAABAAIAEAACTU9MIAAcUk9NIAAcAAD//wADAAAAAQACAAD//wABAAMABGNhbHQAGmNsaWcALmxpZ2EANGxvY2wAOgAAAAgAAAABAAIAAwAEAAUABgAHAAAAAQAIAAAAAQAJAAAAAQAQABEAJAAsADQAPABEAEwAVABeAGYAbgB2AH4AhgCOAJYAngCmAAYAAAABAIoABgAAAAEBEAAGAAAAAQGGAAYAAAABAewABgAAAAECUgAGAAAAAQKgAAYAAAACAu4DHAAEAAAAAQNUAAQAAAABA+oABAAAAAEFUgAEAAAAAQYWAAEAAAABBjIABAAAAAEGNgABAAAAAQZKAAQAAAABBk4AAQAAAAEGYgABAAAAAQZmAAMAAAADAC4ANAA6AA0AQABGAEwAUgBYAF4AZABqAHAAdgB8AIIAiAABAAAACgABAAEATAABAAEAVAABAAEAVAABAAEATAABAAEAUQABAAEASgABAAEARAABAAEAUQABAAEAVAABAAEATAABAAEAVAABAAEARAABAAEAWQABAAEASgABAAEAXQABAAEAWQADAAEAPAADACoAMAA2AAoAQgBIAE4AVABaAGAAZgBsAHIAeAABAAAACgABAAEATAABAAEAUQABAAEASgABAAECZAABAAEARAABAAEAUQABAAEAVAABAAEATAABAAEAVAABAAEARAABAAEAWQABAAEASgABAAEAXQABAAEAWQADAAIALAAyAAEAJgAJADgAPgBEAEoAUABWAFwAYgBoAAEAAAALAAEAAQBEAAEAAQJlAAEAAQJkAAEAAQBRAAEAAQBUAAEAAQBMAAEAAQBUAAEAAQBEAAEAAQBZAAEAAQBKAAEAAQBdAAEAAQBZAAMAAwA+AEQASgAEACYALAAyADgABQBQAFYAXABiAGgAAQAAAAwAAQABAFEAAQABAFQAAQABAEwAAQABAFQAAQABAmUAAQABAmUAAQABAmQAAQABAEQAAQABAFkAAQABAEoAAQABAF0AAQABAFkAAwAEACYALAAyADgAAQAgAAQAPgBEAEoAUAABAAAADQABAAEARAABAAECZgABAAECZQABAAECZQABAAECZAABAAEAWQABAAEASgABAAEAXQABAAEAWQADAAUAOAA+AEQASgBQAAQAIAAmACwAMgAAAAEAAAAOAAEAAQBZAAEAAQBKAAEAAQBdAAEAAQBZAAEAAQJnAAEAAQJmAAEAAQJlAAEAAQJlAAEAAQJkAAMAAQAaAAEAFAABACQAAQAAAA8AAQABAF0AAgABABUAHgAAAAIAAQAVAB4AAAADAAIAHgAmAAEAGAACADAAOAABAAAADwABAAEAXQABAAIABQBkAAIAAQAVAB4AAAABAAIABQBkAAIAAQAVAB4AAAABAJQAAwAMAFAAcgADAAgAIAAyAmMACwBUAFQATABRAEoARABRAFQATABUAioACABUAFQATABRAEoARAAsAioACABUAFQATABRAEoARABMAAQACgAQABYAHAI0AAIAKwI0AAIASwIzAAIAKAIzAAIASAAEAAoAEAAWABwCNAACACsCNAACAEsCMwACACgCMwACAEgAAQADAEwAdAB+AAEBXgAHABQA7gD4AQIBKAFCAUwAEgAmADIAPgBKAFYAYgBuAHoAhgCSAJoAogCqALIAugDCAMoA0gJQAAUADQAeAA4ADgJPAAUADQAdAA4ADgJOAAUADQAcAA4ADgJNAAUADQAbAA4ADgJMAAUADQAaAA4ADgJLAAUADQAZAA4ADgJKAAUADQAYAA4ADgJJAAUADQAXAA4ADgJIAAUADQAWAA4ADgJHAAMAHgAOAkYAAwAdAA4CRQADABwADgJEAAMAGwAOAkMAAwAaAA4CQgADABkADgJBAAMAGAAOAkAAAwAXAA4CPwADABYADgABAAQCNwACACMAAQAEAjoAAgAjAAQACgASABoAIAI+AAMAEgAOAj0AAwASAA0CPgACAA4CPQACAA0AAwAIAA4AFAI7AAIAFAI5AAIAQQI1AAIAEgABAAQCPAACACMAAgAGAAwCOAACACECNgACACMAAQAHAA0AEgAUAB8AIQBBAGEAAQDCAAMADACUALAADwAgACgAMAA4AEAASABQAFgAXgBkAGoAcAB2AHwAggJZAAMASwBZAlgAAwBLAFECVwADAEsAUAJWAAMASwBPAlUAAwBLAE4CVAADAEsATQJTAAMASwBHAloAAgBNAl0AAgBZAlwAAgBQAlsAAgBPAlIAAgBLAlEAAgBHAikAAgBRAigAAgBOAAMACAAQABYCXwADAEsASwJgAAIAWQJeAAIASwACAAYADAJiAAIAWQJhAAIASwABAAMASwBXAFkAAQAeAAEACAACAAYADgJkAAMAVABUAmUAAwBRAEoAAQABAEwAAQAGAiEAAQABAEQAAQAWAAEACAABAAQCZgAEAFQATABUAAEAAQBRAAEABgIjAAEAAQBEAAEAFgABAAgAAQAEAmgABABKAF0AWQABAAEAWQABAAYAPgABAAEAXQACAA4ABAFGAUcBSAFJAAEABAEeAR8BIgEjAAAAAQAAAAoAVABkAARERkxUABpjeXJsACZncmVrADJsYXRuAD4ABAAAAAD//wABAAAABAAAAAD//wABAAAABAAAAAD//wABAAAABAAAAAD//wABAAAAAWtlcm4ACAAAAAIAAAABAAIABgAQAAIAAAACABQA0gACAAAAAjwAPNQAAQCeAAQAAAAOACYAMAA+AD4ARABOAGAAagB0AH4AhACKAJAAfgACABb/4gAY//YAAwAXAAoAGgAKABsABQABABb/9gACABb/9gCC//YABAAW/+wAgP/2AIH/9gCC//YAAgAW/+IAgP/2AAIAGf/iABv/7AACABb/7AAX//YAAQAW/+IAAQC0/84AAQC0//YAAwBB/9gARP+wAIP/7AABAA4AEAAWABcAGAAZABoAGwAeACMAJQCDAJAAogImAAI6qgAEAAA0NjfKAFkASwAA//b/8f/x/+z/9v/2//b/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAA/+L/zv/s/+z/2AAAAAD/2P/OAAD/2AAAAAD/7P/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAA//YAAP/2AAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAD/zgAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/nP9+/+wAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAA//b/4gAA/+z/zgAAAAAAAAAAAAAAAAAAAAD/4gAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/sAAAAAD/9gAA/9j/7P/YAAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAD/xAAA/87/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAA/9gAAAAA/+L/2AAAAAAAAAAAAAAAAAAAAAD/2AAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/s//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+wAAAAAAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAA/+L/zgAAAAAAAAAAAAD/zgAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/87/nP/i/8T/xP/Y/8T/zv/YAAAAAP/iAAD/kgAA/8QAAAAA/84AAP/YAAD/pgAA/6b/2AAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/2P/YAAD/zv/s/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAAAAD/9gAA//YAAP/2/+z/7P/Y//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8T/sAAAAAD/zv/s/87/4gAAAAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAD/sAAA/87/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+z/ugAA/93/3f/n/87/2P/TAAAAAAAAAAD/tf/2//EAAAAAAAAAAAAAAAD/v//Y/87/9gAAAAAAAP/EAAAAAAAA/7D/zv/iAAAAAAAAAAAAAAAAAAAAAAAA//b/zv+m/9j/7P/s/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/ugAFAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAA//b/9gAA//YAAP/2//b/7AAA//H/4gAAAAAAAAAAAAD/7P/7AAD/9v/s//YAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAP/EAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAP/s/+L/4v/x/+z/7P/sAAAAAAAAAAAAAP/x/+wAAAAA//YAAP/2//YAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/9v/2AAAAAAAA//YAAP/2AAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAD/9gAA//sAAP/7/+L/2P/2/+z/4v/2//H/7AAA//b/4v/2//b/4v/2//YAAAAAAAAAAP/2AAAAAAAAAAD/9gAAAAAAAAAA//YAAAAAAAAAAP/sAAAAAP/iAAD/7AAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/x/+z/7P/2/+z/9v/sAAAAAP/2AAAAAP/2/+wAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA//YAAAAA//YAAP/2AAAAAAAAAAD/7AAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAP/s//b/9v/s/+z/9v/s/8T/uv+m//YAAP/s/+L/zv/i/+z/xP/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YABf/2AAAAAAAA//b/9v/sAAAAAAAAAAD/5wAAAAD/2P/s/8QAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/x//b/7AAA//b/9v/2//b/7P/2/+z/7AAA//v/7AAAAAD/7P/2AAD/5//s//YAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAP/sAAAAAP/YAAD/9gAAAAD/9gAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA//b/8f/x/+z/9v/2//b/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/8f/x/+z/9v/2//b/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAA//YAAAAA//b/8f/2//YAAP/7//b/7P/sAAAAAAAA//YAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/+IAAP/2/+L/2P/s/9P/4v/YAAAAAP/2AAAAAP/2/+wAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAA/87/7AAAAAAAAAAAAAD/7P/2AAAAAAAAAAAAAAAA/+z/7AAAAAAAAAAAAAAAAAAAAAAAAAAA//H/xAAA/9j/0//2/87/4v/YAAAAAAAAAAD/pv/2AAAAAAAA//YAAAAAAAD/tf/E/9j/9gAAAAD/7P+wAAAAAAAA/7r/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+cAAD/7P/i/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/4gAAAAD/9gAA//sAAP/2/+z/2P/2/93/2AAAAAD/7AAAAAD/2P/2AAD/4v/2//YAAAAAAAAAAP/2AAAAAAAAAAD/9gAA//v/9v/7AAAAAAAA//sAAAAAAAAAAP/YAAD/9gAAAAD/7AAA/8QAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAP/sAAAAAP/2AAAAAAAA/7r/kv9+/+z/4v/n/93/sP/E//H/pgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAP/s/+z/2AAAAAD/uv/Y/5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//b/7P/2AAAAAAAAAAAAAP/2AAD/7P/2//YAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+L/7AAA//YAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAA/+f/5//x//H/9v/s//b/9gAA//b/4gAAAAAAAAAAAAD/9gAAAAD/9gAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IAAP+1/+L/2P/E/7//uv/E/7//pv+SAAAAAP/E/7D/xP+6/7D/kv/Y/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IABf/YAAD/9gAA/8T/2P/E/9gAAAAA/9j/4gAAAAAAAP/O/7D/4v/Y/9gAAAAAAAAAAAAA/+wAAP+wAAAAAP/2/9j/4gAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/2/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IAAP/O/+z/9v/d//YAAP/2/7r/sP+cAAAAAP/O/87/sP/O/9P/pv/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/sAAD/9gAA/9j/xP/i/+IAAAAA/9j/4v/2AAAAAP/E/7D/4v/i/9j/7P/2AAAAAAAA/+wAAP/iAAAAAAAA/+wAAP/sAAAAAAAA//YAAP/Y/+L/7P/i/+z/7P/2/9P/xP+6AAAAAP/O/8n/xP/Y/9j/uv/s/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/s//YAAAAA/9j/4v/i//YAAAAA/+z/4gAAAAD/xP/Y/87/9v/s/+wAAAAAAAAAAAAA/+wAAP/iAAAAAAAA/+IAAAAA//YAAAAA/90AAAAA/+z/4v/s/+L/4v/OAAAAAP/sAAAAAP/s/+cAAAAA/+wAAAAAAAAAAP/sAAAAAAAAAAAAAP/iAAAAAAAA/+IAAAAAAAAAAAAAAAAAAAAA//b/2AAA//b/7AAA/9j/7AAAAAAAAP/sAAD/7AAAAAAAAAAAAAAAAAAA/+wAAP/2AAAAAP/2AAAAAAAAAAAAAAAA/9gAAP+r/87/2P+1/87/zv/Y/7X/nP+IAAAAAP+m/5z/sP/E/6v/nP/Y/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/2P/OAAAAAP/2/7r/xP+6/84AAAAA/9j/2AAAAAAAAP+w/8T/xP/OAAD/9gAAAAAAAAAA/9gAAP+6/+IAAP/x/9j/4v/2/+wAAAAA/+wAAAAA//b/7P/2/+z/4v/iAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//b/9gAA//b/9v/2AAAAAAAAAAAAAP/7//sAAAAA//sAAP/7AAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/8f/7//H/9v/sAAAAAAAAAAAAAAAAAAAAAAAA//v/7P/x//YAAP/EAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAA//v/+//7//sAAAAA//sAAP/2AAAAAP/EAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAD/+wAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sAAAAAAAAAAP/7AAAAAP/OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAA/+wAAAAA/+z/2AAAAAAAAAAAAAAAAAAAAAD/7AAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAAAAAAAAAAAAD/2AAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/4gAAAAAAAAAAAAAAAAAAAAF//b/8QAA//b/9v/2AAAAAAAAAAAAAAAFAAUAAAAAAAUAAP/2//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+z/9v/2AAAAAAAAAAAAAAAAAAAAAP/n/+L/uv/i/+z/xAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAFP/2AAAAAAAA//YAAAAA/+wAAAAAAAD/7AAAAAD/7AAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAFAAAAAAAAP/7AAD/+//7AAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/9gAA//YAAP/2AAAAAAAAAAAAAAAAAAD/7AAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAA/+wAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xAAAAAAAAAAAAAAAAP/iAAAAAAAA/+L/ugAAAAAAAAAAAAAAAP/iAAD/2AAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAD/+wAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAD/+wAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//EAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAP/iAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAAABQAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//v/9gAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4v/7//b/5wAA/+z/9v/2AAAAAAAAAAD/2P/7AAAAAAAA//sAAP/7AAD/4v/s//YAAAAAAAAAAP/iAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//H/8f/7//b/9v/sAAAAAAAAAAAAAAAAAAAAAAAA//v/7P/2//YAAP/YAAAAAAAAAAAAAP/OAAAAAAAAAAAAAAAA//b/9v/2//sAAAAA//YAAP/2AAAAAP/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAD/7AAAAAAAAAAAAAAAAAAA//H/7P/7//b/9v/sAAAAAAAAAAAAAAAAAAAAAAAA//v/7P/2//YAAP/iAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAA//v/+//7//sAAAAA//sAAP/7AAAAAP/EAAAAAAAAAAD/4gAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAA/+z/sAAA/9j/xAAA/87/4v/YAAAAAAAAAAD/sAAAAAAAAAAAAAAAAAAAAAD/xAAA/8T/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/zgAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAA/+L/2AAA/9j/2P/OAAAAAAAAAAD/xAAAAAAAAP/OAAAAAAAAAAD/ugAA/9gAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/7oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAA/8T/iP+wAAAAAP/i/8QAAAAA/9gAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+6AAAAAAAA/9gAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAA/+IAAAAA/+L/7P/Y/+L/7P/s/8T/iP/OAAAAAP/Y/84AAAAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAA/+L/4v/iAAAAAAAA/+IAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/ugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAACgAAAAoACgAKAAAAAAAAAAAAAP/n/+L/pv/O/+z/ugAK//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAD/4gAAAAD/2AAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAFAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//v/+//7//sAAAAA//sAAP/7AAAAAP/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAA/87/nP+wAAAAAP/Y/9j/kgAA/+L/ugAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAP+6AAAAAAAA/8T/7AAA//b/zgAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/E/+IAAP/O/+L/4v/s/7r/iP+SAAAAAP+6/7AAAAAA/8T/Qv/i/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/9v/YAAD/7AAA/9gAAAAAAAAAAAAAAAD/zgAAAAAAAP/EAAAAAAAA/9gAAAAA//YAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAAAA/+z/2AAA/+wAAAAAAAAAAAAA/+L/2P/E/+z/2AAAAAAAAAAAAAAAAAAAAAD/2AAA/9j/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAP/2AAr/+wAAAAUADwAFAAAAAAAAAAAAAP/7//sAAAAAAAoAAAAPAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAFAAAAAAAAAAX/9gAAAAAAAP/iAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUABQAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//v/9gAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/x//H/zv/O//b/zgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAD/4gAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//b/4v/iAAD/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/x//EAAAAA//EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAP/iAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+z/uv/O//b/ugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/sAAD/4gAAAAD/xAAA/8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoABQAFAAoAAAAAAAAAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAoAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUABQAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAJgABQAFAEsABgAGAFcABwAHAEYACAAIAA0ACgAKAA4ACwALAAIADAAMAEYADQANACwADgAOAFQADwAPAAMAEQARAEAAEgASADAAEwATAEAAFAAUAEoAFQAVAFMAGAAYABEAGQAZAAsAGgAaAAoAGwAbABAAHAAcAEkAHQAdAAcAHgAeAAwAHwAgAFUAJAAkAEIAJQAlAAQAJgAmABMAJwAnABQAKAAoABUAKQApABYAKgAqABcAKwArABgALAAsABkALQAuABoALwAvABwAMAAwAB0AMQAxAB4AMgAzABoANAA0AB8ANQA1ACAANgA2AB8ANwA3ACEAOAA4ACIAOQA5ACMAOgA6ACQAOwA7ACUAPAA8ACYAPQA9ACcAPgA+ACgAPwA/ACkAQABAACwAQQBBAAUAQgBCAFQARABEABIARgBGACoARwBHACsASABIAC0ASQBJAFYASgBKADEASwBLADIATABMADMATQBNAD0ATgBOADcATwBPADkAUABQADoAUQBRADsAUgBTAD0AVABUAD4AVQBVAD8AVgBWAFgAVwBXAEcAWABYAEgAWQBZAEwAWgBaAE0AWwBbAE4AXABcAE8AXQBdAFAAXgBeAFEAXwBfAFIAYABgACwAYgBiAFQAZABkAEsAZQBlAAkAbQBtAC8AbwBvADUAcQBxADAAcgByAC8AeQB5AE0AewB7AEEAfwB/ADYAgwCDAA8AhACJABMAigCKABcAiwCLABUAjACPABcAkQCTABsAlACUABYAlQCVABoAlgCaAB8AnACcAB8AnQCgACQAoQChACgAogCiAAEAowCjADQApACpACoAqgCqADEAqwCrAC0ArACvADEAsACwADcAsQCzADgAtAC0AAgAtQC1AD0AtgC6AD4AvAC8AD4AvQDAAE0AwQDBAFEAwgDCACsAwwDDAFEA9QD1ADwBEgESABcBEwETADECDAINADACDgIOAEQCDwIPAEUCEAIQAEMCEQIRAEQCEgISAEUCEwITAEMCFAIUAAYCFQIVAEACFgIWADUCFwIXADYCJwInAEoCKAIoADcCKQIpADsCPQJQAC4CUQJRACsCUgJSADICUwJTACsCVAJUAD0CVQJVADcCVgJWADkCVwJXADoCWAJYADsCWQJZAEwCWgJaAD0CWwJbADkCXAJcADoCXQJdAEwCXgJfADICYAJgAEwCYQJhADICYgJiAEwAAgB6AAcABwAkAAgACAA5AAoACgAdAAsACwAuAAwADAAkAA4ADgBKAA8ADwAvABEAEQARABIAEgBGABMAEwARABQAFAAUABUAFQApABYAFgA0ABcAFwA/ABgAGAA8ABkAGQA2ABoAGgA9ABsAGwA6ABwAHAAfAB0AHQA7AB4AHgAzAB8AIABJACQAJAAYACUAJQA4ACYAJgAKACgAKAABACwALAABAC8ALwAMADQANAABADYANgABADgAOABFADkAOQAXADoAOgAaADsAOwACADwAPAAZAD0APQANAD4APgAOAD8APwAqAEEAQQAwAEIAQgBKAEQARAA3AEYARgADAEcARwBDAEgASgAQAEsASwAEAEwATAAPAE0ATQBDAE4ATgAlAE8ATwAoAFAAUQBDAFIAUwArAFQAVAAQAFUAVQBCAFYAVgAQAFcAVwAtAFgAWAATAFkAWQAFAFoAWgAGAFsAWwAHAFwAXAAIAF0AXQAVAF4AXgAJAF8AXwAWAGIAYgBKAG0AbQAiAG4AbgBBAG8AbwAsAHEAcQBGAHIAcgAiAHYAdgAhAHcAdwAgAHkAeQBCAHsAewBIAH0AfQAcAH4AfgBBAH8AfwBHAIMAgwA1AIQAiQAKAIoAigALAIsAiwABAJAAkABEAJIAkwBEAJYAmgABAJwAnAABAJ0AoAAaAKEAoQAOAKMAowBDAKQAqgADAKsArwAQALAAsAAmALEAsQAnALIAswAmALQAtAAyALUAtQArALYAugAQALwAvAAQAL0AwAAGAMEAwQAJAMIAwgBDAMMAwwAJAPUA9QArARIBEgABARMBEwAQAgwCDQBGAg4CDgAeAg8CDwAjAhACEAASAhECEQAeAhICEgAjAhMCEwASAhQCFAAxAhUCFQARAhYCFgAsAhcCFwBHAhkCGQAbAiYCJgA+AicCJwAUAigCKQAEAj0CUABAAlECXQAEAl4CYAArAmECYgAFAAIAFwAFAAgAAAAKAA8ABAARABUACgAYACAADwAkAEIAGABEAEQANwBGAGAAOABiAGIAUwBkAGUAVABtAG0AVgBvAG8AVwBxAHIAWAB5AHkAWgB7AHsAWwB/AH8AXACDAJoAXQCcALoAdQC8AMMAlAD1APUAnAESARMAnQIMAhcAnwInAikAqwI9AmIArgABALYABAAAAA0AJAAqADAAPgBMAFoAaAByAIwAlgCcAKIArAABAQP/7AABAQP/zgADAYj/7AGO/+wBn//2AAMBgf/2AYj/4gGO//YAAwGA//YBiP/2AZ//9gADAYP/9gGO//YBn//2AAIBgP/YAZ//7AAGAYD/9gGB//YBg//2AYr/9gGO/+IBn//YAAIBgAAUAYj/7AABAYj/4gABAYj/7AACAdT/9gHX//YAAgHU//YB6v/2AAEADQEAAQMBdQGAAYEBgwGIAYoBjwGbAZ8B0QHXAAIN2AAEAAAMQA0OACcAKAAA//b/8f/2//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/9gAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAD/zv/TAAAAAAAA/7X/8QAA/+cAAP+///YAAAAAAAAAAAAA/9j/zv/x//b/7P/sAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAD/7P/2//b/9gAA//H/4gAAAAD/9v/7//YAAAAAAAAAAAAAAAAAAP/2AAD/9gAAAAD/9v/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/7P/s/+z/7AAAAAAAAAAA/+z/9v/s//YAAAAA//b/9v/2AAD/9gAAAAD/9v/s/+L/9gAAAAD/9v/2//b/9gAAAAAAAP/2//YAAAAAAAD/7P/x//b/7P/sAAD/9gAAAAD/7AAA/+z/9gAAAAAAAAAAAAD/9v/2AAAAAAAA/+z/7P/iAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//H/9v/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7P/2//YAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/8f/2//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//b/9gAA//YAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAD/9gAA/+z/9v/7//b/7AAAAAD/9gAA//b/9gAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAA//b/7AAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAP/i//YAAP/T/9gAAP/2AAAAAP/s//b/7AAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/9v/Y/+IAAAAAABQAAAAAAAAAAP/2AAAAAAAAAAAAAAAA//YAAP/O//v/9v/s//b/3f/YAAAAAAAA//b/4gAA//b/+//2AAD/+wAA//b/9v/iAAAAAP/s/9j/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/s/84AAAAA/7r/fv/s/+L/3f/xAAAAAAAAAAD/zgAAAAAAAAAAAAAAAAAA//YAAAAA/8T/xP/2AAAAAAAAAAAAAP/2AAAABQAAAAAAAAAAAAD/7P/x/+z/9gAA//b/4gAAAAD/9gAA//YAAP/2AAAAAAAAAAD/9v/2AAD/9v/2//YAAAAAAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAA/+L/tf+w/7//xP+//5IAAAAA/7D/sP/O/9gAAAAA/+L/4gAFAAD/xAAAAAD/7P+6/87/4v+w/5wAAP/2/+L/xP/OAAAAAP+wAAAAAP/2AAD/3QAAAAD/4v/OAAD/7AAAAAD/5//s/9gAAAAAAAD/9gAAAAAAAP/2AAAAAP/s//b/4v/iAAAAAAAAAAAAAP/2AAD/9v/2//YAAAAA//YAAP/Y/6v/sP/O/9j/tf+IAAAAAP+c/6v/4v/YAAAAAP/n/9j/2AAA/7oAAAAA/9j/uv/Y/+z/zv+6AAD/7P/i/7r/zgAA//b/uv/iAAD/8QAAAAD/+//2//b/9gAAAAAAAAAA//v/+wAA//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//YAAAAAAAAAAAAAAAD/+wAAAAAAAAAAAAAAAAAAAAD/+wAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sAAAAAAAAAAAAAAAD/9v/2//YAAAAAAAD/2AAAAAD/9gAA/8T/7AAAAAAAAAAAAAD/9v/2//b/9gAA//YAAP/2AAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/9j/uv+S/9j/4v+m/4gAAAAA/7r/uv/s/+IAAAAA/9j/2AAAAAD/xAAAAAD/4v+w/9j/2P+m/7AAAP/nAAD/2P/YAAD/9v/YAAAAAAAAAAAAAAAAAAD/9v/2AAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAP/s//YAAAAAAAD/7P/2AAD/7AAA/+IAAAAAAAAAAAAKAAD/7P/2//b/9v/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAUAAoACgAAAAAAAAAA//YAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAABQAAAAeAAAAAAAAAAAAAAAAAAAAAAAA/9gACgAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAD/4gAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//YAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAoAAAAAAAA//YAAP/s/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABf/s//b/9gAAAAAAAAAAAAUABf/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//YAAAAAAAAAAAAAAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//v/9gAAAAAAAAAAAAAAAP/7AAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+//2AAAAAAAAAAAAAAAA//v/+wAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//YAAAAAAAAAAAAAAAD/+//7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//v/9v/s//YAAAAAAAD/2AAA//v/9v/7/+L/9gAAAAAAAAAAAAD/7P/sAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//b/7AAAAAAAAAAAAAD/+//2//YAAAAAAAD/9v/2//v/9gAAAAAAAP/2AAAAAAAA/+wAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAA/+z/9v/sAAAAAAAAAAAAAP/7//b/9gAAAAAAAP/7//v/+//7AAAAAAAA/+wAAAAAAAD/9gAAAAAAAAAAAAAAAAAA//b/9gAAAAAAAAAAAAX/7AAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAA//v/+//7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+wAAAAAAAAAAAAD/9v/iAAAAAAAAAAAAAAAA//H/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAD/8f/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+f/2AAAAAAAAAAAAAAAAP/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAABAaAAZAAHAAcAFAAPAAYADgAIAAkACgAUABQAIAALAAgAFQAIAAQAFAAFAA8AFwAHAAsABQAIAAgACwAIAAgACAAMAAgADQAGAA8AFQAMABAAFwAIAAgAFwAUAAgAFAAMAAwACAASAAEAAgAZABoAGwAYAAMAHwAfABgAHwAfAB8AIQAfACIAEwAZACYAIQAlABoAHwAfABoAFgAfABYAIQAhAB8AGwAbACAAGQATACMAHAAdAB4AFgAWACAAGAAfACYAHwAPABkAEQAkAAEBogBiAA4AAAABACcAAAAmAAcAEAAAAA4AAAAAAA8AAAAGAAAAAAAAABsAAAAdACIAAAAAAAAAEAAAAAAAAQAAAAAAAQAOAA8AAQAIAAAAFQAAAAAAFgAAAAAAIwAAAB4AAgAXABQAFAAcAAoAHwAYABQAFAAUAAMAFAAUAAoAFAAkAAoADAAFAAoADQAUABkAFAAUABoAFAAUACAAFAAhAAoACgAlABQACgALABEAEgATAAMAFAAlABQAFAAFABQAAAAUAAkABAACAAEBoAIDAAAAAAAAAAEAAAAA1aSY2wAAAADTVnRHAAAAANezJNI=); }</style> - <filter id="drop-shadow-filter-0" x="-500%" y="-500%" width="1000%" height="1000%" bx:preset="drop-shadow 1 0 0 11 1 rgba(255,255,255,1)"> - <feGaussianBlur in="SourceAlpha" stdDeviation="11"/> - <feOffset dx="0" dy="0"/> - <feComponentTransfer result="offsetblur"> - <feFuncA id="spread-ctrl" type="linear" slope="2"/> - </feComponentTransfer> - <feFlood flood-color="rgba(255,255,255,1)"/> - <feComposite in2="offsetblur" operator="in"/> - <feMerge> - <feMergeNode/> - <feMergeNode in="SourceGraphic"/> - </feMerge> - </filter> - </defs> - <g transform="matrix(1, 0, 0, 1, -25.719366, -152.601593)"> - <image width="512" height="512" x="-21.491" y="224.183" style="" xlink:href="data:image/png;base64,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"/> - <rect x="23.699" y="322.056" width="421.619" height="240.568" style=""/> - <g style="" transform="matrix(0.245719, 0, 0, 0.245719, 155.796844, 363.102661)"> - <g/> - <path class="st1" d="M193.3,120.4c48.2,24.5,76.2,44.2,91.5,61.1c-7.8,31-48.8,32.4-63.8,31.6c3.1-1.4,5.6-3.1,6.5-5.6 c-3.8-2.7-17.1-0.2-26.4-5.4c3.6-0.7,5.2-1.5,6.9-4c-8.8-2.7-18.2-5.2-23.8-9.7c3,0,5.8,0.6,9.8-2c-7.9-4.3-16.3-7.6-22.8-14 c4-0.1,8.5,0,9.8-1.5c-7.2-4.4-13.3-9.3-18.3-14.6c5.7,0.6,8.1,0.1,9.5-0.9c-5.5-5.5-12.3-10.2-15.7-16.9c4.2,1.5,8.1,2,10.9-0.2 c-1.9-4.1-9.8-6.5-14.4-16.1c4.4,0.4,9.2,1,10.1,0c-2.1-8.3-5.6-13-9.1-17.8c9.5-0.2,24,0,23.3-0.7l-5.9-6 c9.3-2.5,18.8,0.4,25.7,2.5c3.1-2.4-0.1-5.5-3.9-8.6c7.9,1,15.1,2.8,21.5,5.3c3.5-3.1-2.3-6.1-5-9.2c12.3,2.3,17.4,5.5,22.6,8.7 c3.7-3.6,0.2-6.5-2.3-9.6c9.2,3.4,14,7.7,19,12c1.7-2.3,4.3-3.9,1.1-9.4c6.5,3.7,11.5,8.1,15.1,13c4-2.5,2.4-6.1,2.4-9.2 c6.8,5.5,11.1,11.2,16.4,16.9c1-0.7,2-3.4,2.8-7.4c16.2,15.5,39.1,54.6,5.9,70C264.6,149.6,230.8,132.9,193.3,120.4L193.3,120.4z" style="fill: rgb(106, 191, 75);"/> - <path class="st1" d="M448.5,120.4c-48.2,24.5-76.2,44.2-91.5,61.1c7.8,31,48.8,32.4,63.8,31.6c-3.1-1.4-5.6-3.1-6.5-5.6 c3.8-2.7,17.1-0.2,26.4-5.4c-3.6-0.7-5.2-1.5-6.9-4c8.8-2.7,18.2-5.2,23.8-9.7c-3,0-5.8,0.6-9.8-2c7.9-4.2,16.3-7.5,22.8-13.9 c-4-0.1-8.5,0-9.8-1.5c7.2-4.4,13.3-9.3,18.3-14.6c-5.7,0.6-8.1,0.1-9.5-0.9c5.5-5.5,12.3-10.2,15.7-16.9c-4.2,1.5-8.1,2-10.9-0.2 c1.9-4.1,9.8-6.5,14.4-16.1c-4.4,0.4-9.2,1-10.1,0c2.1-8.3,5.6-13,9.1-17.8c-9.5-0.2-24,0-23.3-0.7l5.9-6 c-9.3-2.5-18.9,0.4-25.7,2.5c-3.1-2.4,0.1-5.5,3.9-8.6c-7.9,1-15.1,2.8-21.5,5.3c-3.5-3.1,2.3-6.1,5-9.2 c-12.3,2.3-17.4,5.5-22.6,8.7c-3.7-3.6-0.2-6.5,2.3-9.6c-9.2,3.4-14,7.7-19,12c-1.7-2.3-4.3-3.9-1.1-9.4c-6.5,3.7-11.5,8.1-15.1,13 c-4-2.5-2.4-6.1-2.4-9.2c-6.8,5.5-11.1,11.2-16.4,16.9c-1-0.7-2-3.4-2.8-7.4c-16.2,15.5-39.1,54.6-5.9,70 C377.2,149.6,411,132.9,448.5,120.4L448.5,120.4z" style="fill: rgb(106, 191, 75);"/> - <g> - <path class="st2" d="M379.2,436.2c0.2,29-25.5,52.5-57.4,52.7s-57.9-23.2-58-52.1c0-0.2,0-0.4,0-0.6c-0.2-29,25.5-52.5,57.4-52.7 s57.9,23.2,58,52.1C379.2,435.8,379.2,436,379.2,436.2z" style="fill: rgb(195, 28, 74);"/> - <g> - <path class="st2" d="M289.2,286.4c23.9,15.4,28.2,50.4,9.6,78.1c-18.6,27.7-53,37.7-77,22.3l0,0c-23.9-15.4-28.2-50.4-9.6-78.1 S265.3,271,289.2,286.4L289.2,286.4z" style="fill: rgb(195, 28, 74);"/> - <path class="st2" d="M353.7,283.7c-23.9,15.4-28.2,50.4-9.6,78.1c18.6,27.7,53,37.7,77,22.3l0,0c23.9-15.4,28.2-50.4,9.6-78.1 C412.1,278.2,377.6,268.3,353.7,283.7L353.7,283.7z" style="fill: rgb(195, 28, 74);"/> - </g> - <g> - <path class="st2" d="M171.2,311.9c25.8-6.8,8.7,105.1-12.3,96C135.8,389.6,128.3,335.9,171.2,311.9z" style="fill: rgb(195, 28, 74);"/> - <path class="st2" d="M469.5,310.5c-25.8-6.8-8.7,105.1,12.3,96C504.9,388.1,512.3,334.5,469.5,310.5L469.5,310.5z" style="fill: rgb(195, 28, 74);"/> - </g> - <path class="st2" d="M382.9,226.9c44.5-7.4,81.6,18.6,80.1,66.3C461.5,311.4,366.5,229.6,382.9,226.9z" style="fill: rgb(195, 28, 74);"/> - <path class="st2" d="M259.8,225.5c-44.5-7.4-81.6,18.6-80.1,66.3C181.2,310,276.3,228.2,259.8,225.5z" style="fill: rgb(195, 28, 74);"/> - <path class="st2" d="M321.2,214.4c-26.5-0.6-52.1,19.4-52.1,31.1c-0.1,14.2,21,28.7,52.4,29.1c32,0.2,52.4-11.6,52.5-26.2 C374,231.7,344.8,214.1,321.2,214.4L321.2,214.4z" style="fill: rgb(195, 28, 74);"/> - <path class="st2" d="M323.2,505.1c23.2-1,54.3,7.3,54.3,18.4c0.4,10.7-28.2,35-55.8,34.6c-28.6,1.2-56.7-23.2-56.4-31.6 C264.9,514.2,300.2,504.6,323.2,505.1L323.2,505.1z" style="fill: rgb(195, 28, 74);"/> - <g> - <path class="st2" d="M237.5,439.5c16.5,19.6,24,54,10.2,64.2c-13,7.7-44.7,4.5-67.1-27.3c-15.2-26.7-13.2-53.9-2.6-61.9 C194,404.9,218.5,417.8,237.5,439.5L237.5,439.5z" style="fill: rgb(195, 28, 74);"/> - <path class="st2" d="M403.6,433.3c-17.8,20.6-27.8,58.2-14.8,70.3c12.4,9.4,45.9,8.1,70.6-25.7c17.9-22.7,11.9-60.5,1.7-70.6 C445.9,395.7,424,410.5,403.6,433.3L403.6,433.3z" style="fill: rgb(195, 28, 74);"/> - </g> - </g> - </g> - <line style="stroke: rgb(160, 160, 160); stroke-dasharray: 7, 2; stroke-width: 10px;" x1="28.359" y1="552.901" x2="440.659" y2="552.901"/> - <line style="stroke: rgb(255, 255, 255); stroke-dasharray: 5, 1; stroke-width: 4px;" x1="199.02" y1="541.093" x2="269.998" y2="541.093"/> - </g> - <text style="fill: rgb(51, 51, 51); font-family: &quot;Product Sans Medium&quot;; font-size: 57.4px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 6px; white-space: pre; filter: url(#drop-shadow-filter-0);" x="-42.779" y="-38.806">Next steps:</text> - <text transform="matrix(0.550932, 0, 0, 0.550932, 232.892929, 128.602966)" style="fill: rgb(51, 51, 51); font-family: &quot;Product Sans Medium&quot;; font-size: 57.4px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 10.8906px; text-anchor: middle; white-space: pre; filter: url(#drop-shadow-filter-0);"><tspan x="-42.779" y="-38.806">Boot up the new SD card.</tspan><tspan x="-42.779" dy="1em">​</tspan><tspan>This screen should appear:</tspan></text> - <text transform="matrix(0.524345, 0, 0, 0.524345, 231.569962, 557.56958)" style="fill: rgb(51, 51, 51); font-family: &quot;Product Sans Medium&quot;; font-size: 57.4px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 11.4428px; text-anchor: middle; white-space: pre; filter: url(#drop-shadow-filter-0);"><tspan x="-42.779" y="-38.806">Don't worry - the screen is not frozen.</tspan><tspan x="-42.779" dy="1em">​</tspan></text> - <g transform="matrix(1, 0, 0, 1, 589.418152, -152.601593)"> - <image width="512" height="512" x="-21.491" y="224.183" style="" xlink:href="data:image/png;base64,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"/> - <rect x="23.699" y="322.056" width="421.619" height="240.568" style="fill: rgb(127, 16, 133);"/> - <line style="stroke: rgb(255, 255, 255); stroke-dasharray: 5, 1; stroke-width: 4px;" x1="391.572" y1="553.475" x2="437.166" y2="553.475"/> - <line style="stroke: rgb(255, 255, 255); stroke-dasharray: 5, 1; stroke-width: 4px;" x1="401.235" y1="547.496" x2="437.166" y2="547.496"/> - <rect x="163.414" y="392.5" width="142.189" height="99.679" style="fill: rgb(255, 255, 255);"/> - <line style="stroke-dasharray: 5, 1; fill: none; stroke: rgb(78, 78, 78); stroke-width: 9px;" x1="168.832" y1="410.725" x2="186.577" y2="410.725"/> - <line style="stroke-dasharray: 5, 1; stroke-width: 4px; fill: none; stroke: rgb(78, 78, 78);" x1="194.152" y1="454.043" x2="262.657" y2="454.043"/> - <rect x="284.416" y="392.5" width="21.187" height="9.854" style="fill: rgb(255, 0, 0);"/> - <path d="M 136.2 48.988 H 137.061 V 52.91 H 140.982 V 53.771 H 137.061 V 57.692 H 136.2 V 53.771 H 132.278 V 52.91 H 136.2 Z" style="fill: rgb(255, 255, 255);" transform="matrix(-0.707107, 0.707107, -0.707107, -0.707107, 429.338593, 338.532074)" bx:shape="cross 132.278 48.988 8.704 8.704 0.861 0.861 0.5 1@b6fb31fa"/> - <rect x="179.606" y="459.398" width="109.804" height="6.312" style="fill: rgb(230, 230, 230); stroke: rgb(0, 0, 0); stroke-width: 0.5px;"/> - <rect x="179.856" y="459.811" width="77.316" height="5.486" style="fill: rgb(6, 176, 37);"/> - <line style="stroke-dasharray: 5, 1; stroke-width: 4px; fill: none; stroke: rgb(78, 78, 78);" x1="181.152" y1="469.043" x2="191.901" y2="469.043"/> - <line style="stroke-dasharray: 5, 1; stroke-width: 4px; fill: none; stroke: rgb(78, 78, 78);" x1="247.621" y1="398.017" x2="271.549" y2="398.017"/> - <line style="stroke-dasharray: 5, 1; stroke-width: 4px; fill: none; stroke: rgb(78, 78, 78);" x1="188.966" y1="410.451" x2="212.894" y2="410.451"/> - </g> - <text transform="matrix(0.524345, 0, 0, 0.524345, 846.490356, 128.332809)" style="fill: rgb(51, 51, 51); font-family: &quot;Product Sans Medium&quot;; font-size: 57.4px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 11.4428px; text-anchor: middle; white-space: pre; filter: url(#drop-shadow-filter-0);"><tspan x="-42.779" y="-38.806">After 30 minutes, the WoR installer</tspan><tspan x="-42.779" dy="1em">​</tspan><tspan>will begin extracting Windows to the sd card.</tspan></text> - <text style="fill: rgb(51, 51, 51); font-family: &quot;Product Sans Medium&quot;; font-size: 30.1px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 6px; text-anchor: middle; white-space: pre; filter: url(#drop-shadow-filter-0);" x="823.15" y="537.224">It will reboot the system when finished.</text> - <g transform="matrix(1, 0, 0, 1, 1204.555664, -152.601593)"> - <image width="512" height="512" x="-21.491" y="224.183" style="" xlink:href="data:image/png;base64,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"/> - <image width="1024" height="640" x="-256.84360248141365" y="114.44025223361871" transform="matrix(0.411737, 0, 0, 0.375887, 129.45108, 279.039337)" xlink:href="data:image/png;base64,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"/> - </g> - <text transform="matrix(0.524345, 0, 0, 0.524345, 1460.599731, 128.332809)" style="fill: rgb(51, 51, 51); font-family: &quot;Product Sans Medium&quot;; font-size: 57.4px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 11.4428px; text-anchor: middle; white-space: pre; filter: url(#drop-shadow-filter-0);"><tspan x="-42.779" y="-38.806">If all goes well, Windows will be</tspan><tspan x="-42.779" dy="1em">​</tspan><tspan>running on your Raspberry Pi!</tspan></text> -</svg> \ No newline at end of file diff --git a/overview.svg b/overview.svg deleted file mode 100644 index 8e12780..0000000 --- a/overview.svg +++ /dev/null @@ -1,21 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<svg viewBox="364.577 227.043 500 72.6" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:bx="https://greentree.cf"> - <defs> - <style bx:fonts="Product Sans Medium" bx:pinned="true">@font-face { font-family: "Product Sans Medium"; font-style: normal; font-weight: normal; font-stretch: normal; font-unicode-range: U+0-10FFFF; font-variant: normal; font-feature-settings: normal; src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAASAQAABAAgRkZUTYCuMfUAAWh8AAAAHEdERUYOQRBJAAEVJAAAAEpHUE9TV02NVgABHVAAAEsqR1NVQqDDbjsAARVwAAAH3k9TLzJpZB4uAAABqAAAAGBjbWFwzb+/YAAAC8gAAAOuY3Z0ICFGFdYAACGIAAAA/mZwZ224slEGAAAPeAAAD3NnYXNwAAcAGwABFRgAAAAMZ2x5ZqbN4XgAACdsAADZnGhlYWQWYN8FAAABLAAAADZoaGVhD+cNmAAAAWQAAAAkaG10eJV7WJgAAAIIAAAJwGxvY2E/PAeiAAAiiAAABOJtYXhwBeYCAwAAAYgAAAAgbmFtZcVCJ5QAAQEIAAACqXBvc3TaWWHUAAEDtAAAEWRwcmVwDY+z/QAAHuwAAAKaAAEAAAABAk4NmPsEXw889QAfA+gAAAAA01Z0RwAAAADXsyTS/1D+9wzUA8IAAAAIAAIAAAAAAAAAAQAAA8D/AwAADNT/UP9QDNQAAQAAAAAAAAAAAAAAAAAAAnAAAQAAAnAA5QAKAF0ABAACABAALwCaAAACugCPAAMAAQAEAjUB9AAFAAACigJYAAAASwKKAlgAAAFeADIBJgAAAgsFAwMFAgQCA4AAAAcAAAAAAAAAAAAAAABHT09HAYAADSIVA8D/AwAAA8YBMiAAAZ8AAAAAAeoCzAAAACAAEAI6AEgAAAAAAAAAAAAAAAABTQAAANwAAAD7ADABXQAsAoUAFwIiABgDOwAgAngAJAC9ACwBWABAAVgAFAHCACgCLwA4APwALAG5ADgA+wAwAcoAGwKKACwBlAAoAhQAKAIiACQCWgAoAjQAKAIuACgCFgAoAjAAKAIuACgA+wAwAPwALAIHADgCOwA4AgcAPAH3ACQDaQAsArQADAJjAEgC3AAlAsEASAIwAEgCHABIAyYAJQK9AEgBAQBIAhwAGAKFAEgCBABIA0sASALFAEgDMAAlAkIASAMwACUCVwBIAjQAGAIxABgCjwA4ApAADAOqABAClAAQAmcADAJEACABSgBIAcoAGwFKACAB7QAMAekAAAGlAGACCwAcAkwAOAIVABwCTAAcAiIAHAF/ABgCSwAcAi4AOADvAC0A7//RAfoAOADfADgDYgA4Ai4AOAI4ABwCSAA0AkgAHAGBADgB1QAYAXoAGAIuADQCDwAEAwsABAHtAAgCDgAEAdUAIAFIABgA9gBIAUgAIAIeACAA3AAAAPsAMAISABwCEAAgAlsAPAJaAAwA9gBIAiMAKAGqADsCFAAkAeYAKAJHACgCQgAwAbkAOAIUACQBmAA8AUUAIAIvADgBNwAYAUkAGAGlAJgCLgA0AnkAGADDABQBlQBHAQEAGAINACQCRwA8AvEAMAMYADADRAAwAewAJQK0AAwCtAAMArQADAK0AAwCtAAMArQADAPWAAQC3AAlAjAASAIwAEgCMABIAjAASAEBAAYBAQBIAQH/5QEB//QC/QA4AsUASAMwACUDMAAlAzAAJQMwACUDMAAlAkEAPAMwACUCjwA4Ao8AOAKPADgCjwA4AmcADAJCAEgCPgBIAgsAHAILABwCCwAcAgsAHAILABwCCwAcA3cAHAIVABwCIgAcAiIAHAIiABwCIgAcAO//+wDvAEAA7//cAO//7QI+ACgCLgA4AjgAHAI4ABwCOAAcAjgAHAI4ABwCLwA4AjgAHAIuADQCLgA0Ai4ANAIuADQCDgAEAkwAOAIOAAQCtAAMAgsAHAK0AAwCCwAcArQADAILABwC3AAlAhUAHALcACUCFQAcAtwAJQIVABwC3AAlAhUAHALBAEgCrgAcAr7/+AJKABwCMABIAiIAHAIwAEgCIgAcAjAASAIiABwCMABIAiIAHAIwAEgCIgAcAyYAJQJLABwDJgAlAksAHAMmACUCSwAcAyYAJQJLABwCvQBIAi4AOAK9AAkCLv/7AQH/1QDv/8wBAf/+AO//6QEB//4A7//1AQEAEgDvAAgBAQBDAO8AQAIcABgA7//RAn0ASAH1ADgB7wA4AgQASADfADgCBABIAN8AMAIEAEgBWgBMAgQASAJTACoBiAAeAsUASAIuADgCxQBIAi4AOALFAEgCLgA4AsUASAIuADgDMAAlAjgAHAMwACUCOAAcAzAAJQI4ABwEewAlA7YAHAJXAEgBgQA4Ak4ASAGBADECVwBIAYEAMAI0ABgB1QAYAjQAGAHVABgCNAAYAdUAGAI0ABgB1QAYAjEAGAF6ABgCMQAYAXoAGAIxABgBegAYAo8AOAIuADQCjwA4Ai4ANAKPADgCLgA0Ao8AOAIuADQCjwA4Ai4ANAKPADgCLgA0A6oAEAMLAAQCZwAMAg4ABAJnAAwCRAAgAdUAIAJEACAB1QAgAkQAIAHVACACTQAnArQADAILABwD1gAEA3cAHAMwACUCOAAcAjQAGAHVABgCMQAYAXoAGADv/9EBrAA5AawAOQGYAEgA+wA7AZQAYAGZACABKAAoASgAKAD8ACwBpQCBAbAAIAK0AAwAwwAUAmz/sQL4/7EBPf+xAxL/sQLN/7EC//+xAO//wQK0AAwCYwBIAgQASAKsAAwCMABIAkQAIAK9AEgDPgAlAQEASAJ5AEgCpAAMA0sASALFAEgCBQAcAzAAJQKmAEgCQgBIAgIAKAIxABgCZwAMAx8AJQKUABAC8QA4AxwAKwEB//ICZwAMAkgAHAH9ACQCLgA4AO8AQAIpADQCSAAcAj4ASAIqAAQCOAAcAesAJAHkABgCLgA4ApsALADvAEAB7wA4Ag4ABAIPAAQCDgAYAjgAHALMABgCTAA4AhEAHAJXABwB0wAYAikANALYACwB7QAIAr0ANAMTABwA7//tAikANAI4ABwCKQA0AxMAHAJ9ADQCcwAEAtj/sQJzAAQDhQAoAjAASAIwAEgDAgAYAhEASALZACUCNAAYAQEASAEB//ICHAAYBDgAGAQNAEgC/AAYAnkASAK9AEgCaAAMAqYASAK0AAwCYwBIAmMASAIEAEgDEwAoAjAASAOhAAICKwAkAr0ASAK9AEgCeQBIAs0AGANjAEgCvQBIAzcAJQKmAEgCQgBIAtwAJQI3ABgCaAAMAy8AJQKUABAC0gBIAlYAKAOTAEgDxQBIAvgAGANGAEgCagBIAtkAJAQ5AEgCbgAhAg0AHAJVADgB9wA4AakAOAKIABwCIgAcAuX//AHrACgCLgA4Ai4AOAHvADgCZQAqAqAANAIyADgCOAAcAiEAOAJIADQCFQAcAbsAGAIOAAQC7wAlAe0ACAJRADgCAgAgAwsAOAM7ADgCUQAYAscAOAH9ADgCDwAYAxYAQAHsAAACHgAcAh4AHAIu//sBlQA4Ag8AHAHVABgA7wAtAO//7QDv/9EDfgAqA04AOAIu//sB7wA4Ai4AOAIOAAQCLwA4AhEASAGpADgCZwAMAgcABAOqABADCwAEA6oAEAMLAAQDqgAQAwsABAJnAAwCDgAEAlkAOAOJADgA+wAwAPsAMAD7ADABwAAwAcAAMAG7ADABmwBQAtgAMAFzACgBcwA8ALX/UAFhABwCMAAYAhUAIAPcACACQgAcAoEAGAIdABgChQAY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ACcAKAApACoAKwAsAC0ALgAvADAAMQAyADMANAA1ADYANwA4ADkAOgA7ADwAPQA+AD8AQABBAEIAQwBEAEUARgBHAEgASQBKAEsATABNAE4ATwBQAFEAUgBTAFQAVQBWAFcAWABZAFoAWwBcAF0AXgBfAGAAYQEEAKMAhACFAL0AlgDoAIYAjgCLAJ0AqQCkAQUAigDaAIMAkwDyAPMAjQCXAIgAwwDeAPEAngCqAPUA9AD2AKIArQDJAMcArgBiAGMAkABkAMsAZQDIAMoAzwDMAM0AzgDpAGYA0wDQANEArwBnAPAAkQDWANQA1QBoAOsA7QCJAGoAaQBrAG0AbABuAKAAbwBxAHAAcgBzAHUAdAB2AHcA6gB4AHoAeQB7AH0AfAC4AKEAfwB+AIAAgQDsAO4AugEGAQcBCAEJAQoBCwD9AP4BDAENAQ4BDwD/AQABEAERARIBAQETARQBFQEWARcBGAEZARoBGwEcAR0BHgD4APkBHwEgASEBIgEjASQBJQEmAScBKAEpASoBKwEsAS0BLgD6ANcBLwEwATEBMgEzATQBNQE2ATcBOAE5AToA4gDjATsBPAE9AT4BPwFAAUEBQgFDAUQBRQFGAUcBSACwALEBSQFKAUsBTAFNAU4BTwFQAVEBUgFTAVQA5ADlAVUBVgFXAVgBWQFaAVsBXAFdAV4BXwFgAWEBYgFjAWQBZQFmAWcBaAFpAWoAuwFrAWwBbQFuAOYA5wFvAXABcQFyAXMBdAF1AXYBdwF4AXkBegDYAOEA2wDcAN0A2QF7AXwBfQF+AX8BgAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEBkgGTAZQBlQGWAZcBmAGZAZoBmwGcAZ0BngGfAaABoQGiAaMBpAGlAaYBpwGoAakBqgGrAawBrQGuAa8BsAGxAbIBswG0AbUAmwG2AbcBuAG5AboBuwG8Ab0BvgG/AcABwQHCAcMBxAHFAcYBxwHIAckBygHLAcwBzQHOAc8B0AHRAdIB0wHUAdUB1gHXAdgB2QHaAdsB3AHdAd4B3wHgAeEB4gHjAeQB5QHmAecB6AHpAeoB6wHsAe0B7gHvAfAB8QHyAfMB9AH1AfYB9wH4AfkB+gH7AfwB/QH+Af8CAAIBAgICAwIEAgUCBgIHAggCCQIKAgsCDAINAg4CDwIQAhECEgITAhQCFQIWAhcCGAIZAhoCGwIcAh0CHgIfAiACIQIiAiMCJAIlAiYCJwIoAikCKgIrAiwCLQIuAi8CMAIxAjICMwI0ALIAswC2ALcAxAC0ALUAxQCHAKsAvgC/ALwCNQI2AjcCOAI5AjoCOwI8Aj0CPgI/AkACQQDvAkICQwJEAkUCRgJHAkgCSQJKAksCTAJNAk4CTwJQAlECUgJTAlQCVQJWAlcCWAJZAloCWwJcAl0CXgJfAmACYQJiAmMCZAJlAmYCZwJoAmkCagJrAmwCbQJuAm8CcAJxAnICcwJ0AnUCdgJ3AngCeQJ6AnsCfAJ9An4CfwKAAoECggKDAoQChQKGAocCiAKJAooHdW5pMDAwRAROVUxMB3VuaTAwQTAHdW5pMDBBRAdBbWFjcm9uB2FtYWNyb24GQWJyZXZlBmFicmV2ZQdBb2dvbmVrB2FvZ29uZWsLQ2NpcmN1bWZsZXgLY2NpcmN1bWZsZXgKQ2RvdGFjY2VudApjZG90YWNjZW50BkRjYXJvbgZkY2Fyb24GRGNyb2F0B0VtYWNyb24HZW1hY3JvbgZFYnJldmUGZWJyZXZlCkVkb3RhY2NlbnQKZWRvdGFjY2VudAdFb2dvbmVrB2VvZ29uZWsGRWNhcm9uBmVjYXJvbgtHY2lyY3VtZmxleAtnY2lyY3VtZmxleApHZG90YWNjZW50Cmdkb3RhY2NlbnQHdW5pMDEyMgd1bmkwMTIzC0hjaXJjdW1mbGV4C2hjaXJjdW1mbGV4BEhiYXIEaGJhcgZJdGlsZGUGaXRpbGRlB0ltYWNyb24HaW1hY3JvbgZJYnJldmUGaWJyZXZlB0lvZ29uZWsHaW9nb25lawtKY2lyY3VtZmxleAtqY2lyY3VtZmxleAd1bmkwMTM2B3VuaTAxMzcMa2dyZWVubGFuZGljBkxhY3V0ZQZsYWN1dGUHdW5pMDEzQgd1bmkwMTNDBkxjYXJvbgZsY2Fyb24ETGRvdAZOYWN1dGUGbmFjdXRlB3VuaTAxNDUHdW5pMDE0NgZOY2Fyb24GbmNhcm9uA0VuZwNlbmcHT21hY3JvbgdvbWFjcm9uBk9icmV2ZQZvYnJldmUNT2h1bmdhcnVtbGF1dA1vaHVuZ2FydW1sYXV0BlJhY3V0ZQZyYWN1dGUHdW5pMDE1Ngd1bmkwMTU3BlJjYXJvbgZyY2Fyb24GU2FjdXRlBnNhY3V0ZQtTY2lyY3VtZmxleAtzY2lyY3VtZmxleAd1bmkwMTVFB3VuaTAxNUYHdW5pMDE2Mgd1bmkwMTYzBlRjYXJvbgZ0Y2Fyb24EVGJhcgR0YmFyBlV0aWxkZQZ1dGlsZGUHVW1hY3Jvbgd1bWFjcm9uBlVicmV2ZQZ1YnJldmUFVXJpbmcFdXJpbmcNVWh1bmdhcnVtbGF1dA11aHVuZ2FydW1sYXV0B1VvZ29uZWsHdW9nb25lawtXY2lyY3VtZmxleAt3Y2lyY3VtZmxleAtZY2lyY3VtZmxleAt5Y2lyY3VtZmxleAZaYWN1dGUGemFjdXRlClpkb3RhY2NlbnQKemRvdGFjY2VudAd1bmkwMTkyCkFyaW5nYWN1dGUKYXJpbmdhY3V0ZQdBRWFjdXRlB2FlYWN1dGULT3NsYXNoYWN1dGULb3NsYXNoYWN1dGUHdW5pMDIxOAd1bmkwMjE5B3VuaTAyMUEHdW5pMDIxQgd1bmkwMjM3B3VuaTAzNzQHdW5pMDM3NQd1bmkwMzdFBXRvbm9zDWRpZXJlc2lzdG9ub3MKQWxwaGF0b25vcwlhbm90ZWxlaWEMRXBzaWxvbnRvbm9zCEV0YXRvbm9zCUlvdGF0b25vcwxPbWljcm9udG9ub3MMVXBzaWxvbnRvbm9zCk9tZWdhdG9ub3MRaW90YWRpZXJlc2lzdG9ub3MFQWxwaGEEQmV0YQVHYW1tYQd1bmkwMzk0B0Vwc2lsb24EWmV0YQNFdGEFVGhldGEESW90YQVLYXBwYQZMYW1iZGECTXUCTnUCWGkHT21pY3JvbgJQaQNSaG8FU2lnbWEDVGF1B1Vwc2lsb24DUGhpA0NoaQNQc2kHdW5pMDNBOQxJb3RhZGllcmVzaXMPVXBzaWxvbmRpZXJlc2lzCmFscGhhdG9ub3MMZXBzaWxvbnRvbm9zCGV0YXRvbm9zCWlvdGF0b25vcxR1cHNpbG9uZGllcmVzaXN0b25vcwVhbHBoYQRiZXRhBWdhbW1hBWRlbHRhB2Vwc2lsb24EemV0YQNldGEFdGhldGEEaW90YQVrYXBwYQZsYW1iZGECbnUCeGkHb21pY3JvbgNyaG8HdW5pMDNDMgVzaWdtYQN0YXUHdXBzaWxvbgNwaGkDY2hpA3BzaQVvbWVnYQxpb3RhZGllcmVzaXMPdXBzaWxvbmRpZXJlc2lzDG9taWNyb250b25vcwx1cHNpbG9udG9ub3MKb21lZ2F0b25vcwd1bmkwM0QxB3VuaTAzRDIHdW5pMDNEMwd1bmkwM0Q0B3VuaTAzRDYHdW5pMDQwMAd1bmkwNDAxB3VuaTA0MDIHdW5pMDQwMwd1bmkwNDA0B3VuaTA0MDUHdW5pMDQwNgd1bmkwNDA3B3VuaTA0MDgHdW5pMDQwOQd1bmkwNDBBB3VuaTA0MEIHdW5pMDQwQwd1bmkwNDBEB3VuaTA0MEUHdW5pMDQwRgd1bmkwNDEwB3VuaTA0MTEHdW5pMDQxMgd1bmkwNDEzB3VuaTA0MTQHdW5pMDQxNQd1bmkwNDE2B3VuaTA0MTcHdW5pMDQxOAd1bmkwNDE5B3VuaTA0MUEHdW5pMDQxQgd1bmkwNDFDB3VuaTA0MUQHdW5pMDQxRQd1bmkwNDFGB3VuaTA0MjAHdW5pMDQyMQd1bmkwNDIyB3VuaTA0MjMHdW5pMDQyNAd1bmkwNDI1B3VuaTA0MjYHdW5pMDQyNwd1bmkwNDI4B3VuaTA0MjkHdW5pMDQyQQd1bmkwNDJCB3VuaTA0MkMHdW5pMDQyRAd1bmkwNDJFB3VuaTA0MkYHdW5pMDQzMAd1bmkwNDMxB3VuaTA0MzIHdW5pMDQzMwd1bmkwNDM0B3VuaTA0MzUHdW5pMDQzNgd1bmkwNDM3B3VuaTA0MzgHdW5pMDQzOQd1bmkwNDNBB3VuaTA0M0IHdW5pMDQzQwd1bmkwNDNEB3VuaTA0M0UHdW5pMDQzRgd1bmkwNDQwB3VuaTA0NDEHdW5pMDQ0Mgd1bmkwNDQzB3VuaTA0NDQHdW5pMDQ0NQd1bmkwNDQ2B3VuaTA0NDcHdW5pMDQ0OAd1bmkwNDQ5B3VuaTA0NEEHdW5pMDQ0Qgd1bmkwNDRDB3VuaTA0NEQHdW5pMDQ0RQd1bmkwNDRGB3VuaTA0NTAHdW5pMDQ1MQd1bmkwNDUyB3VuaTA0NTMHdW5pMDQ1NAd1bmkwNDU1B3VuaTA0NTYHdW5pMDQ1Nwd1bmkwNDU4B3VuaTA0NTkHdW5pMDQ1QQd1bmkwNDVCB3VuaTA0NUMHdW5pMDQ1RAd1bmkwNDVFB3VuaTA0NUYHdW5pMDQ5MAd1bmkwNDkxB3VuaTA0QjAHdW5pMDRCMQZXZ3JhdmUGd2dyYXZlBldhY3V0ZQZ3YWN1dGUJV2RpZXJlc2lzCXdkaWVyZXNpcwZZZ3JhdmUGeWdyYXZlB3VuaTIwNzQHdW5pMjBBMwd1bmkyMEE0B3VuaTIwQTkHdW5pMjBBQgRFdXJvB3VuaTIwQUUHdW5pMjBCMQd1bmkyMEI4B3VuaTIwQjkHdW5pMjBCQQd1bmkyMEJEB3VuaTIxMTYHdW5pMjIxNQNmX2kDZl9sB3VuaUY4RkYNb25lLm51bWVyYXRvcg90d28uZGVub21pbmF0b3INdHdvLm51bWVyYXRvcg90aHJlZS5udW1lcmF0b3IQZm91ci5kZW5vbWluYXRvcg5mb3VyLm51bWVyYXRvcgVyLmFsdAd1bmkwM0JDB3VuaTIxMDMHdW5pMjEwOQd1bmkyMTkwB3VuaTIxOTEHdW5pMjE5Mgd1bmkyMTkzB3VuaTIxOTYHdW5pMjE5Nwd1bmkyMTk4B3VuaTIxOTkHdW5pMjYzOQd1bmkyNjNBB3VuaTI3ODAHdW5pMjc4MQd1bmkyNzgyB3VuaTI3ODMHdW5pMjc4NAd1bmkyNzg1B3VuaTI3ODYHdW5pMjc4Nwd1bmkyNzg4B3VuaTI3OEEHdW5pMjc4Qgd1bmkyNzhDB3VuaTI3OEQHdW5pMjc4RQd1bmkyNzhGB3VuaTI3OTAHdW5pMjc5MQd1bmkyNzkyA2ZfYgNmX2YFZl9mX2IFZl9mX2gFZl9mX2kFZl9mX2oFZl9mX2sFZl9mX2wFZl9mX3QDZl9oA2ZfagNmX2sDZl90A3JfZgVyX2ZfZgNyX3QDdF9mA3RfdAtHb29nbGUubG9nbwZHLmxvZ28Gby5sb2dvBmcubG9nbwZsLmxvZ28GZS5sb2dvCWNhcm9uLmFsdA1kb3RhY2NlbnQuY2FwC3VuaTAwMjAwMzI2BUsuYWx0BWEuYWx0DnF1b3RlcmlnaHQuYWx0CXRocmVlLmNhcAABAAIABwAK//8ADwABAAAADAAAADoAQgACAAcAAQInAAECKAIqAAICKwIyAAECMwJmAAICZwJnAAECaAJoAAICaQJvAAEABAAAAAIAAAABAAAAAQAAAAAAAQAAAAoAeAC4AARERkxUABpjeXJsACpncmVrADpsYXRuAEoABAAAAAD//wADAAAAAQACAAQAAAAA//8AAwAAAAEAAgAEAAAAAP//AAMAAAABAAIAEAACTU9MIAAcUk9NIAAcAAD//wADAAAAAQACAAD//wABAAMABGNhbHQAGmNsaWcALmxpZ2EANGxvY2wAOgAAAAgAAAABAAIAAwAEAAUABgAHAAAAAQAIAAAAAQAJAAAAAQAQABEAJAAsADQAPABEAEwAVABeAGYAbgB2AH4AhgCOAJYAngCmAAYAAAABAIoABgAAAAEBEAAGAAAAAQGGAAYAAAABAewABgAAAAECUgAGAAAAAQKgAAYAAAACAu4DHAAEAAAAAQNUAAQAAAABA+oABAAAAAEFUgAEAAAAAQYWAAEAAAABBjIABAAAAAEGNgABAAAAAQZKAAQAAAABBk4AAQAAAAEGYgABAAAAAQZmAAMAAAADAC4ANAA6AA0AQABGAEwAUgBYAF4AZABqAHAAdgB8AIIAiAABAAAACgABAAEATAABAAEAVAABAAEAVAABAAEATAABAAEAUQABAAEASgABAAEARAABAAEAUQABAAEAVAABAAEATAABAAEAVAABAAEARAABAAEAWQABAAEASgABAAEAXQABAAEAWQADAAEAPAADACoAMAA2AAoAQgBIAE4AVABaAGAAZgBsAHIAeAABAAAACgABAAEATAABAAEAUQABAAEASgABAAECZAABAAEARAABAAEAUQABAAEAVAABAAEATAABAAEAVAABAAEARAABAAEAWQABAAEASgABAAEAXQABAAEAWQADAAIALAAyAAEAJgAJADgAPgBEAEoAUABWAFwAYgBoAAEAAAALAAEAAQBEAAEAAQJlAAEAAQJkAAEAAQBRAAEAAQBUAAEAAQBMAAEAAQBUAAEAAQBEAAEAAQBZAAEAAQBKAAEAAQBdAAEAAQBZAAMAAwA+AEQASgAEACYALAAyADgABQBQAFYAXABiAGgAAQAAAAwAAQABAFEAAQABAFQAAQABAEwAAQABAFQAAQABAmUAAQABAmUAAQABAmQAAQABAEQAAQABAFkAAQABAEoAAQABAF0AAQABAFkAAwAEACYALAAyADgAAQAgAAQAPgBEAEoAUAABAAAADQABAAEARAABAAECZgABAAECZQABAAECZQABAAECZAABAAEAWQABAAEASgABAAEAXQABAAEAWQADAAUAOAA+AEQASgBQAAQAIAAmACwAMgAAAAEAAAAOAAEAAQBZAAEAAQBKAAEAAQBdAAEAAQBZAAEAAQJnAAEAAQJmAAEAAQJlAAEAAQJlAAEAAQJkAAMAAQAaAAEAFAABACQAAQAAAA8AAQABAF0AAgABABUAHgAAAAIAAQAVAB4AAAADAAIAHgAmAAEAGAACADAAOAABAAAADwABAAEAXQABAAIABQBkAAIAAQAVAB4AAAABAAIABQBkAAIAAQAVAB4AAAABAJQAAwAMAFAAcgADAAgAIAAyAmMACwBUAFQATABRAEoARABRAFQATABUAioACABUAFQATABRAEoARAAsAioACABUAFQATABRAEoARABMAAQACgAQABYAHAI0AAIAKwI0AAIASwIzAAIAKAIzAAIASAAEAAoAEAAWABwCNAACACsCNAACAEsCMwACACgCMwACAEgAAQADAEwAdAB+AAEBXgAHABQA7gD4AQIBKAFCAUwAEgAmADIAPgBKAFYAYgBuAHoAhgCSAJoAogCqALIAugDCAMoA0gJQAAUADQAeAA4ADgJPAAUADQAdAA4ADgJOAAUADQAcAA4ADgJNAAUADQAbAA4ADgJMAAUADQAaAA4ADgJLAAUADQAZAA4ADgJKAAUADQAYAA4ADgJJAAUADQAXAA4ADgJIAAUADQAWAA4ADgJHAAMAHgAOAkYAAwAdAA4CRQADABwADgJEAAMAGwAOAkMAAwAaAA4CQgADABkADgJBAAMAGAAOAkAAAwAXAA4CPwADABYADgABAAQCNwACACMAAQAEAjoAAgAjAAQACgASABoAIAI+AAMAEgAOAj0AAwASAA0CPgACAA4CPQACAA0AAwAIAA4AFAI7AAIAFAI5AAIAQQI1AAIAEgABAAQCPAACACMAAgAGAAwCOAACACECNgACACMAAQAHAA0AEgAUAB8AIQBBAGEAAQDCAAMADACUALAADwAgACgAMAA4AEAASABQAFgAXgBkAGoAcAB2AHwAggJZAAMASwBZAlgAAwBLAFECVwADAEsAUAJWAAMASwBPAlUAAwBLAE4CVAADAEsATQJTAAMASwBHAloAAgBNAl0AAgBZAlwAAgBQAlsAAgBPAlIAAgBLAlEAAgBHAikAAgBRAigAAgBOAAMACAAQABYCXwADAEsASwJgAAIAWQJeAAIASwACAAYADAJiAAIAWQJhAAIASwABAAMASwBXAFkAAQAeAAEACAACAAYADgJkAAMAVABUAmUAAwBRAEoAAQABAEwAAQAGAiEAAQABAEQAAQAWAAEACAABAAQCZgAEAFQATABUAAEAAQBRAAEABgIjAAEAAQBEAAEAFgABAAgAAQAEAmgABABKAF0AWQABAAEAWQABAAYAPgABAAEAXQACAA4ABAFGAUcBSAFJAAEABAEeAR8BIgEjAAAAAQAAAAoAVABkAARERkxUABpjeXJsACZncmVrADJsYXRuAD4ABAAAAAD//wABAAAABAAAAAD//wABAAAABAAAAAD//wABAAAABAAAAAD//wABAAAAAWtlcm4ACAAAAAIAAAABAAIABgAQAAIAAAACABQA0gACAAAAAjwAPNQAAQCeAAQAAAAOACYAMAA+AD4ARABOAGAAagB0AH4AhACKAJAAfgACABb/4gAY//YAAwAXAAoAGgAKABsABQABABb/9gACABb/9gCC//YABAAW/+wAgP/2AIH/9gCC//YAAgAW/+IAgP/2AAIAGf/iABv/7AACABb/7AAX//YAAQAW/+IAAQC0/84AAQC0//YAAwBB/9gARP+wAIP/7AABAA4AEAAWABcAGAAZABoAGwAeACMAJQCDAJAAogImAAI6qgAEAAA0NjfKAFkASwAA//b/8f/x/+z/9v/2//b/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAA/+L/zv/s/+z/2AAAAAD/2P/OAAD/2AAAAAD/7P/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAA//YAAP/2AAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAD/zgAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/nP9+/+wAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAA//b/4gAA/+z/zgAAAAAAAAAAAAAAAAAAAAD/4gAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/sAAAAAD/9gAA/9j/7P/YAAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAD/xAAA/87/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAA/9gAAAAA/+L/2AAAAAAAAAAAAAAAAAAAAAD/2AAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/s//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+wAAAAAAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAA/+L/zgAAAAAAAAAAAAD/zgAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/87/nP/i/8T/xP/Y/8T/zv/YAAAAAP/iAAD/kgAA/8QAAAAA/84AAP/YAAD/pgAA/6b/2AAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/2P/YAAD/zv/s/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAAAAD/9gAA//YAAP/2/+z/7P/Y//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/8T/sAAAAAD/zv/s/87/4gAAAAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAD/sAAA/87/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+z/ugAA/93/3f/n/87/2P/TAAAAAAAAAAD/tf/2//EAAAAAAAAAAAAAAAD/v//Y/87/9gAAAAAAAP/EAAAAAAAA/7D/zv/iAAAAAAAAAAAAAAAAAAAAAAAA//b/zv+m/9j/7P/s/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/ugAFAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAA//b/9gAA//YAAP/2//b/7AAA//H/4gAAAAAAAAAAAAD/7P/7AAD/9v/s//YAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAP/EAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAP/s/+L/4v/x/+z/7P/sAAAAAAAAAAAAAP/x/+wAAAAA//YAAP/2//YAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/9v/2AAAAAAAA//YAAP/2AAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAD/9gAA//sAAP/7/+L/2P/2/+z/4v/2//H/7AAA//b/4v/2//b/4v/2//YAAAAAAAAAAP/2AAAAAAAAAAD/9gAAAAAAAAAA//YAAAAAAAAAAP/sAAAAAP/iAAD/7AAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/x/+z/7P/2/+z/9v/sAAAAAP/2AAAAAP/2/+wAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA//YAAAAA//YAAP/2AAAAAAAAAAD/7AAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAP/s//b/9v/s/+z/9v/s/8T/uv+m//YAAP/s/+L/zv/i/+z/xP/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YABf/2AAAAAAAA//b/9v/sAAAAAAAAAAD/5wAAAAD/2P/s/8QAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/x//b/7AAA//b/9v/2//b/7P/2/+z/7AAA//v/7AAAAAD/7P/2AAD/5//s//YAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAP/sAAAAAP/YAAD/9gAAAAD/9gAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA//b/8f/x/+z/9v/2//b/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/8f/x/+z/9v/2//b/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAAAAAAAAAAAAAAA//YAAAAA//b/8f/2//YAAP/7//b/7P/sAAAAAAAA//YAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/+IAAP/2/+L/2P/s/9P/4v/YAAAAAP/2AAAAAP/2/+wAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAA/87/7AAAAAAAAAAAAAD/7P/2AAAAAAAAAAAAAAAA/+z/7AAAAAAAAAAAAAAAAAAAAAAAAAAA//H/xAAA/9j/0//2/87/4v/YAAAAAAAAAAD/pv/2AAAAAAAA//YAAAAAAAD/tf/E/9j/9gAAAAD/7P+wAAAAAAAA/7r/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+cAAD/7P/i/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/4gAAAAD/9gAA//sAAP/2/+z/2P/2/93/2AAAAAD/7AAAAAD/2P/2AAD/4v/2//YAAAAAAAAAAP/2AAAAAAAAAAD/9gAA//v/9v/7AAAAAAAA//sAAAAAAAAAAP/YAAD/9gAAAAD/7AAA/8QAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAP/sAAAAAP/2AAAAAAAA/7r/kv9+/+z/4v/n/93/sP/E//H/pgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAP/s/+z/2AAAAAD/uv/Y/5wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//b/7P/2AAAAAAAAAAAAAP/2AAD/7P/2//YAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+L/7AAA//YAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAA/+f/5//x//H/9v/s//b/9gAA//b/4gAAAAAAAAAAAAD/9gAAAAD/9gAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IAAP+1/+L/2P/E/7//uv/E/7//pv+SAAAAAP/E/7D/xP+6/7D/kv/Y/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IABf/YAAD/9gAA/8T/2P/E/9gAAAAA/9j/4gAAAAAAAP/O/7D/4v/Y/9gAAAAAAAAAAAAA/+wAAP+wAAAAAP/2/9j/4gAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/2/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IAAP/O/+z/9v/d//YAAP/2/7r/sP+cAAAAAP/O/87/sP/O/9P/pv/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/sAAD/9gAA/9j/xP/i/+IAAAAA/9j/4v/2AAAAAP/E/7D/4v/i/9j/7P/2AAAAAAAA/+wAAP/iAAAAAAAA/+wAAP/sAAAAAAAA//YAAP/Y/+L/7P/i/+z/7P/2/9P/xP+6AAAAAP/O/8n/xP/Y/9j/uv/s/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/s//YAAAAA/9j/4v/i//YAAAAA/+z/4gAAAAD/xP/Y/87/9v/s/+wAAAAAAAAAAAAA/+wAAP/iAAAAAAAA/+IAAAAA//YAAAAA/90AAAAA/+z/4v/s/+L/4v/OAAAAAP/sAAAAAP/s/+cAAAAA/+wAAAAAAAAAAP/sAAAAAAAAAAAAAP/iAAAAAAAA/+IAAAAAAAAAAAAAAAAAAAAA//b/2AAA//b/7AAA/9j/7AAAAAAAAP/sAAD/7AAAAAAAAAAAAAAAAAAA/+wAAP/2AAAAAP/2AAAAAAAAAAAAAAAA/9gAAP+r/87/2P+1/87/zv/Y/7X/nP+IAAAAAP+m/5z/sP/E/6v/nP/Y/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/2P/OAAAAAP/2/7r/xP+6/84AAAAA/9j/2AAAAAAAAP+w/8T/xP/OAAD/9gAAAAAAAAAA/9gAAP+6/+IAAP/x/9j/4v/2/+wAAAAA/+wAAAAA//b/7P/2/+z/4v/iAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//b/9gAA//b/9v/2AAAAAAAAAAAAAP/7//sAAAAA//sAAP/7AAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/8f/7//H/9v/sAAAAAAAAAAAAAAAAAAAAAAAA//v/7P/x//YAAP/EAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAA//v/+//7//sAAAAA//sAAP/2AAAAAP/EAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAD/+wAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sAAAAAAAAAAP/7AAAAAP/OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAA/+wAAAAA/+z/2AAAAAAAAAAAAAAAAAAAAAD/7AAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAAAAAAAAAAAAAAD/2AAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+IAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/4gAAAAAAAAAAAAAAAAAAAAF//b/8QAA//b/9v/2AAAAAAAAAAAAAAAFAAUAAAAAAAUAAP/2//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+z/9v/2AAAAAAAAAAAAAAAAAAAAAP/n/+L/uv/i/+z/xAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAFP/2AAAAAAAA//YAAAAA/+wAAAAAAAD/7AAAAAD/7AAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAFAAAAAAAAP/7AAD/+//7AAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/9gAA//YAAP/2AAAAAAAAAAAAAAAAAAD/7AAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAA/+wAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/xAAAAAAAAAAAAAAAAP/iAAAAAAAA/+L/ugAAAAAAAAAAAAAAAP/iAAD/2AAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAD/+wAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAD/+wAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//EAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAAAAAAAP/iAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAAABQAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//v/9gAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4v/7//b/5wAA/+z/9v/2AAAAAAAAAAD/2P/7AAAAAAAA//sAAP/7AAD/4v/s//YAAAAAAAAAAP/iAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/OAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//H/8f/7//b/9v/sAAAAAAAAAAAAAAAAAAAAAAAA//v/7P/2//YAAP/YAAAAAAAAAAAAAP/OAAAAAAAAAAAAAAAA//b/9v/2//sAAAAA//YAAP/2AAAAAP/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAD/7AAAAAAAAAAAAAAAAAAA//H/7P/7//b/9v/sAAAAAAAAAAAAAAAAAAAAAAAA//v/7P/2//YAAP/iAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAA//v/+//7//sAAAAA//sAAP/7AAAAAP/EAAAAAAAAAAD/4gAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAA/+z/sAAA/9j/xAAA/87/4v/YAAAAAAAAAAD/sAAAAAAAAAAAAAAAAAAAAAD/xAAA/8T/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/zgAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/2AAA/+L/2AAA/9j/2P/OAAAAAAAAAAD/xAAAAAAAAP/OAAAAAAAAAAD/ugAA/9gAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/7oAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAA/8T/iP+wAAAAAP/i/8QAAAAA/9gAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP+6AAAAAAAA/9gAAAAAAAAAAAAAAAAAAP/sAAAAAAAAAAAAAAAAAAAAAAAA/+IAAAAA/+L/7P/Y/+L/7P/s/8T/iP/OAAAAAP/Y/84AAAAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAA/+L/4v/iAAAAAAAA/+IAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/ugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/sAAAACgAAAAoACgAKAAAAAAAAAAAAAP/n/+L/pv/O/+z/ugAK//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAD/4gAAAAD/2AAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAFAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//v/+//7//sAAAAA//sAAP/7AAAAAP/EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAA/87/nP+wAAAAAP/Y/9j/kgAA/+L/ugAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAP+6AAAAAAAA/8T/7AAA//b/zgAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/E/+IAAP/O/+L/4v/s/7r/iP+SAAAAAP+6/7AAAAAA/8T/Qv/i/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/9j/9v/YAAD/7AAA/9gAAAAAAAAAAAAAAAD/zgAAAAAAAP/EAAAAAAAA/9gAAAAA//YAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAAAA/+z/2AAA/+wAAAAAAAAAAAAA/+L/2P/E/+z/2AAAAAAAAAAAAAAAAAAAAAD/2AAA/9j/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAP/2AAr/+wAAAAUADwAFAAAAAAAAAAAAAP/7//sAAAAAAAoAAAAPAAoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAFAAAAAAAAAAX/9gAAAAAAAP/iAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUABQAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//v/9gAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/x//H/zv/O//b/zgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAD/4gAA/84AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//b/4v/iAAD/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAA/+IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/x//EAAAAA//EAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/4gAAAAAAAP/iAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/nAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s/+z/uv/O//b/ugAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAP/sAAD/4gAAAAD/xAAA/8QAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoABQAFAAoAAAAAAAAAAAAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKAAoAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7AAUABQAAAAAAAAAAAAAAAAAAAAAAAP/7//sAAAAA//sAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAJgABQAFAEsABgAGAFcABwAHAEYACAAIAA0ACgAKAA4ACwALAAIADAAMAEYADQANACwADgAOAFQADwAPAAMAEQARAEAAEgASADAAEwATAEAAFAAUAEoAFQAVAFMAGAAYABEAGQAZAAsAGgAaAAoAGwAbABAAHAAcAEkAHQAdAAcAHgAeAAwAHwAgAFUAJAAkAEIAJQAlAAQAJgAmABMAJwAnABQAKAAoABUAKQApABYAKgAqABcAKwArABgALAAsABkALQAuABoALwAvABwAMAAwAB0AMQAxAB4AMgAzABoANAA0AB8ANQA1ACAANgA2AB8ANwA3ACEAOAA4ACIAOQA5ACMAOgA6ACQAOwA7ACUAPAA8ACYAPQA9ACcAPgA+ACgAPwA/ACkAQABAACwAQQBBAAUAQgBCAFQARABEABIARgBGACoARwBHACsASABIAC0ASQBJAFYASgBKADEASwBLADIATABMADMATQBNAD0ATgBOADcATwBPADkAUABQADoAUQBRADsAUgBTAD0AVABUAD4AVQBVAD8AVgBWAFgAVwBXAEcAWABYAEgAWQBZAEwAWgBaAE0AWwBbAE4AXABcAE8AXQBdAFAAXgBeAFEAXwBfAFIAYABgACwAYgBiAFQAZABkAEsAZQBlAAkAbQBtAC8AbwBvADUAcQBxADAAcgByAC8AeQB5AE0AewB7AEEAfwB/ADYAgwCDAA8AhACJABMAigCKABcAiwCLABUAjACPABcAkQCTABsAlACUABYAlQCVABoAlgCaAB8AnACcAB8AnQCgACQAoQChACgAogCiAAEAowCjADQApACpACoAqgCqADEAqwCrAC0ArACvADEAsACwADcAsQCzADgAtAC0AAgAtQC1AD0AtgC6AD4AvAC8AD4AvQDAAE0AwQDBAFEAwgDCACsAwwDDAFEA9QD1ADwBEgESABcBEwETADECDAINADACDgIOAEQCDwIPAEUCEAIQAEMCEQIRAEQCEgISAEUCEwITAEMCFAIUAAYCFQIVAEACFgIWADUCFwIXADYCJwInAEoCKAIoADcCKQIpADsCPQJQAC4CUQJRACsCUgJSADICUwJTACsCVAJUAD0CVQJVADcCVgJWADkCVwJXADoCWAJYADsCWQJZAEwCWgJaAD0CWwJbADkCXAJcADoCXQJdAEwCXgJfADICYAJgAEwCYQJhADICYgJiAEwAAgB6AAcABwAkAAgACAA5AAoACgAdAAsACwAuAAwADAAkAA4ADgBKAA8ADwAvABEAEQARABIAEgBGABMAEwARABQAFAAUABUAFQApABYAFgA0ABcAFwA/ABgAGAA8ABkAGQA2ABoAGgA9ABsAGwA6ABwAHAAfAB0AHQA7AB4AHgAzAB8AIABJACQAJAAYACUAJQA4ACYAJgAKACgAKAABACwALAABAC8ALwAMADQANAABADYANgABADgAOABFADkAOQAXADoAOgAaADsAOwACADwAPAAZAD0APQANAD4APgAOAD8APwAqAEEAQQAwAEIAQgBKAEQARAA3AEYARgADAEcARwBDAEgASgAQAEsASwAEAEwATAAPAE0ATQBDAE4ATgAlAE8ATwAoAFAAUQBDAFIAUwArAFQAVAAQAFUAVQBCAFYAVgAQAFcAVwAtAFgAWAATAFkAWQAFAFoAWgAGAFsAWwAHAFwAXAAIAF0AXQAVAF4AXgAJAF8AXwAWAGIAYgBKAG0AbQAiAG4AbgBBAG8AbwAsAHEAcQBGAHIAcgAiAHYAdgAhAHcAdwAgAHkAeQBCAHsAewBIAH0AfQAcAH4AfgBBAH8AfwBHAIMAgwA1AIQAiQAKAIoAigALAIsAiwABAJAAkABEAJIAkwBEAJYAmgABAJwAnAABAJ0AoAAaAKEAoQAOAKMAowBDAKQAqgADAKsArwAQALAAsAAmALEAsQAnALIAswAmALQAtAAyALUAtQArALYAugAQALwAvAAQAL0AwAAGAMEAwQAJAMIAwgBDAMMAwwAJAPUA9QArARIBEgABARMBEwAQAgwCDQBGAg4CDgAeAg8CDwAjAhACEAASAhECEQAeAhICEgAjAhMCEwASAhQCFAAxAhUCFQARAhYCFgAsAhcCFwBHAhkCGQAbAiYCJgA+AicCJwAUAigCKQAEAj0CUABAAlECXQAEAl4CYAArAmECYgAFAAIAFwAFAAgAAAAKAA8ABAARABUACgAYACAADwAkAEIAGABEAEQANwBGAGAAOABiAGIAUwBkAGUAVABtAG0AVgBvAG8AVwBxAHIAWAB5AHkAWgB7AHsAWwB/AH8AXACDAJoAXQCcALoAdQC8AMMAlAD1APUAnAESARMAnQIMAhcAnwInAikAqwI9AmIArgABALYABAAAAA0AJAAqADAAPgBMAFoAaAByAIwAlgCcAKIArAABAQP/7AABAQP/zgADAYj/7AGO/+wBn//2AAMBgf/2AYj/4gGO//YAAwGA//YBiP/2AZ//9gADAYP/9gGO//YBn//2AAIBgP/YAZ//7AAGAYD/9gGB//YBg//2AYr/9gGO/+IBn//YAAIBgAAUAYj/7AABAYj/4gABAYj/7AACAdT/9gHX//YAAgHU//YB6v/2AAEADQEAAQMBdQGAAYEBgwGIAYoBjwGbAZ8B0QHXAAIN2AAEAAAMQA0OACcAKAAA//b/8f/2//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/9gAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7AAAAAD/zv/TAAAAAAAA/7X/8QAA/+cAAP+///YAAAAAAAAAAAAA/9j/zv/x//b/7P/sAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAD/7P/2//b/9gAA//H/4gAAAAD/9v/7//YAAAAAAAAAAAAAAAAAAP/2AAD/9gAAAAD/9v/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/7P/s/+z/7AAAAAAAAAAA/+z/9v/s//YAAAAA//b/9v/2AAD/9gAAAAD/9v/s/+L/9gAAAAD/9v/2//b/9gAAAAAAAP/2//YAAAAAAAD/7P/x//b/7P/sAAD/9gAAAAD/7AAA/+z/9gAAAAAAAAAAAAD/9v/2AAAAAAAA/+z/7P/iAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2//H/9v/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/7P/2//YAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//b/8f/2//b/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//b/9gAA//YAAAAAAAAAAAAAAAAAAAAAAAAAHgAAAAD/9gAA/+z/9v/7//b/7AAAAAD/9gAA//b/9gAAAAAAAAAAAAAAAAAAAAAAAP/2//YAAAAA//b/7AAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAP/i//YAAP/T/9gAAP/2AAAAAP/s//b/7AAAAAAAAAAAAAAAAAAAAAAAAAAA/+L/9v/Y/+IAAAAAABQAAAAAAAAAAP/2AAAAAAAAAAAAAAAA//YAAP/O//v/9v/s//b/3f/YAAAAAAAA//b/4gAA//b/+//2AAD/+wAA//b/9v/iAAAAAP/s/9j/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9v/s/84AAAAA/7r/fv/s/+L/3f/xAAAAAAAAAAD/zgAAAAAAAAAAAAAAAAAA//YAAAAA/8T/xP/2AAAAAAAAAAAAAP/2AAAABQAAAAAAAAAAAAD/7P/x/+z/9gAA//b/4gAAAAD/9gAA//YAAP/2AAAAAAAAAAD/9v/2AAD/9v/2//YAAAAAAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAA/+L/tf+w/7//xP+//5IAAAAA/7D/sP/O/9gAAAAA/+L/4gAFAAD/xAAAAAD/7P+6/87/4v+w/5wAAP/2/+L/xP/OAAAAAP+wAAAAAP/2AAD/3QAAAAD/4v/OAAD/7AAAAAD/5//s/9gAAAAAAAD/9gAAAAAAAP/2AAAAAP/s//b/4v/iAAAAAAAAAAAAAP/2AAD/9v/2//YAAAAA//YAAP/Y/6v/sP/O/9j/tf+IAAAAAP+c/6v/4v/YAAAAAP/n/9j/2AAA/7oAAAAA/9j/uv/Y/+z/zv+6AAD/7P/i/7r/zgAA//b/uv/iAAD/8QAAAAD/+//2//b/9gAAAAAAAAAA//v/+wAA//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//YAAAAAAAAAAAAAAAD/+wAAAAAAAAAAAAAAAAAAAAD/+wAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//sAAAAAAAAAAAAAAAD/9v/2//YAAAAAAAD/2AAAAAD/9gAA/8T/7AAAAAAAAAAAAAD/9v/2//b/9gAA//YAAP/2AAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAA/9j/uv+S/9j/4v+m/4gAAAAA/7r/uv/s/+IAAAAA/9j/2AAAAAD/xAAAAAD/4v+w/9j/2P+m/7AAAP/nAAD/2P/YAAD/9v/YAAAAAAAAAAAAAAAAAAD/9v/2AAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAP/s//YAAAAAAAD/7P/2AAD/7AAA/+IAAAAAAAAAAAAKAAD/7P/2//b/9v/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAUAAoACgAAAAAAAAAA//YAAAAAAAoAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAABQAAAAeAAAAAAAAAAAAAAAAAAAAAAAA/9gACgAAAAAAAAAAAAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAD/4gAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//YAAAAAAAAAAAAAAAD/7AAAAAAAAAAAAAAAAAAoAAAAAAAA//YAAP/s/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABf/s//b/9gAAAAAAAAAAAAUABf/2//YAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAD/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//YAAAAAAAAAAAAAAAD/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//v/9gAAAAAAAAAAAAAAAP/7AAAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+//2AAAAAAAAAAAAAAAA//v/+wAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/7//YAAAAAAAAAAAAAAAD/+//7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//v/9v/s//YAAAAAAAD/2AAA//v/9v/7/+L/9gAAAAAAAAAAAAD/7P/sAAD/9v/2AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/s//b/7AAAAAAAAAAAAAD/+//2//YAAAAAAAD/9v/2//v/9gAAAAAAAP/2AAAAAAAA/+wAAAAAAAAAAAAAAAAAAP/7//sAAAAAAAAAAAAA/+z/9v/sAAAAAAAAAAAAAP/7//b/9gAAAAAAAP/7//v/+//7AAAAAAAA/+wAAAAAAAD/9gAAAAAAAAAAAAAAAAAA//b/9gAAAAAAAAAAAAX/7AAAAAAAAAAAAAAAAAAFAAAAAAAAAAAAAAAA//v/+//7//sAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+wAAAAAAAAAAAAD/9v/iAAAAAAAAAAAAAAAA//H/9gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAAAAAAAAAAAAAD/8f/xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/+f/2AAAAAAAAAAAAAAAAP/s//YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/9gAAAAAAAP/iAAAAAAAAAAAAAAAAAAAAAP/2AAAAAAABAaAAZAAHAAcAFAAPAAYADgAIAAkACgAUABQAIAALAAgAFQAIAAQAFAAFAA8AFwAHAAsABQAIAAgACwAIAAgACAAMAAgADQAGAA8AFQAMABAAFwAIAAgAFwAUAAgAFAAMAAwACAASAAEAAgAZABoAGwAYAAMAHwAfABgAHwAfAB8AIQAfACIAEwAZACYAIQAlABoAHwAfABoAFgAfABYAIQAhAB8AGwAbACAAGQATACMAHAAdAB4AFgAWACAAGAAfACYAHwAPABkAEQAkAAEBogBiAA4AAAABACcAAAAmAAcAEAAAAA4AAAAAAA8AAAAGAAAAAAAAABsAAAAdACIAAAAAAAAAEAAAAAAAAQAAAAAAAQAOAA8AAQAIAAAAFQAAAAAAFgAAAAAAIwAAAB4AAgAXABQAFAAcAAoAHwAYABQAFAAUAAMAFAAUAAoAFAAkAAoADAAFAAoADQAUABkAFAAUABoAFAAUACAAFAAhAAoACgAlABQACgALABEAEgATAAMAFAAlABQAFAAFABQAAAAUAAkABAACAAEBoAIDAAAAAAAAAAEAAAAA1aSY2wAAAADTVnRHAAAAANezJNI=); }</style> - <filter id="drop-shadow-filter-0" x="-500%" y="-500%" width="1000%" height="1000%" bx:preset="drop-shadow 1 0 0 7 1 rgba(255,255,255,1)"> - <feGaussianBlur in="SourceAlpha" stdDeviation="7"/> - <feOffset dx="0" dy="0"/> - <feComponentTransfer result="offsetblur"> - <feFuncA id="spread-ctrl" type="linear" slope="2"/> - </feComponentTransfer> - <feFlood flood-color="rgba(255,255,255,1)"/> - <feComposite in2="offsetblur" operator="in"/> - <feMerge> - <feMergeNode/> - <feMergeNode in="SourceGraphic"/> - </feMerge> - </filter> - </defs> - <text style="font-family: &quot;Product Sans Medium&quot;; font-size: 26px; paint-order: stroke; stroke: rgb(255, 255, 255); stroke-linecap: round; stroke-linejoin: round; stroke-width: 3px; white-space: pre; filter: url(#drop-shadow-filter-0);" x="455.572" y="270.901">Installation Overview</text> - <image width="64" height="64" x="366.488" y="231.821" xlink:href="data:image/png;base64,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"/> -</svg> \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..5b04fdb --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "wor-flasher", + "version": "2.0.0", + "private": true, + "type": "module", + "description": "Release tooling for WoR-Flasher packaging; disk flashing remains in the shell engine.", + "license": "GPL-3.0-or-later", + "homepage": "https://github.com/blackoutsecure/wor-flasher#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/blackoutsecure/wor-flasher.git" + }, + "bugs": { + "url": "https://github.com/blackoutsecure/wor-flasher/issues" + }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/Botspot" + }, + { + "type": "github", + "url": "https://github.com/sponsors/blackoutsecure" + } + ], + "keywords": [ + "raspberry-pi", + "windows-on-raspberry", + "wor", + "flasher", + "arm64", + "macos", + "linux" + ], + "scripts": { + "clean": "node src/build-release.mjs --clean", + "build": "node src/build-release.mjs --platform=all", + "build:macos": "node src/build-release.mjs --platform=macos", + "build:linux": "node src/build-release.mjs --platform=linux", + "package:macos": "node src/build-release.mjs --platform=macos", + "package:all": "node src/build-release.mjs --platform=all", + "pe:check": "node src/check-pe-installer.mjs --check", + "pe:update": "node src/check-pe-installer.mjs --write-latest", + "metadata:check": "node src/sync-package-metadata.mjs --check", + "metadata:write": "node src/sync-package-metadata.mjs --write", + "update-check": "node src/updater.mjs --check-release", + "version:set": "node src/set-version.mjs", + "test": "node --test tests/*.test.mjs && ./tests/run-tests.sh", + "test:node": "node --test tests/*.test.mjs", + "check": "bash -n src/lib/*.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh src/macos-app/Contents/MacOS/WoR-Flasher tests/*.sh && node --check src/build-release.mjs && node --check src/check-pe-installer.mjs && node --check src/sync-package-metadata.mjs && node --check src/updater.mjs && node --check src/package-macos-app.mjs && node --check src/set-version.mjs && node --check src/lib/node-runtime.mjs && node --test tests/*.test.mjs && node src/sync-package-metadata.mjs --check && node src/build-release.mjs --check --platform=all" + }, + "engines": { + "node": ">=20" + } +} diff --git a/src/build-release.mjs b/src/build-release.mjs new file mode 100644 index 0000000..b9310f6 --- /dev/null +++ b/src/build-release.mjs @@ -0,0 +1,317 @@ +#!/usr/bin/env node +import { spawnSync } from "node:child_process"; +import { createHash } from "node:crypto"; +import { + existsSync, + mkdtempSync, + mkdirSync, + readFileSync, + readdirSync, + rmSync, + statSync, + writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join, relative } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + copyTree, + formatRuntimeManifest, + generateRuntimeManifest, + readProjectMetadata, + readRuntimePaths, +} from "./lib/node-runtime.mjs"; +import { checkPackageMetadata } from "./sync-package-metadata.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, ".."); +const releaseDir = join(repoDir, "release"); +const macosAppTemplate = join(repoDir, "src", "macos-app"); +const runtimePaths = Object.freeze(readRuntimePaths()); + +const platformPlans = Object.freeze({ + macos: { + directory: "macos", + description: "macOS app bundle", + build: buildMacos, + }, + linux: { + directory: "linux", + description: "Linux shell distribution", + build: buildLinux, + }, + windows: { + directory: "windows", + description: "Windows placeholder", + build: buildWindowsPlaceholder, + }, +}); + +const args = process.argv.slice(2); +const checkOnly = args.includes("--check"); +const cleanOnly = args.includes("--clean"); +const platform = valueFor("--platform") ?? "all"; +let checkDir; + +function valueFor(name) { + const arg = args.find((item) => item.startsWith(`${name}=`)); + return arg ? arg.split("=", 2)[1] : undefined; +} + +function fail(message) { + throw new Error(message); +} + +function run(command, commandArgs, env = {}) { + const result = spawnSync(command, commandArgs, { + cwd: repoDir, + env: { ...process.env, ...env }, + stdio: "inherit", + }); + if (result.status !== 0) fail(`${command} ${commandArgs.join(" ")} failed`); +} + +function assertExists(path, message) { + if (!existsSync(path)) fail(message); +} + +function walk(root) { + const entries = []; + for (const name of readdirSync(root)) { + const path = join(root, name); + const info = statSync(path); + if (info.isDirectory()) entries.push(...walk(path)); + else if (info.isFile()) entries.push(path); + } + return entries.sort(); +} + +function copyRuntime(targetRoot) { + for (const runtimePath of runtimePaths) { + const source = join(repoDir, runtimePath); + assertExists(source, `Missing runtime path: ${runtimePath}`); + copyTree(source, join(targetRoot, runtimePath)); + } +} + +function sha256(path) { + return createHash("sha256").update(readFileSync(path)).digest("hex"); +} + +function writeChecksums(root) { + const lines = walk(root) + .filter((file) => relative(root, file) !== "SHA256SUMS") + .map((file) => `${sha256(file)} ${relative(root, file)}`); + writeFileSync(join(root, "SHA256SUMS"), `${lines.join("\n")}\n`); +} + +function writeReadme(root, lines) { + writeFileSync(join(root, "README.txt"), `${lines.join("\n")}\n`); +} + +function createLinuxArchive(root, version) { + const archive = join(root, `WoR-Flasher-${version}-linux.tar.gz`); + run("tar", ["-czf", archive, "-C", root, "wor-flasher"]); + const listing = spawnSync("tar", ["-tzf", archive], { + cwd: repoDir, + encoding: "utf8", + }); + if (listing.status !== 0) fail(`Unable to verify ${archive}`); + for (const script of [ + "wor-flasher/install-wor.sh", + "wor-flasher/install-wor-gui.sh", + "wor-flasher/install-wor-hook.sh", + ]) { + if (!listing.stdout.split("\n").includes(script)) { + fail(`Linux archive is missing ${script}`); + } + } +} + +function selectedPlatforms() { + if (platform === "all") return ["macos", "linux", "windows"]; + if (!Object.hasOwn(platformPlans, platform)) { + fail( + `Unsupported platform '${platform}'. Supported values: all, macos, linux, windows.`, + ); + } + return [platform]; +} + +function preparePlatformDir(name) { + const base = checkDir ?? releaseDir; + const root = join(base, platformPlans[name].directory); + rmSync(root, { recursive: true, force: true }); + mkdirSync(root, { recursive: true }); + return root; +} + +function verifyShellRuntime() { + for (const runtimePath of runtimePaths) + assertExists(join(repoDir, runtimePath), `Missing ${runtimePath}`); + + const requiredTemplates = [ + "config.schema.json", + "config.json", + "pi3.config.txt", + "pi4.config.txt", + "pi5.config.txt", + "pi4-ram-unlock.ps1", + "pi4-ram-unlock-specialize.xml", + "oobe-network-bypass.xml", + "prefinalize.cmd", + ]; + + for (const templateFile of requiredTemplates) { + const fullPath = join(repoDir, "config-templates", templateFile); + assertExists(fullPath, `Missing config-templates/${templateFile}`); + if (statSync(fullPath).size === 0) { + fail(`Template config-templates/${templateFile} is empty`); + } + } + + const schemaPath = join(repoDir, "config-templates", "config.schema.json"); + const configJsonPath = join(repoDir, "config-templates", "config.json"); + try { + JSON.parse(readFileSync(schemaPath, "utf8")); + JSON.parse(readFileSync(configJsonPath, "utf8")); + } catch (err) { + fail(`Invalid JSON in configuration templates: ${err.message}`); + } + + //src/config/metadata.json is the source of truth for package.json's description, license, + //homepage, repository, bugs, funding, and keywords; a release must not ship them stale. + const packageDrift = checkPackageMetadata(); + if (packageDrift.length > 0) { + fail( + `package.json is out of sync with src/config/metadata.json (${packageDrift.map((d) => d.key).join(", ")}); run: node src/sync-package-metadata.mjs --write`, + ); + } +} + +function buildMacos(root) { + verifyShellRuntime(); + assertExists(macosAppTemplate, "src/macos-app is missing"); + assertExists( + join(macosAppTemplate, "Contents", "Info.plist"), + "src/macos-app/Contents/Info.plist is missing", + ); + assertExists( + join(macosAppTemplate, "Contents", "MacOS", "WoR-Flasher"), + "src/macos-app/Contents/MacOS/WoR-Flasher is missing", + ); + assertExists( + join(macosAppTemplate, "Contents", "Resources", "WoR-Flasher.icns"), + "src/macos-app/Contents/Resources/WoR-Flasher.icns is missing", + ); + const metadata = readProjectMetadata(); + const assetsDirname = metadata.product?.assetsDirname; + const logoFilename = metadata.product?.logoFilename; + if (!assetsDirname || !logoFilename) { + fail( + "product.assetsDirname or product.logoFilename is missing from src/config/metadata.json", + ); + } + const logoSource = join(repoDir, assetsDirname, logoFilename); + assertExists(logoSource, `Missing ${assetsDirname}/${logoFilename}`); + const appTarget = join(root, "WoR-Flasher.app"); + copyTree(macosAppTemplate, appTarget); + // The app bundle template does not check in its own logo copy; it is always + // staged from the single canonical source in assets/ to prevent drift. + copyTree(logoSource, join(appTarget, "Contents", "Resources", logoFilename)); + const pkg = JSON.parse(readFileSync(join(repoDir, "package.json"), "utf8")); + const runtimeTarget = join(appTarget, "Contents", "Resources", "runtime"); + const manifestTarget = join( + appTarget, + "Contents", + "Resources", + "runtime-manifest.json", + ); + const manifest = generateRuntimeManifest( + runtimeTarget, + pkg.version, + runtimePaths, + repoDir, + ); + writeFileSync(manifestTarget, formatRuntimeManifest(manifest)); + writeReadme(root, [ + "WoR-Flasher macOS release artifact", + "", + "This directory is regenerated by npm run build.", + "Publish the WoR-Flasher.app bundle after code signing/notarization review.", + "Disk flashing logic remains in install-wor.sh; Node only stages release artifacts.", + "", + "Verify SHA256SUMS before publishing.", + ]); + writeChecksums(root); +} + +function buildLinux(root) { + verifyShellRuntime(); + const payload = join(root, "wor-flasher"); + copyRuntime(payload); + createLinuxArchive(root, readProjectMetadata().product.version); + writeReadme(root, [ + "WoR-Flasher Linux release artifact", + "", + "This directory is regenerated by npm run build.", + "Publish WoR-Flasher-<version>-linux.tar.gz for Linux users.", + "Run ./wor-flasher/install-wor-gui.sh on a supported Debian-family desktop, or ./wor-flasher/install-wor.sh for CLI use.", + "Loop, snap, current boot and non-writable devices are hidden from the GUI device list by design.", + "", + "Verify SHA256SUMS before publishing.", + ]); + writeChecksums(root); +} + +function buildWindowsPlaceholder(root) { + writeReadme(root, [ + "WoR-Flasher Windows release placeholder", + "", + "Windows packaging is intentionally not implemented yet.", + "A Windows UI must get its own reviewed device-safety, elevation and removable-media design before this project ships one.", + "Until then, Windows users should use the official Windows on Raspberry Imager.", + ]); + writeChecksums(root); +} + +try { + if (args.includes("--help") || args.includes("-h")) { + console.log( + `WoR-Flasher Release Builder\n\nUsage: node src/build-release.mjs [options]\n\nOptions:\n --platform=all|macos|linux|windows Target platform (default: all)\n --check Validate release packaging without writing artifacts\n --clean Remove release/ staging directory\n --help, -h Show this help message\n`, + ); + process.exit(0); + } + + const unknownArg = args.find( + (arg) => + arg !== "--check" && arg !== "--clean" && !arg.startsWith("--platform="), + ); + if (unknownArg) fail(`Unsupported option '${unknownArg}'.`); + if (checkOnly && cleanOnly) + fail("--check and --clean cannot be used together."); + + if (cleanOnly) { + rmSync(releaseDir, { recursive: true, force: true }); + console.log("Removed release"); + } else { + checkDir = checkOnly + ? mkdtempSync(join(tmpdir(), "wor-flasher-release-check-")) + : undefined; + for (const name of selectedPlatforms()) { + const root = preparePlatformDir(name); + platformPlans[name].build(root); + } + + if (checkOnly) console.log("Release packaging plan is valid"); + else + console.log( + `Release artifacts staged in ${relative(repoDir, releaseDir)}`, + ); + } +} catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; +} finally { + if (checkDir) rmSync(checkDir, { recursive: true, force: true }); +} diff --git a/src/check-pe-installer.mjs b/src/check-pe-installer.mjs new file mode 100644 index 0000000..0b8a4cc --- /dev/null +++ b/src/check-pe-installer.mjs @@ -0,0 +1,154 @@ +#!/usr/bin/env node +import { createHash } from "node:crypto"; +import { existsSync, readFileSync, writeFileSync } from "node:fs"; +import { basename, dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { readProjectMetadata } from "./lib/node-runtime.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, ".."); +const defaultMetadataFile = join(repoDir, "src", "config", "metadata.json"); +const defaultReleaseApiUrl = + "https://api.github.com/repos/worproject/dldserv-mirror/releases/latest"; +const peAssetPattern = /^WoR-PE_Package_.*\.zip$/i; + +function fail(message, code = 1) { + console.error(message); + process.exit(code); +} + +function valueFor(args, name) { + const index = args.indexOf(name); + if (index !== -1) return args[index + 1]; + const prefix = `${name}=`; + const match = args.find((arg) => arg.startsWith(prefix)); + return match ? match.slice(prefix.length) : undefined; +} + +export function sha256Buffer(buffer) { + return createHash("sha256").update(buffer).digest("hex").toUpperCase(); +} + +export async function readUrlBytes(url) { + if (url.startsWith("file://")) { + return readFileSync(fileURLToPath(url)); + } + if (!url.startsWith("https://")) { + throw new Error(`refusing to download non-HTTPS URL: ${url}`); + } + const response = await fetch(url, { + headers: { "User-Agent": "WoR-Flasher-PE-Checker" }, + signal: AbortSignal.timeout(30000), + }); + if (!response.ok) { + throw new Error(`download failed for ${url}: HTTP ${response.status}`); + } + return Buffer.from(await response.arrayBuffer()); +} + +export async function hashUrl(url) { + return sha256Buffer(await readUrlBytes(url)); +} + +export function selectPeAsset(release) { + const assets = Array.isArray(release?.assets) ? release.assets : []; + const asset = assets.find((item) => peAssetPattern.test(item?.name || "")); + if (!asset?.browser_download_url) { + throw new Error( + "latest dldserv-mirror release does not contain a WoR-PE_Package_*.zip asset", + ); + } + return asset; +} + +export async function fetchLatestPeAsset(releaseApiUrl = defaultReleaseApiUrl) { + const response = await fetch(releaseApiUrl, { + headers: { "User-Agent": "WoR-Flasher-PE-Checker" }, + signal: AbortSignal.timeout(10000), + }); + if (!response.ok) { + throw new Error(`GitHub release lookup failed: HTTP ${response.status}`); + } + return selectPeAsset(await response.json()); +} + +export async function verifyPinnedPeInstaller( + metadataFile = defaultMetadataFile, +) { + const metadata = readProjectMetadata(metadataFile); + const url = metadata.systemDefaults?.peInstallerUrl; + const expected = metadata.systemDefaults?.peInstallerSha256; + if (!url || !expected) { + throw new Error( + "metadata is missing systemDefaults.peInstallerUrl or peInstallerSha256", + ); + } + const actual = await hashUrl(url); + return { + ok: actual.toUpperCase() === expected.toUpperCase(), + url, + expected, + actual, + }; +} + +export async function updatePeInstallerMetadata({ + metadataFile = defaultMetadataFile, + releaseApiUrl = defaultReleaseApiUrl, +} = {}) { + if (!existsSync(metadataFile)) { + throw new Error(`missing project metadata: ${metadataFile}`); + } + const metadata = readProjectMetadata(metadataFile); + const asset = await fetchLatestPeAsset(releaseApiUrl); + const url = asset.browser_download_url; + const sha256 = await hashUrl(url); + metadata.systemDefaults = metadata.systemDefaults || {}; + metadata.systemDefaults.peInstallerUrl = url; + metadata.systemDefaults.peInstallerSha256 = sha256; + writeFileSync(metadataFile, `${JSON.stringify(metadata, null, 2)}\n`); + return { url, sha256, name: asset.name || basename(url) }; +} + +async function main() { + const args = process.argv.slice(2); + if (args.includes("--help") || args.includes("-h")) { + console.log( + `WoR-Flasher PE installer metadata tool\n\nUsage:\n node src/check-pe-installer.mjs --check [--metadata FILE]\n node src/check-pe-installer.mjs --write-latest [--metadata FILE] [--release-api-url URL]\n\nOptions:\n --check Download the pinned PE installer and verify peInstallerSha256\n --write-latest Resolve the latest WoR-PE asset, hash it, and update metadata\n --metadata FILE Project metadata file (default: src/config/metadata.json)\n --release-api-url GitHub release API URL for latest lookup\n`, + ); + return; + } + + const metadataFile = valueFor(args, "--metadata") || defaultMetadataFile; + if (args.includes("--write-latest")) { + const result = await updatePeInstallerMetadata({ + metadataFile, + releaseApiUrl: + valueFor(args, "--release-api-url") || defaultReleaseApiUrl, + }); + console.log(`Updated PE installer metadata: ${result.name}`); + console.log(`URL: ${result.url}`); + console.log(`SHA-256: ${result.sha256}`); + return; + } + + if (args.includes("--check") || args.length === 0) { + const result = await verifyPinnedPeInstaller(metadataFile); + if (!result.ok) { + console.error(`PE installer SHA-256 mismatch for ${result.url}`); + console.error(`Expected: ${result.expected}`); + console.error(`Actual: ${result.actual}`); + process.exit(1); + } + console.log(`PE installer SHA-256 verified: ${result.actual}`); + return; + } + + fail("Unsupported option. Run with --help for usage.", 2); +} + +if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { + main().catch((error) => + fail(error instanceof Error ? error.message : String(error)), + ); +} diff --git a/src/config/metadata.json b/src/config/metadata.json new file mode 100644 index 0000000..802f171 --- /dev/null +++ b/src/config/metadata.json @@ -0,0 +1,62 @@ +{ + "$schema": "./metadata.schema.json", + "product": { + "name": "WoR-Flasher", + "version": "2.0.0", + "description": "Release tooling for WoR-Flasher packaging; disk flashing remains in the shell engine.", + "license": "GPL-3.0-or-later", + "keywords": [ + "raspberry-pi", + "windows-on-raspberry", + "wor", + "flasher", + "arm64", + "macos", + "linux" + ], + "fundingUrls": [ + "https://github.com/sponsors/Botspot", + "https://github.com/sponsors/blackoutsecure" + ], + "bundleIdentifier": "com.botspot.wor-flasher", + "minimumMacosVersion": "13.0", + "iconName": "WoR-Flasher", + "iconFilename": "WoR-Flasher.icns", + "assetsDirname": "assets", + "logoFilename": "logo-full.png" + }, + "systemDefaults": { + "peInstallerUrl": "https://github.com/worproject/dldserv-mirror/releases/download/13%2F02%2F2024/WoR-PE_Package_1.1.0.zip", + "peInstallerSha256": "A039E28FE7E39147899B0634C15E336C3B26A6F76201092EBB9732474CD43D0A", + "uefiShellPackageUrl": "https://github.com/tianocore/edk2/releases/download/edk2-stable202002/ShellBinPkg.zip", + "uefiShellPackageSha256": "d6876ef9b9a48ca612149b888d4f4e923d1b8ccf495b8f6f6c3f83defb40b56a", + "uefiVerPi3": "v1.39", + "uefiVerPi4": "v1.50", + "uefiVerPi5": "v0.3", + "uefiRepoPi3": "pftf/RPi3", + "uefiRepoPi4": "pftf/RPi4", + "uefiRepoPi5": "worproject/rpi5-uefi", + "driverVer": "v0.17", + "driversRepo": "worproject/RPi-Windows-Drivers", + "armv80MaxBuild": 25163, + "win11MinBuild": 22000, + "win10OldestBuild": "17134.112", + "exampleBid": "22621.525", + "armv80SafeBid": "22631.2861", + "repoSlug": "blackoutsecure/wor-flasher", + "logDirname": "logs" + }, + "runtimePaths": [ + "README.md", + "LICENSE", + "NOTICE", + "install-wor.sh", + "install-wor-gui.sh", + "install-wor-hook.sh", + "src/lib", + "src/config", + "src/updater.mjs", + "config-templates", + "assets" + ] +} diff --git a/src/config/metadata.schema.json b/src/config/metadata.schema.json new file mode 100644 index 0000000..c640df8 --- /dev/null +++ b/src/config/metadata.schema.json @@ -0,0 +1,120 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "WoR-Flasher Project Metadata", + "description": "Project/runtime metadata used by shell runtime and Node release tooling. User run configuration belongs in config-templates/config.json.", + "type": "object", + "required": ["product", "systemDefaults", "runtimePaths"], + "properties": { + "$schema": { + "type": "string" + }, + "product": { + "type": "object", + "required": [ + "name", + "version", + "description", + "license", + "keywords", + "fundingUrls", + "bundleIdentifier", + "minimumMacosVersion", + "iconName", + "iconFilename", + "assetsDirname", + "logoFilename" + ], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "version": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$" + }, + "description": { "type": "string", "minLength": 1 }, + "license": { "type": "string", "minLength": 1 }, + "keywords": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "minItems": 1 + }, + "fundingUrls": { + "type": "array", + "items": { "type": "string", "pattern": "^https://" }, + "minItems": 1 + }, + "bundleIdentifier": { "type": "string", "minLength": 1 }, + "minimumMacosVersion": { + "type": "string", + "pattern": "^[0-9]+\\.[0-9]+$" + }, + "iconName": { "type": "string", "minLength": 1 }, + "iconFilename": { "type": "string", "minLength": 1 }, + "assetsDirname": { "type": "string", "minLength": 1 }, + "logoFilename": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "systemDefaults": { + "type": "object", + "required": [ + "peInstallerUrl", + "peInstallerSha256", + "uefiShellPackageUrl", + "uefiShellPackageSha256", + "uefiVerPi3", + "uefiVerPi4", + "uefiVerPi5", + "uefiRepoPi3", + "uefiRepoPi4", + "uefiRepoPi5", + "driverVer", + "driversRepo", + "armv80MaxBuild", + "win11MinBuild", + "win10OldestBuild", + "exampleBid", + "armv80SafeBid", + "repoSlug", + "logDirname" + ], + "properties": { + "peInstallerUrl": { "type": "string", "pattern": "^https://" }, + "peInstallerSha256": { + "type": "string", + "pattern": "^[A-Fa-f0-9]{64}$" + }, + "uefiShellPackageUrl": { "type": "string", "pattern": "^https://" }, + "uefiShellPackageSha256": { + "type": "string", + "pattern": "^[A-Fa-f0-9]{64}$" + }, + "uefiVerPi3": { "type": "string", "minLength": 1 }, + "uefiVerPi4": { "type": "string", "minLength": 1 }, + "uefiVerPi5": { "type": "string", "minLength": 1 }, + "uefiRepoPi3": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "uefiRepoPi4": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "uefiRepoPi5": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "driverVer": { "type": "string", "minLength": 1 }, + "driversRepo": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "armv80MaxBuild": { "type": "integer", "minimum": 1 }, + "win11MinBuild": { "type": "integer", "minimum": 1 }, + "win10OldestBuild": { "type": "string", "minLength": 1 }, + "exampleBid": { "type": "string", "minLength": 1 }, + "armv80SafeBid": { "type": "string", "minLength": 1 }, + "repoSlug": { "type": "string", "pattern": "^[^/]+/[^/]+$" }, + "logDirname": { "type": "string", "minLength": 1 } + }, + "additionalProperties": false + }, + "runtimePaths": { + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "minLength": 1, + "not": { "pattern": "(^/|(^|/)\\.\\.(/|$))" } + } + } + }, + "additionalProperties": false +} diff --git a/src/lib/cleanup.sh b/src/lib/cleanup.sh new file mode 100644 index 0000000..ded268c --- /dev/null +++ b/src/lib/cleanup.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +#Shared mount, device and temporary-file cleanup used by both the CLI and GUI execution paths. +#Every register_* helper re-arms the EXIT trap, so an interrupted run still releases what it claimed. +cleanup_mounts() { + local mountpoint + for mountpoint in "${CLEANUP_MOUNTS[@]}" ;do + if is_macos ;then + diskutil unmount force "$mountpoint" >/dev/null 2>&1 + else + sudo umount -q "$mountpoint" 2>/dev/null + fi + done + for mountpoint in "${CLEANUP_DEVICES[@]}" ;do + if is_macos ;then + hdiutil detach "$mountpoint" >/dev/null 2>&1 + fi + done + local file + for file in "${CLEANUP_FILES[@]}" ;do + rm -f "$file" + done +} + +register_mount_cleanup() { #Input: mountpoint + CLEANUP_MOUNTS+=("$1") + trap cleanup_mounts EXIT +} + +register_device_cleanup() { #Input: hdiutil device + CLEANUP_DEVICES+=("$1") + trap cleanup_mounts EXIT +} + +register_file_cleanup() { #Input: file path + CLEANUP_FILES+=("$1") + trap cleanup_mounts EXIT +} diff --git a/src/lib/dependencies.sh b/src/lib/dependencies.sh new file mode 100644 index 0000000..0687d69 --- /dev/null +++ b/src/lib/dependencies.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +#Shared dependency declarations consumed by the engine and the native macOS preflight. +#Data only, so every entry point validates or installs exactly the same tools. +WOR_MACOS_BREW_FORMULAE=(aria2 cabextract jq wget wimlib gptfdisk pv) +WOR_LINUX_PACKAGES=(yad aria2 cabextract wimtools chntpw genisoimage exfat-fuse wget udftools bc parted dosfstools unzip git pv) diff --git a/src/lib/gui.sh b/src/lib/gui.sh new file mode 100644 index 0000000..0ac11d4 --- /dev/null +++ b/src/lib/gui.sh @@ -0,0 +1,293 @@ +#!/bin/bash + +#GUI definitions for both front-ends, in three sections: shared, then macOS, then Linux. +#The two toolkits render very differently, but they must never describe the same option differently, +#which is what happened when a "not recommended" marker was added on macOS and the Linux wording +#drifted behind it. Kept in one file to match cleanup.sh, which likewise branches by platform inside +#a single concern, and because every entry point sources this unconditionally on both platforms. + +# --- Shared ----------------------------------------------------------------------------------- +#Every user-facing option label lives here exactly once. +#Ordering stays with each front-end: yad parses its results positionally, so its field order is load-bearing. + +#Output: key<TAB>label<TAB>caution for every Advanced Options toggle. +#caution 1 marks an option that departs from the tested defaults, so both front-ends can flag it. +wor_advanced_toggles() { #Input: pinned UEFI version, pinned driver version, Pi model. + printf 'oobe\tAllow Windows setup to continue without a network connection\t0\n' + printf 'pi4\t%s\t0\n' "$(wor_pi4_label "$3")" + printf 'uefi\tUse the latest UEFI firmware instead of the tested pinned version (%s)\t1\n' "$1" + printf 'drivers\tUse the latest Windows ARM64 drivers instead of the pinned version (%s)\t0\n' "$2" + printf 'verify\tSkip verifying the written image after flashing\t1\n' + printf 'dryrun\tSkip flashing the device (dry run)\t0\n' +} + +wor_advanced_label() { #Input: key, pinned UEFI version, pinned driver version, Pi model. Output: label. + wor_advanced_toggles "$2" "$3" "$4" | awk -F'\t' -v key="$1" '$1 == key {print $2}' +} + +wor_advanced_caution() { #Input: key. Output: 1 when the option departs from the tested defaults. + wor_advanced_toggles '' '' '' | awk -F'\t' -v key="$1" '$1 == key {print $3}' +} + +wor_pi4_label() { #Input: Pi model. Output: label for the Pi 4 RAM-unlock toggle. + if [ "$1" == 4 ];then + printf 'Automatically disable the Pi 4 3 GB RAM limit after install' + else + printf 'Automatically disable the Pi 4 3 GB RAM limit after install (not applicable to the Pi %s)' "$1" + fi +} + +wor_config_scope() { #Input: 1 when the installer writes the target drive directly. Output: where config.txt lands. + if [ "$1" == 1 ];then + printf 'boot partition' + else + printf 'recovery media, not the Windows drive' + fi +} + +wor_config_txt_label() { #Input: config scope. Output: the config.txt toggle label. + printf 'Apply the customized config.txt to the %s' "$1" +} + +wor_host_platform() { #Output: the config key naming this host: macos, linux or other. + case "$(uname -s 2>/dev/null)" in + Darwin) printf 'macos' ;; + Linux) printf 'linux' ;; + *) printf 'other' ;; + esac +} + +wor_sound_options() { #Output: value<TAB>label for every completion sound this host can play, or nothing. + local name + case "$(wor_host_platform)" in + macos) + #every macOS install ships these in /System/Library/Sounds, so nothing has to be bundled + for name in Basso Blow Bottle Frog Funk Glass Hero Morse Ping Pop Purr Sosumi Submarine Tink ;do + printf '%s\t%s\n' "$name" "$name" + done + ;; + linux) + #freedesktop sound-theme event names, resolved by canberra or the shipped .oga files + printf 'complete\tComplete\n' + printf 'bell\tBell\n' + printf 'message\tMessage\n' + printf 'device-added\tDevice added\n' + printf 'dialog-information\tInformation\n' + ;; + esac +} + +wor_sound_default() { #Output: the completion sound used when the config names none, or nothing on a host with no catalogue. + case "$(wor_host_platform)" in + macos) printf 'Glass' ;; + linux) printf 'complete' ;; + esac +} + +wor_sound_label() { #Input: sound value. Output: its menu label, or the value itself when unknown. + wor_sound_options | awk -F'\t' -v v="$1" '$1 == v {print $2; found=1} END {if (!found) print v}' +} + +wor_completion_sound() { #Output: the sound to play, ignoring a value this host cannot play. + local candidate="${COMPLETION_SOUND:-}" + if [ -n "$candidate" ] && wor_sound_options | awk -F'\t' -v v="$candidate" '$1 == v {found=1} END {exit !found}' ;then + printf '%s' "$candidate" + return 0 + fi + wor_sound_default +} + + +# --- macOS (AppKit through JXA) ---------------------------------------------------------------- +#Linux has no equivalent of these calls; they are macOS-only by nature rather than by omission. + +wor_jxa_window_lib() { #Output: the JXA every window is built with, so no screen can drift from the others. + cat <<'JXA' +//Prepended to every WoR-Flasher window. Each screen still lays out its own content and picks its own +//size, but the title bar, menu bar and Dock icon come from here so they cannot differ between screens. +function worSetAppIcon(app, iconPath) { + if (iconPath && iconPath.length > 0) { + app.setApplicationIconImage($.NSImage.alloc.initWithContentsOfFile($(iconPath))) + } +} + +function worAppVersion(windowTitle, appTitle) { + const prefix = appTitle + ' v' + if (windowTitle && windowTitle.indexOf(prefix) === 0) return windowTitle.substring(prefix.length) + return '' +} + +//macOS does not deliver app-menu action dispatch to ANY item - custom or native, including Quit - +//while the screen-specific app.runModalForWindow session is active (every screen using this menu is one +//of those). A clickable-looking item that silently does nothing on click is worse than a disabled, +//informational one, so this shows the product name and version only; Quit remains reachable through +//the Dock, Cmd-Q (handled separately via Apple Events, see worInstallWindowHandlers) and each +//screen-specific Cancel/Abort/close-box button. +function worInstallAppMenu(app, appTitle, windowTitle, iconPath) { + const mainMenu = $.NSMenu.alloc.initWithTitle(appTitle) + const appMenuItem = $.NSMenuItem.alloc.init + appMenuItem.title = appTitle + const appMenu = $.NSMenu.alloc.initWithTitle(appTitle) + const version = worAppVersion(windowTitle, appTitle) + const infoItem = appMenu.addItemWithTitleActionKeyEquivalent(version.length > 0 ? appTitle + ' v' + version : appTitle, '', '') + infoItem.enabled = false + appMenuItem.submenu = appMenu + mainMenu.addItem(appMenuItem) + app.mainMenu = mainMenu + //returned so a screen can append its own menu, e.g. Edit for the text fields in Advanced Options + return mainMenu +} + +//options: width, height, title, delegate, closable (default true) +function worMakeWindow(options) { + let style = $.NSWindowStyleMaskTitled + if (options.miniaturizable !== false) style = style | $.NSWindowStyleMaskMiniaturizable + //a screen opts out only where closing the window would not stop the work behind it + if (options.closable !== false) style = style | $.NSWindowStyleMaskClosable + const window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer($.NSMakeRect(0, 0, options.width, options.height), style, $.NSBackingStoreBuffered, false) + window.title = options.title + window.center + //no window here is resizable, and without that mask the zoom button is drawn disabled rather than + //omitted, so hide it outright instead of leaving a dead third dot in the title bar + const zoomButton = window.standardWindowButton($.NSWindowZoomButton) + if (zoomButton) zoomButton.hidden = true + if (options.delegate) window.setDelegate(options.delegate) + window.contentView = $.NSView.alloc.initWithFrame($.NSMakeRect(0, 0, options.width, options.height)) + return window +} + +//a coloured trailing note tells the reader at a glance whether an option matches the tested defaults +function worAnnotateCheckbox(checkbox, label, note, color) { + const suffix = ' ' + note + const title = $.NSMutableAttributedString.alloc.init + title.mutableString.appendString($(label + suffix)) + const labelRange = $.NSMakeRange(0, label.length) + const noteRange = $.NSMakeRange(label.length, suffix.length) + title.addAttributeValueRange($.NSFontAttributeName, $.NSFont.systemFontOfSize(13), labelRange) + title.addAttributeValueRange($.NSForegroundColorAttributeName, $.NSColor.labelColor, labelRange) + title.addAttributeValueRange($.NSFontAttributeName, $.NSFont.systemFontOfSizeWeight(13, $.NSFontWeightSemibold), noteRange) + title.addAttributeValueRange($.NSForegroundColorAttributeName, color, noteRange) + checkbox.attributedTitle = title +} + +//the Dock Quit item sends an aevt/quit Apple Event that a modal session would otherwise never see, +//and a modal session starves the default run loop, so events have to be pumped by hand +function worInstallWindowHandlers(controller) { + $.NSAppleEventManager.sharedAppleEventManager.setEventHandlerAndSelectorForEventClassAndEventID(controller, 'handleQuitEvent:withReplyEvent:', 0x61657674, 0x71756974) + $.NSAppleEventManager.sharedAppleEventManager.setEventHandlerAndSelectorForEventClassAndEventID(controller, 'handleReopenEvent:withReplyEvent:', 0x61657674, 0x72617070) + const pumpTimer = $.NSTimer.timerWithTimeIntervalTargetSelectorUserInfoRepeats(0.25, controller, 'pumpEvents:', $(), true) + $.NSRunLoop.currentRunLoop.addTimerForMode(pumpTimer, $.NSModalPanelRunLoopMode) + return pumpTimer +} +JXA +} + + +# --- Linux (yad) ------------------------------------------------------------------------------ +#macOS has no equivalent of these flags; they are Linux-only by nature rather than by omission. + +wor_detect_yad_screen() { #Sets WOR_YAD_SCREEN_WIDTH/HEIGHT from the active desktop, with a conservative fallback. + local geometry='' + [[ "${WOR_YAD_SCREEN_WIDTH:-}x${WOR_YAD_SCREEN_HEIGHT:-}" =~ ^[0-9]+x[0-9]+$ ]] && return 0 + if command -v xrandr >/dev/null ;then + geometry="$(xrandr --current 2>/dev/null | awk '$0 ~ /[0-9]+x[0-9]+/ && $0 ~ /\*/ {print $1; exit}')" + fi + if [[ ! "$geometry" =~ ^[0-9]+x[0-9]+$ ]] && command -v xdpyinfo >/dev/null ;then + geometry="$(xdpyinfo 2>/dev/null | awk '/dimensions:/ {print $2; exit}')" + fi + if [[ ! "$geometry" =~ ^[0-9]+x[0-9]+$ ]] && command -v xwininfo >/dev/null ;then + local root_width root_height + root_width="$(xwininfo -root 2>/dev/null | awk '/Width:/ {print $2; exit}')" + root_height="$(xwininfo -root 2>/dev/null | awk '/Height:/ {print $2; exit}')" + geometry="${root_width}x${root_height}" + fi + [[ "$geometry" =~ ^[0-9]+x[0-9]+$ ]] || geometry='1024x768' + WOR_YAD_SCREEN_WIDTH="${geometry%x*}" + WOR_YAD_SCREEN_HEIGHT="${geometry#*x}" +} + +wor_yad_width() { #Input: desired width. Output: width clamped inside the detected desktop. + local desired_width="$1" maximum_width=$((WOR_YAD_SCREEN_WIDTH - 40)) + [ "$maximum_width" -gt 0 ] || maximum_width="$WOR_YAD_SCREEN_WIDTH" + [ "$desired_width" -le "$maximum_width" ] || desired_width="$maximum_width" + printf '%s' "$desired_width" +} + +wor_yad_height() { #Input: desired height. Output: height clamped inside the detected desktop. + local desired_height="$1" maximum_height=$((WOR_YAD_SCREEN_HEIGHT - 60)) + [ "$maximum_height" -gt 0 ] || maximum_height="$WOR_YAD_SCREEN_HEIGHT" + [ "$desired_height" -le "$maximum_height" ] || desired_height="$maximum_height" + printf '%s' "$desired_height" +} + +wor_yad_image_for_screen() { #Input: preferred image, fallback image, minimum width and height for preferred image. + if [ "$WOR_YAD_SCREEN_WIDTH" -ge "$3" ] && [ "$WOR_YAD_SCREEN_HEIGHT" -ge "$4" ];then + printf '%s' "$1" + else + printf '%s' "$2" + fi +} + +wor_init_yad_flags() { #Sets the shared yadflags array. Assigns rather than echoes, because it is an array. + wor_detect_yad_screen + export GIO_USE_VOLUME_MONITOR=unix + yadflags=(--center --fixed --buttons-layout=center --width="$(wor_yad_width 400)" --height="$(wor_yad_height 250)" --window-icon="$WOR_LOGO_PATH" --class="$WOR_ICON_NAME" --title="$WOR_WINDOW_TITLE" --separator='\n') +} + +wor_yad_label() { #Input: label, caution flag. Output: label with the shared not-recommended marker. + #yad cannot colour a checkbox label, so the macOS red badge becomes plain text here + if [ "$2" == 1 ];then + printf '%s (not recommended)' "$1" + else + printf '%s' "$1" + fi +} + +wor_yad_bool() { #Input: 1 or 0. Output: the TRUE/FALSE yad expects for a check box. + [ "$1" == 1 ] && printf 'TRUE' || printf 'FALSE' +} + +wor_play_result_sound() { #Input: success or failure. The macOS side uses NSSound; this is the yad equivalent. + #Best effort only: backgrounded so it cannot delay the dialog, and always successful so a desktop + #without a sound theme or player never turns a finished flash into a failed run + [ "${PLAY_SOUND:-1}" == 1 ] || return 0 + local event='dialog-error' + if [ "$1" == success ];then + #only the completion sound is user-selectable; a failure keeps the desktop's own error sound + event="$(wor_completion_sound)" + [ -n "$event" ] || return 0 + fi + if command -v canberra-gtk-play >/dev/null ;then + canberra-gtk-play -i "$event" >/dev/null 2>&1 & + elif command -v paplay >/dev/null && [ -f "/usr/share/sounds/freedesktop/stereo/${event}.oga" ] ;then + paplay "/usr/share/sounds/freedesktop/stereo/${event}.oga" >/dev/null 2>&1 & + fi + return 0 +} + +wor_show_result_notification() { #Input: success or failure. A flash is long enough to walk away from, so the desktop reports the result too. + #Best effort only: backgrounded, and always successful, so a denied notification permission or a + #desktop with no notification daemon never turns a finished flash into a failed run + [ "${SHOW_NOTIFICATION:-1}" == 1 ] || return 0 + local title="${WOR_APP_TITLE:-WoR-Flasher}" body + if [ "$1" == success ];then + body="Finished flashing ${DEVICE:-the drive}. It is ready to boot on your Raspberry Pi." + else + body="Flashing stopped before it finished. Open ${title} for details." + fi + case "$(wor_host_platform)" in + macos) + #passed as argv, never pasted into the source: a device path or title must not be able to close + #a string literal and run as AppleScript + wor_osascript - "$title" "$body" >/dev/null 2>&1 <<'APPLESCRIPT' & +on run argv + display notification (item 2 of argv) with title (item 1 of argv) +end run +APPLESCRIPT + ;; + linux) + command -v notify-send >/dev/null && notify-send "$title" "$body" >/dev/null 2>&1 & + ;; + esac + return 0 +} diff --git a/src/lib/macos-disk-alerts.js b/src/lib/macos-disk-alerts.js new file mode 100644 index 0000000..dbf671f --- /dev/null +++ b/src/lib/macos-disk-alerts.js @@ -0,0 +1,165 @@ +"use strict"; + +function worDiskAlertText(value) { + return typeof value === "string" + ? value.replace(/\u2026/g, "...").replace(/\s+/g, " ").trim().toLowerCase() + : ""; +} + +function worDiskAlertWritePhase(progress) { + const lines = String(progress).split("\n"); + let step = ""; + let write = ""; + for (let index = lines.length - 1; index >= 0; index--) { + if (!step && lines[index].indexOf("STEP\t") === 0) step = lines[index]; + if (!write && lines[index].indexOf("DISK_WRITE\t") === 0) write = lines[index]; + if (step && write) break; + } + const fields = step.split("\t"); + const target = write.split("\t"); + return /^[0-9]+$/.test(fields[1]) && Number(fields[2]) === 8 && + Number(fields[1]) >= 5 && Number(fields[1]) <= 8 && + target[1] === "1" && /^\/dev\/disk[0-9]+$/.test(target[2]); +} + +function worDiskAlertDecision(progress, stopped, windows) { + if (stopped || !worDiskAlertWritePhase(progress)) return { action: "none" }; + const matches = []; + for (const window of windows) { + if (window.owner !== "UserNotificationCenter" && window.owner !== "DiskArbitrationAgent") continue; + if (!window.texts.some(function (text) { + return /^the disk you (attached|inserted) was not readable by this computer\.?$/.test(worDiskAlertText(text)); + })) continue; + const buttons = window.buttons; + const ignore = buttons.filter(function (button) { + return worDiskAlertText(button.name) === "ignore" && button.enabled === true; + }); + if (ignore.length !== 1 || !buttons.some(function (button) { + return worDiskAlertText(button.name) === "eject"; + }) || !buttons.some(function (button) { + return /^(initialize|initialize\.\.\.)$/.test(worDiskAlertText(button.name)); + })) continue; + matches.push({ owner: window.owner, window: window.reference, button: ignore[0].reference }); + } + if (matches.length > 1) return { action: "ambiguous" }; + if (matches.length === 1) return { action: "ignore", target: matches[0] }; + return { action: "none" }; +} + +function worDiskAlertTransientError(error) { + const code = Number(error.errorNumber || error.number); + const message = String(error.message || ""); + if (/not allowed|not authorized|assistive access|permission/i.test(message)) return false; + return code === -1728 || code === -600 || + (code === -1719 && /invalid index|can.t get|cannot get|not found/i.test(message)); +} + +function run(argv) { + if (argv.length !== 5 || !/^[1-9][0-9]*$/.test(argv[3])) { + throw new Error("Expected progress, completion, abort, installer PID, and status paths."); + } + ObjC.import("Foundation"); + ObjC.import("ApplicationServices"); + ObjC.bindFunction("kill", ["int", ["int", "int"]]); + const progressPath = argv[0]; + const donePath = argv[1]; + const abortPath = argv[2]; + const installerPid = Number(argv[3]); + const statusPath = argv[4]; + const files = $.NSFileManager.defaultManager; + let lastStatus = ""; + let lastState = ""; + let dismissed = 0; + + function readFile(path) { + if (!files.fileExistsAtPath($(path))) return ""; + const text = $.NSString.stringWithContentsOfFileEncodingError($(path), $.NSUTF8StringEncoding, Ref()); + if (text.isNil()) throw new Error("Could not read the disk-alert control file."); + return ObjC.unwrap(text); + } + + function stopped() { + return files.fileExistsAtPath($(donePath)) || + files.fileExistsAtPath($(abortPath)) || $.kill(installerPid, 0) !== 0; + } + + function report(state, message) { + const json = JSON.stringify({ state: state, message: message, dismissed: dismissed }); + if (json === lastStatus) return; + if (!$(json).writeToFileAtomicallyEncodingError($(statusPath), true, $.NSUTF8StringEncoding, Ref())) { + throw new Error("Could not write the disk-alert status."); + } + lastStatus = json; + lastState = state; + } + + function snapshot(owner, window) { + const texts = [window.name()]; + const buttons = []; + const elements = window.entireContents(); + if (elements.length > 100) return null; + for (const element of elements) { + const properties = element.properties(); + if (properties.role === "AXStaticText") { + texts.push(properties.value); + texts.push(properties.name); + } else if (properties.role === "AXButton") { + buttons.push({ name: properties.name, enabled: properties.enabled, reference: element }); + } + } + return { owner: owner, reference: window, texts: texts, buttons: buttons }; + } + + const systemEvents = Application("System Events"); + report("waiting", "Automatic Ignore is waiting for disk preparation."); + try { + while (!stopped()) { + const progress = readFile(progressPath); + if (worDiskAlertWritePhase(progress)) { + if (!$.AXIsProcessTrusted()) { + report("warning", "Automatic Ignore needs Accessibility permission for WoR-Flasher. Choose Ignore manually until it is allowed."); + } else { + try { + const windows = []; + for (const owner of ["UserNotificationCenter", "DiskArbitrationAgent"]) { + const processes = systemEvents.processes.whose({ name: owner })(); + for (const process of processes) { + for (const window of process.windows()) { + const entry = snapshot(owner, window); + if (entry) windows.push(entry); + } + } + } + const decision = worDiskAlertDecision(progress, stopped(), windows); + if (decision.action === "ambiguous") { + report("warning", "Multiple unreadable-disk alerts are open. Choose Ignore manually; no alert was selected automatically."); + } else if (decision.action === "ignore") { + const current = snapshot(decision.target.owner, decision.target.window); + const confirmed = worDiskAlertDecision(readFile(progressPath), stopped(), current ? [current] : []); + if (confirmed.action === "ignore" && worDiskAlertText(confirmed.target.button.name()) === "ignore") { + confirmed.target.button.click(); + dismissed++; + report("ignored", "Automatically chose Ignore for the macOS unreadable-disk alert."); + console.log("Automatically chose Ignore for the macOS unreadable-disk alert."); + } + } else if (dismissed === 0 || lastState === "warning") { + report("watching", "Automatic Ignore is watching for the macOS unreadable-disk alert."); + } + } catch (error) { + if (!worDiskAlertTransientError(error)) { + const message = "Automatic Ignore is unavailable. Allow WoR-Flasher in Privacy & Security > Accessibility and Automation, or choose Ignore manually."; + report("warning", message); + throw new Error(message); + } + } + } + } + $.NSThread.sleepForTimeInterval(0.4); + } + } catch (error) { + if (lastState !== "warning") { + report("warning", "Automatic Ignore stopped. Choose Ignore manually if the unreadable-disk alert appears."); + } + throw error; + } +} diff --git a/src/lib/metadata.sh b/src/lib/metadata.sh new file mode 100644 index 0000000..606c4ba --- /dev/null +++ b/src/lib/metadata.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +#Canonical product metadata and the shared macOS script-host helper. +#Every entry point loads this file, so nothing here may depend on another library. +WOR_METADATA_CONFIG="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/config/metadata.json" + +wor_metadata_value() { #Input: section and key. Output: scalar value from src/config/metadata.json. + awk -v section="$1" -v key="$2" ' + BEGIN { quote=sprintf("%c", 34) } + $0 ~ "^[[:space:]]*" quote section quote "[[:space:]]*:" { in_section=1; next } + in_section && $0 ~ /^[[:space:]]*}/ { exit } + in_section { + pattern="^[[:space:]]*" quote key quote "[[:space:]]*:[[:space:]]*" + if ($0 ~ pattern) { + line=$0 + sub(pattern, "", line) + sub(/,[[:space:]]*$/, "", line) + if (substr(line, 1, 1) == quote) { + line=substr(line, 2) + if (substr(line, length(line), 1) == quote) line=substr(line, 1, length(line) - 1) + } + print line + exit + } + } + ' "$WOR_METADATA_CONFIG" +} + +wor_metadata_required() { #Input: section, key, env var. Assigns a required scalar metadata value. + local value + value="$(wor_metadata_value "$1" "$2")" + if [ -z "$value" ];then + printf 'Required WoR-Flasher metadata %s.%s is missing from %s.\n' "$1" "$2" "$WOR_METADATA_CONFIG" >&2 + return 1 + fi + printf -v "$3" '%s' "$value" +} + +wor_metadata_required product name WOR_FLASHER_NAME || return 1 +wor_metadata_required product version WOR_FLASHER_VERSION || return 1 +wor_metadata_required product assetsDirname WOR_ASSETS_DIRNAME || return 1 +wor_metadata_required product logoFilename WOR_LOGO_FILENAME || return 1 +wor_metadata_required product iconFilename WOR_ICON_FILENAME || return 1 +wor_metadata_required product iconName WOR_ICON_NAME || return 1 +wor_metadata_required systemDefaults peInstallerUrl WOR_DEFAULT_PE_INSTALLER_URL || return 1 +wor_metadata_required systemDefaults peInstallerSha256 WOR_DEFAULT_PE_INSTALLER_SHA256 || return 1 +wor_metadata_required systemDefaults uefiShellPackageUrl WOR_DEFAULT_UEFI_SHELL_PACKAGE_URL || return 1 +wor_metadata_required systemDefaults uefiShellPackageSha256 WOR_DEFAULT_UEFI_SHELL_PACKAGE_SHA256 || return 1 +wor_metadata_required systemDefaults uefiVerPi3 WOR_DEFAULT_UEFI_VER_PI3 || return 1 +wor_metadata_required systemDefaults uefiVerPi4 WOR_DEFAULT_UEFI_VER_PI4 || return 1 +wor_metadata_required systemDefaults uefiVerPi5 WOR_DEFAULT_UEFI_VER_PI5 || return 1 +wor_metadata_required systemDefaults uefiRepoPi3 WOR_DEFAULT_UEFI_REPO_PI3 || return 1 +wor_metadata_required systemDefaults uefiRepoPi4 WOR_DEFAULT_UEFI_REPO_PI4 || return 1 +wor_metadata_required systemDefaults uefiRepoPi5 WOR_DEFAULT_UEFI_REPO_PI5 || return 1 +wor_metadata_required systemDefaults driverVer WOR_DEFAULT_DRIVER_VER || return 1 +wor_metadata_required systemDefaults driversRepo WOR_DEFAULT_DRIVERS_REPO || return 1 +wor_metadata_required systemDefaults armv80MaxBuild WOR_DEFAULT_ARMV80_MAX_BUILD || return 1 +wor_metadata_required systemDefaults win11MinBuild WOR_DEFAULT_WIN11_MIN_BUILD || return 1 +wor_metadata_required systemDefaults win10OldestBuild WOR_DEFAULT_WIN10_OLDEST_BUILD || return 1 +wor_metadata_required systemDefaults exampleBid WOR_DEFAULT_EXAMPLE_BID || return 1 +wor_metadata_required systemDefaults armv80SafeBid WOR_DEFAULT_ARMV80_SAFE_BID || return 1 +wor_metadata_required systemDefaults repoSlug WOR_DEFAULT_REPO_SLUG || return 1 +wor_metadata_required systemDefaults logDirname WOR_DEFAULT_LOG_DIRNAME || return 1 +: "${WOR_APP_TITLE:=$WOR_FLASHER_NAME}" +: "${WOR_WINDOW_TITLE:=$WOR_FLASHER_NAME v$WOR_FLASHER_VERSION}" +: "${WOR_RUN_ID:=$(date -u +%Y%m%dT%H%M%SZ)}" + +wor_osascript() { + local host_dir="${TMPDIR:-/tmp}/wor-flasher-script-host-${UID}" + local script_host="$host_dir/$WOR_FLASHER_NAME" + if [ -e "$host_dir" ];then + [ -d "$host_dir" ] && [ "$(stat -f '%u' "$host_dir" 2>/dev/null)" == "$UID" ] || return 1 + else + mkdir -m 700 "$host_dir" || return 1 + fi + chmod 700 "$host_dir" || return 1 + ln -sfn /usr/bin/osascript "$script_host" || return 1 + "$script_host" "$@" +} diff --git a/src/lib/node-runtime.mjs b/src/lib/node-runtime.mjs new file mode 100644 index 0000000..57e2d96 --- /dev/null +++ b/src/lib/node-runtime.mjs @@ -0,0 +1,293 @@ +import { createHash } from "node:crypto"; +import { + chmodSync, + copyFileSync, + existsSync, + lstatSync, + mkdirSync, + readFileSync, + readdirSync, + rmSync, +} from "node:fs"; +import { dirname, isAbsolute, join, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, "..", ".."); +const defaultMetadataFile = join(repoDir, "src", "config", "metadata.json"); + +export function parseVersion(versionStr) { + if (!versionStr || typeof versionStr !== "string") return [0, 0, 0]; + const cleaned = versionStr.replace(/^v/i, "").trim(); + const parts = cleaned.split(".").map((p) => parseInt(p, 10) || 0); + while (parts.length < 3) parts.push(0); + return parts.slice(0, 3); +} + +export function compareVersions(v1, v2) { + const p1 = parseVersion(v1); + const p2 = parseVersion(v2); + for (let i = 0; i < 3; i++) { + if (p1[i] > p2[i]) return 1; + if (p1[i] < p2[i]) return -1; + } + return 0; +} + +export function readProjectMetadata(metadataFile = defaultMetadataFile) { + if (!existsSync(metadataFile)) { + throw new Error(`Missing project metadata: ${metadataFile}`); + } + return JSON.parse(readFileSync(metadataFile, "utf8")); +} + +function isSafeRuntimePath(path) { + return typeof path === "string" && !isAbsolute(path) && + path.split(/[\\/]/).every((part) => part !== "" && part !== "." && part !== ".."); +} + +export function readRuntimePaths(metadataFile = defaultMetadataFile) { + const metadata = readProjectMetadata(metadataFile); + const runtimePaths = metadata.runtimePaths; + if (!Array.isArray(runtimePaths) || runtimePaths.length === 0) { + throw new Error("src/config/metadata.json must define runtimePaths"); + } + for (const relPath of runtimePaths) { + if (!isSafeRuntimePath(relPath)) { + throw new Error(`Invalid runtime path in package.json: ${relPath}`); + } + } + return runtimePaths; +} + +export function copyTree(source, target) { + const info = lstatSync(source); + if (info.isSymbolicLink() || lstatSync(target, { throwIfNoEntry: false })?.isSymbolicLink()) { + throw new Error(`Symbolic links are not allowed in runtime copies: ${source}`); + } + if (info.isDirectory()) { + mkdirSync(target, { recursive: true }); + for (const name of readdirSync(source)) { + copyTree(join(source, name), join(target, name)); + } + return; + } + mkdirSync(dirname(target), { recursive: true }); + copyFileSync(source, target); + chmodSync(target, info.mode); +} + +export function generateRuntimeManifest( + stageRoot, + version, + runtimePaths = readRuntimePaths(), + rootSource = repoDir, +) { + const files = []; + + for (const relPath of runtimePaths) { + if (!isSafeRuntimePath(relPath)) { + throw new Error(`Invalid runtime path: ${relPath}`); + } + const srcPath = join(rootSource, relPath); + if (!existsSync(srcPath)) { + throw new Error(`Missing required runtime path: ${relPath}`); + } + const destPath = join(stageRoot, relPath); + if (resolve(srcPath) !== resolve(destPath)) { + let ancestor = stageRoot; + for (const part of ["", ...relPath.split(/[\\/]/)]) { + ancestor = join(ancestor, part); + if (lstatSync(ancestor, { throwIfNoEntry: false })?.isSymbolicLink()) { + throw new Error(`Symbolic link in staging path: ${ancestor}`); + } + } + //Only replace the declared staging path; obsolete staged files must not enter a new manifest. + rmSync(destPath, { recursive: true, force: true }); + copyTree(srcPath, destPath); + } + } + + function processEntry(currentPath) { + const info = lstatSync(currentPath); + if (info.isSymbolicLink()) throw new Error(`Symbolic link in runtime: ${currentPath}`); + if (info.isDirectory()) { + for (const name of readdirSync(currentPath)) { + processEntry(join(currentPath, name)); + } + } else if (info.isFile()) { + const rel = relative(stageRoot, currentPath).replace(/\\/g, "/"); + const sha256 = createHash("sha256") + .update(readFileSync(currentPath)) + .digest("hex"); + const mode = (info.mode & 0o777).toString(8); + files.push({ path: rel, sha256, mode }); + } + } + + for (const relPath of runtimePaths) { + processEntry(join(stageRoot, relPath)); + } + + files.sort((a, b) => a.path.localeCompare(b.path)); + return { schemaVersion: 1, version, files }; +} + +export function formatRuntimeManifest(manifest) { + const files = manifest.files + .map( + (entry) => + ` {"path": ${JSON.stringify(entry.path)}, "sha256": "${entry.sha256}", "mode": "${entry.mode}"}`, + ) + .join(",\n"); + return `{ + "schemaVersion": ${manifest.schemaVersion}, + "version": ${JSON.stringify(manifest.version)}, + "files": [ +${files} + ] +} +`; +} + +export function verifyRuntimeManifest(appRoot, expectedVersion) { + const runtimeDir = join(appRoot, "Contents", "Resources", "runtime"); + const manifestFile = join( + appRoot, + "Contents", + "Resources", + "runtime-manifest.json", + ); + + if (!existsSync(runtimeDir) || !existsSync(manifestFile)) return false; + + try { + const manifest = JSON.parse(readFileSync(manifestFile, "utf8")); + if (!manifest || manifest.schemaVersion !== 1 || !Array.isArray(manifest.files) || manifest.files.length === 0) return false; + if (expectedVersion !== undefined && manifest.version !== expectedVersion) return false; + if (lstatSync(runtimeDir).isSymbolicLink() || lstatSync(manifestFile).isSymbolicLink()) return false; + const paths = new Set(); + + for (const entry of manifest.files) { + if (!entry || !isSafeRuntimePath(entry.path) || entry.path.includes("\\") || + paths.has(entry.path)) return false; + paths.add(entry.path); + const filePath = join(runtimeDir, entry.path); + if (!existsSync(filePath)) return false; + let currentPath = runtimeDir; + for (const part of entry.path.split("/")) { + currentPath = join(currentPath, part); + if (lstatSync(currentPath).isSymbolicLink()) return false; + } + const info = lstatSync(filePath); + if (!info.isFile()) return false; + const sha256 = createHash("sha256") + .update(readFileSync(filePath)) + .digest("hex"); + if (sha256 !== entry.sha256) return false; + const mode = (info.mode & 0o777).toString(8); + if (mode !== entry.mode) return false; + } + return true; + } catch { + return false; + } +} + +export function loadSystemConfig(repoPath = repoDir) { + const metadataPath = join(repoPath, "src", "config", "metadata.json"); + const configPath = join(repoPath, "config-templates", "config.json"); + let system = {}; + if (existsSync(metadataPath)) { + try { + const defaults = readProjectMetadata(metadataPath).systemDefaults || {}; + system = { + peInstallerUrl: defaults.peInstallerUrl, + peInstallerSha256: defaults.peInstallerSha256, + uefiVerPi3: defaults.uefiVerPi3, + uefiVerPi4: defaults.uefiVerPi4, + uefiVerPi5: defaults.uefiVerPi5, + driverVer: defaults.driverVer, + armv80MaxBuild: defaults.armv80MaxBuild, + win11MinBuild: defaults.win11MinBuild, + win10OldestBuild: defaults.win10OldestBuild, + exampleBid: defaults.exampleBid, + armv80SafeBid: defaults.armv80SafeBid, + repoSlug: defaults.repoSlug, + }; + system = Object.fromEntries( + Object.entries(system).filter(([, value]) => value !== undefined), + ); + } catch { + system = {}; + } + } + if (!existsSync(configPath)) return { system }; + try { + const config = JSON.parse(readFileSync(configPath, "utf8")); + return { ...config, system: { ...system, ...(config.system || {}) } }; + } catch { + return { system }; + } +} + +//The slug is interpolated into the update URL, so it must match the exact owner/name shape +//GitHub allows. Anything looser could point the check at an attacker-chosen host. +export function isSafeRepoSlug(slug) { + return ( + typeof slug === "string" && + /^[A-Za-z0-9._-]+\/[A-Za-z0-9._-]+$/.test(slug) && + !slug.split("/").includes("..") + ); +} + +//Read-only release check: it never writes, never executes anything, and never mutates the +//installation. The caller is told a newer release exists and decides what to do about it. +export async function checkReleaseUpdate({ + currentVersion, + repoSlug, + repoPath = repoDir, + timeoutMs = 5000, + fetchImpl = fetch, +} = {}) { + const systemConfig = loadSystemConfig(repoPath)?.system || {}; + const finalRepoSlug = repoSlug || systemConfig.repoSlug; + const version = + currentVersion || readProjectMetadata().product?.version || "0.0.0"; + + if (!isSafeRepoSlug(finalRepoSlug)) { + return { updateAvailable: false, error: "unsafe-repo-slug" }; + } + + try { + const response = await fetchImpl( + `https://api.github.com/repos/${finalRepoSlug}/releases/latest`, + { + headers: { + "User-Agent": "WoR-Flasher-Updater", + Accept: "application/vnd.github+json", + }, + redirect: "error", + signal: AbortSignal.timeout(timeoutMs), + }, + ); + if (!response.ok) return { updateAvailable: false, error: response.status }; + + const data = await response.json(); + if (data.draft || data.prerelease) { + return { updateAvailable: false, currentVersion: version }; + } + + const latestVersion = data.tag_name || data.name || ""; + const htmlUrl = data.html_url || ""; + + return { + updateAvailable: compareVersions(latestVersion, version) > 0, + currentVersion: version, + latestVersion, + htmlUrl: htmlUrl.startsWith("https://") ? htmlUrl : "", + }; + } catch (err) { + return { updateAvailable: false, error: err.message }; + } +} diff --git a/src/lib/paths.sh b/src/lib/paths.sh new file mode 100644 index 0000000..4bedac3 --- /dev/null +++ b/src/lib/paths.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +#Path resolution shared by every entry point. +#Resolves a path using whichever native tool the host provides, without changing the caller's directory. +resolve_path() { #Input: path. Output: absolute path. + [ -z "$1" ] && return 1 + if command -v realpath >/dev/null ;then + realpath "$1" && return 0 + fi + if readlink -f "$1" >/dev/null 2>&1 ;then + readlink -f "$1" && return 0 + fi + if [ -d "$1" ];then + (cd "$1" && pwd -P) + else + local dir + local base + dir="$(dirname "$1")" + base="$(basename "$1")" + printf '%s/%s\n' "$(cd "$dir" && pwd -P)" "$base" + fi +} diff --git a/src/macos-app/Contents/Info.plist b/src/macos-app/Contents/Info.plist new file mode 100644 index 0000000..e49f7d4 --- /dev/null +++ b/src/macos-app/Contents/Info.plist @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDisplayName</key> + <string>WoR-Flasher</string> + <key>CFBundleExecutable</key> + <string>WoR-Flasher</string> + <key>CFBundleIconFile</key> + <string>WoR-Flasher.icns</string> + <key>CFBundleIconName</key> + <string>WoR-Flasher</string> + <key>CFBundleIdentifier</key> + <string>com.botspot.wor-flasher</string> + <key>CFBundleName</key> + <string>WoR-Flasher</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>2.0.0</string> + <key>CFBundleVersion</key> + <string>2.0.0</string> + <key>LSMinimumSystemVersion</key> + <string>13.0</string> + <key>NSHighResolutionCapable</key> + <true/> +</dict> +</plist> diff --git a/src/macos-app/Contents/MacOS/WoR-Flasher b/src/macos-app/Contents/MacOS/WoR-Flasher new file mode 100755 index 0000000..a412850 --- /dev/null +++ b/src/macos-app/Contents/MacOS/WoR-Flasher @@ -0,0 +1,689 @@ +#!/bin/bash + +#Native macOS launcher for the WoR-Flasher GUI and engine. +#A source checkout uses its canonical files directly. A distributed app validates its immutable +#embedded runtime and any verified runtime installed under Application Support before execution. + +set -u + +LAUNCHER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +APP_CONTENTS="$(cd "$LAUNCHER_DIR/.." && pwd -P)" +APP_ROOT="$(cd "$APP_CONTENTS/.." && pwd -P)" +CHECKOUT_DIR="$(cd "$APP_ROOT/.." && pwd -P)" +INFO_PLIST="${WOR_APP_INFO_PLIST:-$APP_CONTENTS/Info.plist}" +RESOURCES_DIR="${WOR_APP_RESOURCES_DIR:-$APP_CONTENTS/Resources}" +EMBEDDED_RUNTIME="$RESOURCES_DIR/runtime" +EMBEDDED_MANIFEST="$RESOURCES_DIR/runtime-manifest.json" +REPO_DIR='' +GUI_SCRIPT='' +METADATA_FILE='' +DEPENDENCIES_FILE='' +RUNTIME_MANIFEST='' +SOURCE_CHECKOUT=0 +WOR_FLASHER_NAME="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleDisplayName' "$INFO_PLIST" 2>/dev/null || printf 'WoR-Flasher')" +#the shared metadata is not loadable until a runtime is verified, so derive the window title from the bundle +WOR_BUNDLE_VERSION="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$INFO_PLIST" 2>/dev/null || printf '')" +[ -n "$WOR_BUNDLE_VERSION" ] && WOR_WINDOW_TITLE="$WOR_FLASHER_NAME v$WOR_BUNDLE_VERSION" || WOR_WINDOW_TITLE="$WOR_FLASHER_NAME" +BUNDLE_ICON_FILENAME="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIconFile' "$INFO_PLIST" 2>/dev/null || printf 'WoR-Flasher.icns')" +#Application Support folder name matches the bundle's own display name, so a rebrand needs no launcher edit +APP_SUPPORT_DIR="${WOR_APP_SUPPORT_DIR:-$HOME/Library/Application Support/$WOR_FLASHER_NAME}" +RUNTIMES_DIR="$APP_SUPPORT_DIR/runtimes" +ACTIVE_RUNTIME_FILE="$APP_SUPPORT_DIR/active-runtime" +PREVIOUS_RUNTIME_FILE="$APP_SUPPORT_DIR/previous-runtime" +#src/config/metadata.json is the source of truth for license/repository branding; read before any +#runtime is selected, since the JXA startup window below cannot source src/lib/metadata.sh yet +EARLY_METADATA_FILE="$CHECKOUT_DIR/src/config/metadata.json" +if [ ! -d "$CHECKOUT_DIR/.git" ] || [ ! -x "$CHECKOUT_DIR/install-wor-gui.sh" ];then + EARLY_METADATA_FILE="$EMBEDDED_RUNTIME/src/config/metadata.json" +fi +WOR_PRODUCT_LICENSE="$(/usr/bin/plutil -extract product.license raw -o - "$EARLY_METADATA_FILE" 2>/dev/null)" +WOR_REPO_SLUG="$(/usr/bin/plutil -extract systemDefaults.repoSlug raw -o - "$EARLY_METADATA_FILE" 2>/dev/null)" +STARTUP_STATUS='' +STARTUP_DONE='' +STARTUP_PID='' + +set_runtime_paths() { #Input: runtime root and manifest. + REPO_DIR="$1" + RUNTIME_MANIFEST="$2" + GUI_SCRIPT="$REPO_DIR/install-wor-gui.sh" + METADATA_FILE="$REPO_DIR/src/lib/metadata.sh" + DEPENDENCIES_FILE="$REPO_DIR/src/lib/dependencies.sh" +} + +manifest_version() { #Input: manifest. Output: declared version. + sed -n 's/^[[:space:]]*"version": "\([^"]*\)",*[[:space:]]*$/\1/p' "$1" | head -n1 +} + +version_is_newer() { #Input: candidate and current numeric release versions. + local candidate="$1" + local current="$2" + local candidate_part current_part index + case "$candidate:$current" in + *[!0-9.:]*) return 1 ;; + esac + for index in 1 2 3 4 ;do + candidate_part="$(printf '%s' "$candidate" | cut -d. -f"$index")" + current_part="$(printf '%s' "$current" | cut -d. -f"$index")" + candidate_part="${candidate_part:-0}" + current_part="${current_part:-0}" + [ "$candidate_part" -gt "$current_part" ] && return 0 + [ "$candidate_part" -lt "$current_part" ] && return 1 + done + return 1 +} + +validate_runtime() { #Input: runtime root and manifest. Output: success for a complete verified runtime. + local runtime_root="$1" + local manifest="$2" + local line path expected_mode expected_digest actual_mode actual_digest count=0 + [ -d "$runtime_root" ] && [ -f "$manifest" ] || return 1 + [ -n "$(manifest_version "$manifest")" ] || return 1 + while IFS= read -r line ;do + path="$(printf '%s\n' "$line" | sed -n 's/^[[:space:]]*{"path": "\([^"]*\)", "sha256": "[^"]*", "mode": "[^"]*"},*[[:space:]]*$/\1/p')" + [ -n "$path" ] || continue + case "/$path/" in + /*/../*|/*/./*|//*|/*\\*) return 1 ;; + esac + expected_digest="$(printf '%s\n' "$line" | sed -n 's/.*"sha256": "\([0-9a-f]*\)".*/\1/p')" + expected_mode="$(printf '%s\n' "$line" | sed -n 's/.*"mode": "\([0-7]*\)".*/\1/p')" + [ -f "$runtime_root/$path" ] && [ ! -L "$runtime_root/$path" ] || return 1 + actual_digest="$(shasum -a 256 "$runtime_root/$path" | awk '{print $1}')" + if stat -f '%Lp' "$runtime_root/$path" >/dev/null 2>&1 ;then + actual_mode="$(stat -f '%Lp' "$runtime_root/$path")" + else + actual_mode="$(stat -Lc '%a' "$runtime_root/$path")" + fi + [ "$actual_digest" == "$expected_digest" ] && [ "$actual_mode" == "$expected_mode" ] || return 1 + count=$((count + 1)) + done < "$manifest" + [ "$count" -gt 0 ] \ + && [ -x "$runtime_root/install-wor.sh" ] \ + && [ -x "$runtime_root/install-wor-gui.sh" ] \ + && [ -r "$runtime_root/src/lib/metadata.sh" ] \ + && [ -r "$runtime_root/src/lib/dependencies.sh" ] +} + +runtime_from_pointer() { #Input: pointer file. Output: runtime and manifest separated by a tab. + local pointer="$1" + local version runtime manifest + [ -f "$pointer" ] || return 1 + IFS= read -r version < "$pointer" + case "$version" in + ''|*/*|*\\*|.|..) return 1 ;; + esac + runtime="$RUNTIMES_DIR/$version/runtime" + manifest="$RUNTIMES_DIR/$version/runtime-manifest.json" + validate_runtime "$runtime" "$manifest" || return 1 + printf '%s\t%s\n' "$runtime" "$manifest" +} + +select_runtime() { + local selected + if [ -d "$CHECKOUT_DIR/.git" ] && [ -x "$CHECKOUT_DIR/install-wor-gui.sh" ];then + SOURCE_CHECKOUT=1 + set_runtime_paths "$CHECKOUT_DIR" '' + return 0 + fi + selected="$(runtime_from_pointer "$ACTIVE_RUNTIME_FILE" 2>/dev/null)" \ + || selected="$(runtime_from_pointer "$PREVIOUS_RUNTIME_FILE" 2>/dev/null)" \ + || selected='' + if [ -n "$selected" ];then + set_runtime_paths "${selected%%$'\t'*}" "${selected#*$'\t'}" + return 0 + fi + validate_runtime "$EMBEDDED_RUNTIME" "$EMBEDDED_MANIFEST" || return 1 + set_runtime_paths "$EMBEDDED_RUNTIME" "$EMBEDDED_MANIFEST" +} + +write_runtime_pointer() { #Input: pointer path and version. + local pointer="$1" + local version="$2" + local temporary + mkdir -p "$APP_SUPPORT_DIR" + temporary="$(mktemp "$APP_SUPPORT_DIR/.runtime-pointer.XXXXXX")" || return 1 + printf '%s\n' "$version" > "$temporary" + mv -f "$temporary" "$pointer" +} + +activate_runtime() { #Input: version. Preserve the formerly active version for rollback. + local version="$1" + local active='' + if [ -f "$ACTIVE_RUNTIME_FILE" ];then + IFS= read -r active < "$ACTIVE_RUNTIME_FILE" + fi + if [ -n "$active" ] && [ "$active" != "$version" ];then + write_runtime_pointer "$PREVIOUS_RUNTIME_FILE" "$active" || return 1 + fi + write_runtime_pointer "$ACTIVE_RUNTIME_FILE" "$version" +} + +install_staged_runtime() { #Input: staged version directory. Validate and promote atomically. + local staged="$1" + local version target + version="$(manifest_version "$staged/runtime-manifest.json")" + case "$version" in + ''|*/*|*\\*|.|..) return 1 ;; + esac + validate_runtime "$staged/runtime" "$staged/runtime-manifest.json" || return 1 + target="$RUNTIMES_DIR/$version" + if [ -e "$target" ];then + if cmp -s "$target/runtime-manifest.json" "$staged/runtime-manifest.json" \ + && validate_runtime "$target/runtime" "$target/runtime-manifest.json" ;then + #an identical, intact runtime is already installed, so keep it and discard the staged copy + rm -rf "$staged" + else + #the installed copy is a different version or has been corrupted; the staged copy was + #validated above, so replacing it repairs the install instead of stranding a bad runtime + rm -rf "$target" || return 1 + mv "$staged" "$target" || return 1 + fi + else + mv "$staged" "$target" || return 1 + fi + activate_runtime "$version" +} + +bootstrap_embedded_runtime() { + local version target staged selected + version="$(manifest_version "$EMBEDDED_MANIFEST")" + target="$RUNTIMES_DIR/$version" + if cmp -s "$target/runtime-manifest.json" "$EMBEDDED_MANIFEST" 2>/dev/null \ + && validate_runtime "$target/runtime" "$target/runtime-manifest.json" 2>/dev/null ;then + selected="$(runtime_from_pointer "$ACTIVE_RUNTIME_FILE" 2>/dev/null)" || selected='' + [ -n "$selected" ] || activate_runtime "$version" + return + fi + mkdir -p "$RUNTIMES_DIR" + staged="$(mktemp -d "$RUNTIMES_DIR/.install.XXXXXX")" || return 1 + cp -R "$EMBEDDED_RUNTIME" "$staged/runtime" \ + && cp "$EMBEDDED_MANIFEST" "$staged/runtime-manifest.json" \ + && install_staged_runtime "$staged" \ + || { rm -rf "$staged"; return 1; } +} + +metadata_value() { #Input: JSON metadata path and key. Output: scalar value. + /usr/bin/plutil -extract "$2" raw -o - "$1" 2>/dev/null +} + +archive_entries_are_safe() { #Input: tar.gz archive. Reject links and paths escaping extraction. + local archive="$1" + local entry + tar -tzf "$archive" >/dev/null 2>&1 || return 1 + while IFS= read -r entry ;do + case "$entry" in + ''|/*|..|../*|*/../*|*\\*) return 1 ;; + esac + done < <(tar -tzf "$archive") + tar -tvzf "$archive" | awk 'substr($1, 1, 1) != "-" && substr($1, 1, 1) != "d" { exit 1 }' +} + +check_for_runtime_update() { + #systemDefaults.repoSlug in src/config/metadata.json is the source of truth for the update source + local metadata_url="${WOR_UPDATE_METADATA_URL:-https://github.com/${WOR_DEFAULT_REPO_SLUG:-blackoutsecure/wor-flasher}/releases/latest/download/macos-runtime.json}" + local curl_bin="${WOR_CURL_BIN:-/usr/bin/curl}" + local work_dir metadata archive version archive_url expected_digest actual_digest staged current_version + case "$metadata_url" in + https://*) ;; + *) [ "${WOR_ALLOW_INSECURE_UPDATE:-0}" == 1 ] || return 0 ;; + esac + work_dir="$(mktemp -d "${TMPDIR:-/tmp}/wor-flasher-runtime-update.XXXXXX")" || return 0 + metadata="$work_dir/metadata.json" + archive="$work_dir/runtime.tar.gz" + if ! "$curl_bin" --fail --location --silent --max-time 15 "$metadata_url" -o "$metadata" ;then + rm -rf "$work_dir" + return 0 + fi + version="$(metadata_value "$metadata" version)" || { rm -rf "$work_dir"; return 0; } + archive_url="$(metadata_value "$metadata" url)" || { rm -rf "$work_dir"; return 0; } + expected_digest="$(metadata_value "$metadata" sha256)" || { rm -rf "$work_dir"; return 0; } + current_version="$(manifest_version "$RUNTIME_MANIFEST")" + version_is_newer "$version" "$current_version" || { rm -rf "$work_dir"; return 0; } + case "$version" in ''|*/*|*\\*|.|..) rm -rf "$work_dir"; return 0 ;; esac + case "$archive_url" in + https://*) ;; + *) [ "${WOR_ALLOW_INSECURE_UPDATE:-0}" == 1 ] || { rm -rf "$work_dir"; return 0; } ;; + esac + printf '%s' "$expected_digest" | grep -Eq '^[0-9a-f]{64}$' || { rm -rf "$work_dir"; return 0; } + ask_to_continue "WoR-Flasher $version is available as a verified runtime update." 'Update Now' \ + || { rm -rf "$work_dir"; return 0; } + if ! "$curl_bin" --fail --location --silent --max-time 120 "$archive_url" -o "$archive" ;then + rm -rf "$work_dir" + return 0 + fi + actual_digest="$(shasum -a 256 "$archive" | awk '{print $1}')" + [ "$actual_digest" == "$expected_digest" ] && archive_entries_are_safe "$archive" \ + || { rm -rf "$work_dir"; return 0; } + mkdir -p "$RUNTIMES_DIR" + staged="$(mktemp -d "$RUNTIMES_DIR/.install.XXXXXX")" || { rm -rf "$work_dir"; return 0; } + if tar -xzf "$archive" -C "$staged" \ + && [ "$(manifest_version "$staged/runtime-manifest.json")" == "$version" ] \ + && install_staged_runtime "$staged" ;then + rm -rf "$work_dir" + return 0 + fi + rm -rf "$staged" "$work_dir" + return 0 +} + +start_startup_window() { + local icon_path="$RESOURCES_DIR/$BUNDLE_ICON_FILENAME" + rm -f "$STARTUP_DONE" + #this window opens before any runtime is validated, so it cannot load the shared window library from + #src/lib/metadata.sh; keep its chrome in step with worMakeWindow by hand instead + /usr/bin/osascript -l JavaScript - "$STARTUP_STATUS" "$STARTUP_DONE" "$WOR_FLASHER_NAME" "$icon_path" "$WOR_WINDOW_TITLE" "$WOR_PRODUCT_LICENSE" "$WOR_REPO_SLUG" <<'JXA' >/dev/null 2>&1 & +ObjC.import('AppKit') +ObjC.import('Foundation') +const args = $.NSProcessInfo.processInfo.arguments +const statusPath = ObjC.unwrap(args.objectAtIndex(4)) +const donePath = ObjC.unwrap(args.objectAtIndex(5)) +const appTitle = ObjC.unwrap(args.objectAtIndex(6)) +const iconPath = ObjC.unwrap(args.objectAtIndex(7)) +const windowTitle = ObjC.unwrap(args.objectAtIndex(8) || appTitle) +const licenseText = ObjC.unwrap(args.objectAtIndex(9)) || 'GPL-3.0-or-later' +const repoSlugText = ObjC.unwrap(args.objectAtIndex(10)) || 'blackoutsecure/wor-flasher' +const fm = $.NSFileManager.defaultManager +$.NSProcessInfo.processInfo.processName = appTitle +const app = $.NSApplication.sharedApplication +app.setActivationPolicy($.NSApplicationActivationPolicyRegular) +app.setApplicationIconImage($.NSImage.alloc.initWithContentsOfFile($(iconPath))) + +let aboutWindow = null +let aboutController = null + +function appVersion() { + const prefix = appTitle + ' v' + if (windowTitle.indexOf(prefix) === 0) return windowTitle.substring(prefix.length) + return '' +} + +function showAboutWindow() { + if (aboutWindow) { + aboutWindow.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) + return + } + const width = 500 + const height = 300 + const style = $.NSWindowStyleMaskTitled | $.NSWindowStyleMaskClosable + aboutWindow = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer($.NSMakeRect(0, 0, width, height), style, $.NSBackingStoreBuffered, false) + aboutWindow.title = 'About ' + appTitle + aboutWindow.center + const zoomButton = aboutWindow.standardWindowButton($.NSWindowZoomButton) + if (zoomButton) zoomButton.hidden = true + const miniButton = aboutWindow.standardWindowButton($.NSWindowMiniaturizeButton) + if (miniButton) miniButton.hidden = true + const content = $.NSView.alloc.initWithFrame($.NSMakeRect(0, 0, width, height)) + aboutWindow.contentView = content + + const imageView = $.NSImageView.alloc.initWithFrame($.NSMakeRect(28, 182, 72, 72)) + imageView.image = $.NSImage.alloc.initWithContentsOfFile($(iconPath)) + imageView.imageScaling = $.NSImageScaleProportionallyUpOrDown + content.addSubview(imageView) + + const title = $.NSTextField.labelWithString(appTitle) + title.frame = $.NSMakeRect(116, 226, width - 144, 30) + title.font = $.NSFont.systemFontOfSizeWeight(22, $.NSFontWeightBold) + content.addSubview(title) + + const subtitle = $.NSTextField.labelWithString(appVersion().length > 0 ? 'Version ' + appVersion() : 'Version unavailable') + subtitle.frame = $.NSMakeRect(116, 202, width - 144, 22) + subtitle.font = $.NSFont.systemFontOfSizeWeight(13, $.NSFontWeightMedium) + subtitle.textColor = $.NSColor.secondaryLabelColor + content.addSubview(subtitle) + + const body = $.NSTextField.labelWithString('Create bootable Windows 10/11 ARM64 media for Raspberry Pi from Linux or macOS.\n\nMaintainers: Botspot + Blackout Secure\nLicense: ' + licenseText + '\nRepository: github.com/' + repoSlugText + '\nRuntime: validated standalone macOS launcher') + body.frame = $.NSMakeRect(28, 70, width - 56, 112) + body.font = $.NSFont.systemFontOfSize(12) + body.setUsesSingleLineMode(false) + body.cell.setWraps(true) + body.cell.setScrollable(false) + content.addSubview(body) + + const okButton = $.NSButton.buttonWithTitleTargetAction('OK', aboutController, 'closeAbout:') + okButton.bezelStyle = $.NSBezelStyleRounded + okButton.keyEquivalent = '\r' + okButton.sizeToFit + const okWidth = Math.max(96, okButton.frame.size.width) + okButton.frame = $.NSMakeRect(width - 28 - okWidth, 24, okWidth, 32) + content.addSubview(okButton) + + aboutWindow.makeKeyAndOrderFront(null) + app.activateIgnoringOtherApps(true) +} + +const AboutController = ObjC.registerSubclass({ + name: 'WorLauncherAboutController', + superclass: 'NSObject', + methods: { + 'showAbout:': { + types: ['void', ['id']], + implementation: function() { showAboutWindow() } + }, + 'closeAbout:': { + types: ['void', ['id']], + implementation: function() { + if (aboutWindow) { + aboutWindow.close + aboutWindow = null + } + } + } + } +}) +aboutController = $.WorLauncherAboutController.alloc.init + +const mainMenu = $.NSMenu.alloc.initWithTitle(appTitle) +const appMenuItem = $.NSMenuItem.alloc.init +appMenuItem.title = appTitle +const appMenu = $.NSMenu.alloc.initWithTitle(appTitle) +const aboutItem = appMenu.addItemWithTitleActionKeyEquivalent('About ' + appTitle, 'showAbout:', '') +aboutItem.target = aboutController +//AppKit auto-validates menu actions against the responder chain, which never finds a JXA-only target +appMenu.autoenablesItems = false +aboutItem.enabled = true +//leave target nil so these resolve through the standard first-responder chain to NSApp, the same +//way they always have; only autoenablesItems needed the explicit override, for the custom About action +const hideItem = appMenu.addItemWithTitleActionKeyEquivalent('Hide ' + appTitle, 'hide:', 'h') +hideItem.enabled = true +const hideOthersItem = appMenu.addItemWithTitleActionKeyEquivalent('Hide Others', 'hideOtherApplications:', 'h') +hideOthersItem.enabled = true +const showAllItem = appMenu.addItemWithTitleActionKeyEquivalent('Show All', 'unhideAllApplications:', '') +showAllItem.enabled = true +appMenu.addItem($.NSMenuItem.separatorItem) +const quitItem = appMenu.addItemWithTitleActionKeyEquivalent('Quit ' + appTitle, 'terminate:', 'q') +quitItem.enabled = true +appMenuItem.submenu = appMenu +mainMenu.addItem(appMenuItem) +app.mainMenu = mainMenu + +//the close box quits outright, the same as the app menu's Quit item, rather than merely hiding +//this window while the verification it reports on kept running unattended +const window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer( + $.NSMakeRect(0, 0, 440, 150), + $.NSWindowStyleMaskTitled | $.NSWindowStyleMaskMiniaturizable | $.NSWindowStyleMaskClosable, + $.NSBackingStoreBuffered, + false, +) +window.title = windowTitle +window.center +const zoomButton = window.standardWindowButton($.NSWindowZoomButton) +if (zoomButton) zoomButton.hidden = true + +const heading = $.NSTextField.labelWithString('Preparing WoR-Flasher') +heading.font = $.NSFont.boldSystemFontOfSize(17) +heading.frame = $.NSMakeRect(24, 98, 392, 24) +window.contentView.addSubview(heading) + +const detail = $.NSTextField.labelWithString('Starting...') +detail.frame = $.NSMakeRect(24, 63, 392, 22) +detail.lineBreakMode = $.NSLineBreakByTruncatingTail +window.contentView.addSubview(detail) + +const bar = $.NSProgressIndicator.alloc.initWithFrame($.NSMakeRect(24, 31, 392, 16)) +bar.indeterminate = true +bar.style = $.NSProgressIndicatorStyleBar +bar.startAnimation(null) +window.contentView.addSubview(bar) + +function readStatus() { + try { + const value = $.NSString.stringWithContentsOfFileEncodingError( + $(statusPath), + $.NSUTF8StringEncoding, + undefined, + ) + return ObjC.unwrap(value) || 'Preparing...' + } catch (error) { + return 'Preparing...' + } +} + +ObjC.registerSubclass({ + name: 'WorStartupController', + superclass: 'NSObject', + methods: { + 'tick:': { + types: ['void', ['id']], + implementation: function () { + if (fm.fileExistsAtPath($(donePath))) { + app.terminate(null) + return + } + detail.stringValue = readStatus() + }, + }, + //without this, the close box would hide the window while verification kept running unattended + 'windowShouldClose:': { + types: ['bool', ['id']], + implementation: function () { + app.terminate(null) + return true + }, + }, + }, +}) +const controller = $.WorStartupController.alloc.init +window.setDelegate(controller) +const timer = $.NSTimer.scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeats( + 0.15, + controller, + 'tick:', + null, + true, +) +window.makeKeyAndOrderFront(null) +app.activateIgnoringOtherApps(true) +app.run +timer.invalidate +JXA + STARTUP_PID=$! +} + +startup_phase() { #Input: status shown in the startup window. + printf '%s\n' "$1" > "$STARTUP_STATUS" +} + +stop_startup_window() { + touch "$STARTUP_DONE" + if [ -n "$STARTUP_PID" ];then + wait "$STARTUP_PID" 2>/dev/null || true + fi + STARTUP_PID='' +} + +cleanup_startup() { + stop_startup_window + rm -f "$STARTUP_STATUS" "$STARTUP_DONE" +} + +load_metadata() { + #shellcheck source=../../../src/lib/metadata.sh + source "$METADATA_FILE" + #shellcheck source=../../../src/lib/dependencies.sh + source "$DEPENDENCIES_FILE" +} + +run_apple_script() { + if declare -F wor_osascript >/dev/null ;then + wor_osascript "$@" + else + /usr/bin/osascript "$@" + fi +} + +sync_bundle_metadata() { + local key value current logo_source logo_resource icon_resource iconset_root iconset icon_size icon_png + logo_source="$REPO_DIR/$WOR_ASSETS_DIRNAME/$WOR_LOGO_FILENAME" + logo_resource="$RESOURCES_DIR/$WOR_LOGO_FILENAME" + icon_resource="$RESOURCES_DIR/$WOR_ICON_FILENAME" + [ -f "$logo_source" ] || return 1 + mkdir -p "$RESOURCES_DIR" || return 1 + if [ ! -f "$logo_resource" ] || ! cmp -s "$logo_source" "$logo_resource" ;then + cp "$logo_source" "$logo_resource" || return 1 + fi + if [ ! -f "$icon_resource" ] || [ "$logo_source" -nt "$icon_resource" ];then + iconset_root="$(mktemp -d "${TMPDIR:-/tmp}/wor-flasher-iconset.XXXXXX")" || return 1 + iconset="$iconset_root/wor-flasher.iconset" + mkdir "$iconset" || return 1 + trap 'rm -rf "$iconset_root"' RETURN + sips --resampleHeight 440 "$logo_source" --out "$iconset/source.png" >/dev/null || return 1 + sips --padToHeightWidth 512 512 --padColor 000000 "$iconset/source.png" --out "$iconset/icon_512x512.png" >/dev/null || return 1 + rm -f "$iconset/source.png" + for icon_size in 16 32 128 256 512 ;do + icon_png="$iconset/icon_${icon_size}x${icon_size}.png" + sips --resampleHeightWidth "$icon_size" "$icon_size" "$iconset/icon_512x512.png" --out "$icon_png" >/dev/null || return 1 + if [ "$icon_size" != 512 ];then + local retina_size=$((icon_size * 2)) + sips --resampleHeightWidth "$retina_size" "$retina_size" "$iconset/icon_512x512.png" --out "$iconset/icon_${icon_size}x${icon_size}@2x.png" >/dev/null || return 1 + fi + done + sips --resampleHeightWidth 1024 1024 "$iconset/icon_512x512.png" --out "$iconset/icon_512x512@2x.png" >/dev/null || return 1 + iconutil --convert icns --output "$icon_resource" "$iconset" || return 1 + rm -rf "$iconset_root" + trap - RETURN + fi + while IFS=$'\t' read -r key value ;do + current="$(/usr/libexec/PlistBuddy -c "Print :$key" "$INFO_PLIST" 2>/dev/null)" + if [ -z "$current" ];then + /usr/libexec/PlistBuddy -c "Add :$key string $value" "$INFO_PLIST" || return 1 + elif [ "$current" != "$value" ];then + /usr/libexec/PlistBuddy -c "Set :$key $value" "$INFO_PLIST" || return 1 + fi + done <<EOF +CFBundleDisplayName $WOR_FLASHER_NAME +CFBundleExecutable $WOR_FLASHER_NAME +CFBundleIconFile $WOR_ICON_FILENAME +CFBundleIconName $WOR_ICON_NAME +CFBundleName $WOR_FLASHER_NAME +CFBundleShortVersionString $WOR_FLASHER_VERSION +CFBundleVersion $WOR_FLASHER_VERSION +EOF +} + +show_error() { #Input: message. + run_apple_script - "$WOR_FLASHER_NAME" "$1" <<'APPLESCRIPT' >/dev/null +on run argv + display alert (item 1 of argv) message (item 2 of argv) as critical buttons {"Quit"} default button "Quit" +end run +APPLESCRIPT +} + +ask_to_continue() { #Input: message, action label. Exit 0 when accepted. + local result + result="$(run_apple_script - "$WOR_FLASHER_NAME" "$1" "$2" <<'APPLESCRIPT' 2>/dev/null +on run argv + set dialogResult to display dialog (item 2 of argv) with title (item 1 of argv) buttons {"Quit", item 3 of argv} default button (item 3 of argv) cancel button "Quit" with icon caution + return button returned of dialogResult +end run +APPLESCRIPT +)" || return 1 + [ "$result" == "$2" ] +} + +repair_missing_files() { + local required_path + local required_paths=() + local missing=() + if git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 ;then + while IFS= read -r required_path ;do + [ -n "$required_path" ] && required_paths+=("$required_path") + done < <(git -C "$REPO_DIR" ls-tree -r --name-only HEAD -- \ + install-wor.sh install-wor-gui.sh install-wor-hook.sh src/lib src/config src/updater.mjs \ + config-templates assets) + else + required_paths=(install-wor.sh install-wor-gui.sh src/lib/metadata.sh src/lib/dependencies.sh \ + src/lib/paths.sh src/lib/cleanup.sh src/lib/gui.sh config-templates assets/logo-full.png \ + assets/partnership.png assets/next-steps.png) + fi + for required_path in "${required_paths[@]}" ;do + [ -e "$REPO_DIR/$required_path" ] || missing+=("$required_path") + done + [ "${#missing[@]}" -gt 0 ] || return 0 + + git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 \ + || { show_error $'Required WoR-Flasher files are missing and this is not a Git checkout:\n\n'"${missing[*]}"; return 1; } + for required_path in "${missing[@]}" ;do + git -C "$REPO_DIR" cat-file -e "HEAD:$required_path" 2>/dev/null \ + || { show_error $'Required file cannot be repaired from the local checkout:\n\n'"$required_path"; return 1; } + done + ask_to_continue $'WoR-Flasher is missing required files:\n\n'"${missing[*]}"$'\n\nRestore only these files from the local Git revision?' Repair \ + || return 1 + git -C "$REPO_DIR" restore --source=HEAD -- "${missing[@]}" \ + || { show_error 'WoR-Flasher could not restore its missing files.'; return 1; } +} + +prepare_dependencies() { + export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" + if ! command -v brew >/dev/null ;then + ask_to_continue 'WoR-Flasher needs Homebrew to install its command-line dependencies. Open the official Homebrew website?' 'Open Homebrew' \ + && /usr/bin/open 'https://brew.sh' + return 1 + fi + + local formula installed_formulae + local missing=() + installed_formulae="$(brew list --formula 2>/dev/null)" || installed_formulae='' + for formula in "${WOR_MACOS_BREW_FORMULAE[@]}" ;do + grep -Fxq "$formula" <<<"$installed_formulae" || missing+=("$formula") + done + [ "${#missing[@]}" -gt 0 ] || return 0 + + ask_to_continue $'WoR-Flasher needs these Homebrew formulae:\n\n'"${missing[*]}"$'\n\nInstall only the missing dependencies now?' Install \ + || return 1 + export HOMEBREW_NO_AUTO_UPDATE=1 + local install_log="${TMPDIR:-/tmp}/wor-flasher-brew-install.log" + if ! brew install "${missing[@]}" >"$install_log" 2>&1 ;then + show_error "Homebrew could not install every dependency.\n\nLog: $install_log" + return 1 + fi + rm -f "$install_log" +} + +launcher_main() { + STARTUP_STATUS="$(mktemp "${TMPDIR:-/tmp}/wor-flasher-startup.XXXXXX")" || exit 1 + STARTUP_DONE="${STARTUP_STATUS}.done" + trap cleanup_startup EXIT INT TERM + startup_phase 'Checking for updates and repairs...' + start_startup_window + + #Classify the source tree before branching. select_runtime also detects it, but calling that only + #inside the packaged branch made the local missing-file repair unreachable during normal startup. + if [ -d "$CHECKOUT_DIR/.git" ];then + SOURCE_CHECKOUT=1 + set_runtime_paths "$CHECKOUT_DIR" '' + fi + if [ "$SOURCE_CHECKOUT" == 1 ];then + repair_missing_files || exit 1 + load_metadata || { show_error 'WoR-Flasher metadata could not be loaded after repair.'; exit 1; } + startup_phase 'Synchronizing application resources...' + sync_bundle_metadata || { show_error 'The updated macOS app metadata could not be synchronized.'; exit 1; } + else + startup_phase 'Preparing the verified runtime...' + bootstrap_embedded_runtime || { show_error 'The embedded WoR-Flasher runtime could not be installed safely.'; exit 1; } + select_runtime || { show_error 'The installed WoR-Flasher runtime could not be selected.'; exit 1; } + load_metadata || { show_error 'Verified WoR-Flasher metadata could not be loaded.'; exit 1; } + startup_phase 'Checking for updates and repairs...' + check_for_runtime_update + select_runtime || { show_error 'No verified WoR-Flasher runtime remains available.'; exit 1; } + load_metadata || { show_error 'Verified WoR-Flasher metadata could not be loaded.'; exit 1; } + fi + startup_phase 'Checking required tools...' + prepare_dependencies || exit 1 + + [ -x "$GUI_SCRIPT" ] || { show_error $'The GUI is not executable:\n\n'"$GUI_SCRIPT"; exit 1; } + startup_phase 'Opening the setup assistant...' + stop_startup_window + export NO_UPDATE=1 + export HOMEBREW_NO_AUTO_UPDATE=1 + export WOR_NATIVE_APP=1 + export WOR_APP_BUNDLE_PATH="$APP_ROOT" + export WOR_CACHE_DIR="$APP_SUPPORT_DIR/cache" + export WOR_ICON_PATH="$RESOURCES_DIR/$WOR_ICON_FILENAME" + trap - EXIT INT TERM + rm -f "$STARTUP_STATUS" "$STARTUP_DONE" + exec "$GUI_SCRIPT" +} + +if [ "${WOR_LAUNCHER_SOURCE_ONLY:-0}" != 1 ];then + launcher_main +fi diff --git a/src/macos-app/Contents/Resources/WoR-Flasher.icns b/src/macos-app/Contents/Resources/WoR-Flasher.icns new file mode 100644 index 0000000..ecea57e Binary files /dev/null and b/src/macos-app/Contents/Resources/WoR-Flasher.icns differ diff --git a/src/macos-disk-claim.c b/src/macos-disk-claim.c new file mode 100644 index 0000000..98f5277 --- /dev/null +++ b/src/macos-disk-claim.c @@ -0,0 +1,117 @@ +#include <CoreFoundation/CoreFoundation.h> +#include <DiskArbitration/DiskArbitration.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +static volatile sig_atomic_t stopped; +static bool claim_finished; +static bool claimed; + +static void stop_requested(int signal_number) { + (void)signal_number; + stopped = 1; +} + +static void claim_completed(DADiskRef disk, DADissenterRef dissenter, void *context) { + (void)disk; + (void)context; + claimed = dissenter == NULL; + claim_finished = true; + if (dissenter != NULL) { + fprintf(stderr, "Disk claim failed: 0x%x\n", DADissenterGetStatus(dissenter)); + } +} + +static DADissenterRef release_requested(DADiskRef disk, void *context) { + (void)disk; + (void)context; + return DADissenterCreate(kCFAllocatorDefault, kDAReturnBusy, + CFSTR("WoR-Flasher is preparing this disk.")); +} + +static bool input_closed(void) { + char buffer[64]; + ssize_t count = read(STDIN_FILENO, buffer, sizeof(buffer)); + return count == 0 || (count < 0 && errno != EAGAIN && errno != EINTR); +} + +static bool whole_disk_path(const char *path) { + const char *prefix = "/dev/disk"; + if (strncmp(path, prefix, strlen(prefix)) != 0) return false; + const char *suffix = path + strlen(prefix); + if (*suffix == '\0') return false; + for (; *suffix != '\0'; suffix++) { + if (!isdigit((unsigned char)*suffix)) return false; + } + return true; +} + +int main(int argc, char **argv) { + if (argc != 2 || !whole_disk_path(argv[1])) { + fprintf(stderr, "Usage: macos-disk-claim /dev/diskN < control-pipe\n"); + return 2; + } + if (isatty(STDIN_FILENO)) { + fprintf(stderr, "A control pipe is required; interactive disk claims are refused.\n"); + return 2; + } + int input_flags = fcntl(STDIN_FILENO, F_GETFL); + if (input_flags < 0 || fcntl(STDIN_FILENO, F_SETFL, input_flags | O_NONBLOCK) < 0) { + perror("control pipe"); + return 1; + } + if (input_closed()) return 0; + signal(SIGINT, stop_requested); + signal(SIGTERM, stop_requested); + signal(SIGHUP, stop_requested); + signal(SIGPIPE, SIG_IGN); + + DASessionRef session = DASessionCreate(kCFAllocatorDefault); + if (session == NULL) return 1; + DADiskRef disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, argv[1]); + CFDictionaryRef description = disk == NULL ? NULL : DADiskCopyDescription(disk); + CFTypeRef protocol = description == NULL ? NULL : + CFDictionaryGetValue(description, kDADiskDescriptionDeviceProtocolKey); + CFTypeRef model = description == NULL ? NULL : + CFDictionaryGetValue(description, kDADiskDescriptionDeviceModelKey); + bool disk_image = protocol != NULL && model != NULL && + CFEqual(protocol, CFSTR("Virtual Interface")) && CFEqual(model, CFSTR("Disk Image")); + bool eligible = description != NULL && + CFDictionaryGetValue(description, kDADiskDescriptionMediaWholeKey) == kCFBooleanTrue && + (disk_image || CFDictionaryGetValue(description, kDADiskDescriptionDeviceInternalKey) == kCFBooleanFalse) && + CFDictionaryGetValue(description, kDADiskDescriptionMediaWritableKey) == kCFBooleanTrue; + if (description != NULL) CFRelease(description); + if (!eligible) { + fprintf(stderr, "Refusing to claim a disk that is not external, writable, and whole.\n"); + if (disk != NULL) CFRelease(disk); + CFRelease(session); + return 1; + } + + DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + DADiskClaim(disk, kDADiskClaimOptionDefault, release_requested, NULL, claim_completed, NULL); + CFAbsoluteTime deadline = CFAbsoluteTimeGetCurrent() + 10; + while (!stopped && !claim_finished && CFAbsoluteTimeGetCurrent() < deadline) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true); + if (input_closed()) stopped = 1; + } + if (claimed && !stopped) { + if (printf("READY\n") < 0 || fflush(stdout) != 0) stopped = 1; + while (!stopped) { + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.1, true); + if (input_closed()) stopped = 1; + } + } + if (!claim_finished) fprintf(stderr, "Disk claim canceled or timed out.\n"); + if (claimed) DADiskUnclaim(disk); + DASessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); + CFRelease(disk); + CFRelease(session); + return claimed ? 0 : 1; +} diff --git a/src/package-macos-app.mjs b/src/package-macos-app.mjs new file mode 100644 index 0000000..544019d --- /dev/null +++ b/src/package-macos-app.mjs @@ -0,0 +1,75 @@ +#!/usr/bin/env node +import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + formatRuntimeManifest, + generateRuntimeManifest, + verifyRuntimeManifest, + readRuntimePaths, + readProjectMetadata, +} from "./lib/node-runtime.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, ".."); +const appRoot = + process.env.WOR_MACOS_APP_ROOT || + join(repoDir, "release", "macos", "WoR-Flasher.app"); +const mode = process.argv[2] || "--check"; + +function fail(msg) { + console.error(msg); + process.exit(1); +} + +if (mode !== "--check" && mode !== "--write") { + console.error("Usage: node src/package-macos-app.mjs [--check|--write]"); + process.exit(2); +} + +const infoPlist = join(appRoot, "Contents", "Info.plist"); +if (!existsSync(infoPlist)) { + fail(`macOS app template is missing at ${appRoot}; run npm run build:macos.`); +} + +const version = readProjectMetadata().product?.version; +if (!version) + fail("product.version could not be read from src/config/metadata.json."); + +const resourcesDir = join(appRoot, "Contents", "Resources"); +const runtimeDir = join(resourcesDir, "runtime"); +const manifestFile = join(resourcesDir, "runtime-manifest.json"); + +if (mode === "--check") { + if ( + !existsSync(appRoot) || + !existsSync(runtimeDir) || + !existsSync(manifestFile) + ) { + fail( + "Generated macOS app or runtime manifest is missing; run npm run build:macos.", + ); + } + if (!verifyRuntimeManifest(appRoot, version)) { + fail( + "Embedded macOS runtime manifest is invalid or stale; run npm run build:macos.", + ); + } + const staged = JSON.parse(readFileSync(manifestFile, "utf8")); + const canonical = generateRuntimeManifest(repoDir, version, readRuntimePaths(), repoDir); + if (JSON.stringify(staged.files) !== JSON.stringify(canonical.files)) { + fail("Embedded macOS runtime does not match canonical source; run npm run build:macos."); + } + console.log(`Embedded macOS runtime is current (${version}).`); +} else if (mode === "--write") { + mkdirSync(resourcesDir, { recursive: true }); + const runtimePaths = readRuntimePaths(); + const manifest = generateRuntimeManifest( + runtimeDir, + version, + runtimePaths, + repoDir, + ); + writeFileSync(manifestFile, formatRuntimeManifest(manifest)); + console.log(`Packaged macOS runtime ${version}.`); +} diff --git a/src/set-version.mjs b/src/set-version.mjs new file mode 100644 index 0000000..7f2f3b6 --- /dev/null +++ b/src/set-version.mjs @@ -0,0 +1,109 @@ +#!/usr/bin/env node +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { execFileSync } from "node:child_process"; +import { writePackageMetadata } from "./sync-package-metadata.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, ".."); +const versionArg = process.argv[2]; + +if (!versionArg || !/^\d+\.\d+\.\d+$/.test(versionArg.replace(/^v/i, ""))) { + console.error("Usage: node src/set-version.mjs <X.Y.Z>"); + process.exit(2); +} + +const cleanVersion = versionArg.replace(/^v/i, ""); +const metadataFile = join(repoDir, "src", "config", "metadata.json"); +const plistFile = join(repoDir, "src", "macos-app", "Contents", "Info.plist"); +const readmeFile = join(repoDir, "README.md"); +const engineFile = join(repoDir, "install-wor.sh"); +const packageFile = join(repoDir, "package.json"); + +for (const file of [metadataFile, plistFile, readmeFile, engineFile]) { + if (!existsSync(file)) { + console.error(`Required version file is missing: ${file}`); + process.exit(1); + } +} + +//String.replace is a silent no-op when the pattern stops matching, which would bump some files and +//quietly skip others, shipping a release whose parts disagree about their own version +function replaceOrFail(content, pattern, replacement, label) { + //test the pattern rather than diffing the result: re-setting the current version changes nothing + //and is legal, but a pattern that no longer matches must not pass silently + if ( + !new RegExp(pattern.source, pattern.flags.replace("g", "")).test(content) + ) { + console.error( + `Could not update the version in ${label}; its format has changed.`, + ); + process.exit(1); + } + return content.replace(pattern, replacement); +} + +// 1. project metadata +let metadataContent = readFileSync(metadataFile, "utf8"); +metadataContent = replaceOrFail( + metadataContent, + /("version"\s*:\s*")[^"]+("\s*,)/, + `$1${cleanVersion}$2`, + metadataFile, +); +writeFileSync(metadataFile, metadataContent); + +// 2. Info.plist +let plistContent = readFileSync(plistFile, "utf8"); +plistContent = replaceOrFail( + plistContent, + /(<key>CFBundleShortVersionString<\/key>\s*<string>)[^<]*(<\/string>)/g, + `$1${cleanVersion}$2`, + `${plistFile} (CFBundleShortVersionString)`, +); +plistContent = replaceOrFail( + plistContent, + /(<key>CFBundleVersion<\/key>\s*<string>)[^<]*(<\/string>)/g, + `$1${cleanVersion}$2`, + `${plistFile} (CFBundleVersion)`, +); +writeFileSync(plistFile, plistContent); + +// 3. README.md +let readmeContent = readFileSync(readmeFile, "utf8"); +readmeContent = readmeContent.replace( + /version-\d+\.\d+\.\d+-/, + `version-${cleanVersion}-`, +); +if (!readmeContent.includes(`- **${cleanVersion}**`)) { + readmeContent = readmeContent.replace( + /(## Versions\n\n)/, + `$1- **${cleanVersion}** - Release version update.\n`, + ); +} +writeFileSync(readmeFile, readmeContent); + +// 4. install-wor.sh +let engineContent = readFileSync(engineFile, "utf8"); +if (!engineContent.includes(`#${cleanVersion} - `)) { + engineContent = engineContent.replace( + /(#Version history\n#---------------\n)/, + `$1#${cleanVersion} - Release version update.\n`, + ); +} +writeFileSync(engineFile, engineContent); + +// 5. package.json - description, license, homepage, repository, bugs, funding, and keywords +// are all derived from src/config/metadata.json (already bumped in step 1), not hand-patched here +if (existsSync(packageFile)) { + writePackageMetadata(packageFile); +} + +// Rebuild macOS release artifacts +execFileSync(process.execPath, [join(scriptDir, "build-release.mjs"), "--platform=macos"], { + cwd: repoDir, + stdio: "inherit", +}); + +console.log(`Updated WoR-Flasher version surfaces to ${cleanVersion}.`); diff --git a/src/sync-package-metadata.mjs b/src/sync-package-metadata.mjs new file mode 100644 index 0000000..38696d2 --- /dev/null +++ b/src/sync-package-metadata.mjs @@ -0,0 +1,133 @@ +#!/usr/bin/env node +import { readFileSync, writeFileSync, existsSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { readProjectMetadata } from "./lib/node-runtime.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, ".."); +const defaultPackageFile = join(repoDir, "package.json"); + +//src/config/metadata.json is the source of truth for these package.json fields. Anything here +//is derived, never hand-edited in package.json: edit metadata.json and run --write instead. +export function desiredPackageFields(metadata) { + const product = metadata.product || {}; + const repoSlug = metadata.systemDefaults?.repoSlug; + if (!repoSlug) { + throw new Error( + "metadata.systemDefaults.repoSlug is required to derive package.json URLs", + ); + } + for (const key of [ + "version", + "description", + "license", + "keywords", + "fundingUrls", + ]) { + if (product[key] === undefined) { + throw new Error( + `metadata.product.${key} is required to sync package.json`, + ); + } + } + return { + version: product.version, + description: product.description, + license: product.license, + homepage: `https://github.com/${repoSlug}#readme`, + repository: { type: "git", url: `git+https://github.com/${repoSlug}.git` }, + bugs: { url: `https://github.com/${repoSlug}/issues` }, + funding: product.fundingUrls.map((url) => ({ type: "github", url })), + keywords: [...product.keywords], + }; +} + +function diffFields(pkg, desired) { + const drift = []; + for (const [key, value] of Object.entries(desired)) { + if (JSON.stringify(pkg[key]) !== JSON.stringify(value)) { + drift.push({ key, current: pkg[key], desired: value }); + } + } + return drift; +} + +function applyFields(pkg, desired) { + for (const [key, value] of Object.entries(desired)) { + pkg[key] = value; + } + return pkg; +} + +export function checkPackageMetadata( + packageFile = defaultPackageFile, + metadataFile = undefined, +) { + const pkg = JSON.parse(readFileSync(packageFile, "utf8")); + const metadata = readProjectMetadata(metadataFile); + const desired = desiredPackageFields(metadata); + return diffFields(pkg, desired); +} + +export function writePackageMetadata( + packageFile = defaultPackageFile, + metadataFile = undefined, +) { + const pkg = JSON.parse(readFileSync(packageFile, "utf8")); + const metadata = readProjectMetadata(metadataFile); + const desired = desiredPackageFields(metadata); + applyFields(pkg, desired); + writeFileSync(packageFile, `${JSON.stringify(pkg, null, 2)}\n`); + return desired; +} + +function fail(message, code = 1) { + console.error(message); + process.exit(code); +} + +function main() { + const args = process.argv.slice(2); + if (args.includes("--help") || args.includes("-h")) { + console.log( + `Sync package.json metadata from src/config/metadata.json (the source of truth)\n\nUsage:\n node src/sync-package-metadata.mjs --check Fail if package.json is out of sync\n node src/sync-package-metadata.mjs --write Update package.json to match metadata.json\n`, + ); + return; + } + if (!existsSync(defaultPackageFile)) { + fail(`Missing package.json: ${defaultPackageFile}`); + } + + if (args.includes("--write")) { + const desired = writePackageMetadata(); + console.log( + `Synced package.json from src/config/metadata.json (version ${desired.version}).`, + ); + return; + } + + if (args.includes("--check") || args.length === 0) { + const drift = checkPackageMetadata(); + if (drift.length > 0) { + console.error( + "package.json is out of sync with src/config/metadata.json:", + ); + for (const { key, current, desired } of drift) { + console.error(` ${key}:`); + console.error(` current: ${JSON.stringify(current)}`); + console.error(` desired: ${JSON.stringify(desired)}`); + } + console.error("Run: node src/sync-package-metadata.mjs --write"); + process.exit(1); + } + console.log("package.json matches src/config/metadata.json."); + return; + } + + fail("Unsupported option. Run with --help for usage.", 2); +} + +if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { + main(); +} diff --git a/src/updater.mjs b/src/updater.mjs new file mode 100644 index 0000000..36cb053 --- /dev/null +++ b/src/updater.mjs @@ -0,0 +1,110 @@ +#!/usr/bin/env node +import { writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { + checkReleaseUpdate, + compareVersions, + formatRuntimeManifest, + generateRuntimeManifest, + readProjectMetadata, + readRuntimePaths, + verifyRuntimeManifest, +} from "./lib/node-runtime.mjs"; + +const scriptDir = dirname(fileURLToPath(import.meta.url)); +const repoDir = join(scriptDir, ".."); + +async function main() { + const args = process.argv.slice(2); + if (args.length === 0 || args.includes("--help") || args.includes("-h")) { + console.log( + `WoR-Flasher Updater Tooling\n\n` + + `Usage: node src/updater.mjs [options]\n\n` + + `Options:\n` + + ` --check-release Check for a newer published release\n` + + ` --build-manifest <stage> <out> [v] Build runtime-manifest.json\n` + + ` --verify-manifest <appRoot> Verify embedded runtime manifest\n` + + ` --compare-versions <v1> <v2> Compare two version strings\n` + + ` --repo-dir=<path> Repository root directory\n` + + ` --repo-slug=<owner/name> Release source repository\n` + + ` --version=<version> Version to compare against\n` + + ` --json Emit machine-readable JSON\n`, + ); + return; + } + + const valueFor = (prefix) => { + const found = args.find((a) => a.startsWith(`${prefix}=`)); + return found ? found.split("=", 2)[1] : undefined; + }; + + const customRepoDir = valueFor("--repo-dir") || repoDir; + const projectVersion = () => { + try { + return readProjectMetadata( + join(customRepoDir, "src", "config", "metadata.json"), + ).product?.version; + } catch { + return undefined; + } + }; + + if (args.includes("--check-release")) { + const res = await checkReleaseUpdate({ + currentVersion: valueFor("--version") || projectVersion(), + repoSlug: valueFor("--repo-slug"), + repoPath: customRepoDir, + }); + if (args.includes("--json")) { + console.log(JSON.stringify(res, null, 2)); + return; + } + //Single stable line so the shell engine can branch on it without parsing JSON. + if (res.updateAvailable) { + console.log(`UPDATE_AVAILABLE ${res.latestVersion} ${res.htmlUrl}`); + } else if (res.error !== undefined) { + console.log("UNKNOWN"); + } else { + console.log("CURRENT"); + } + return; + } + + const buildIdx = args.indexOf("--build-manifest"); + if (buildIdx !== -1 && args[buildIdx + 1] && args[buildIdx + 2]) { + const stageRoot = args[buildIdx + 1]; + const manifestOut = args[buildIdx + 2]; + const ver = args[buildIdx + 3] || projectVersion(); + if (!ver) throw new Error("Could not resolve a version for the manifest"); + const manifest = generateRuntimeManifest( + stageRoot, + ver, + readRuntimePaths(), + customRepoDir, + ); + writeFileSync(manifestOut, formatRuntimeManifest(manifest)); + console.log(`Generated manifest at ${manifestOut}`); + return; + } + + const verifyIdx = args.indexOf("--verify-manifest"); + if (verifyIdx !== -1 && args[verifyIdx + 1]) { + const appRoot = args[verifyIdx + 1]; + if (!verifyRuntimeManifest(appRoot)) process.exit(1); + console.log("Manifest is valid"); + return; + } + + const compIdx = args.indexOf("--compare-versions"); + if (compIdx !== -1 && args[compIdx + 1] && args[compIdx + 2]) { + console.log(compareVersions(args[compIdx + 1], args[compIdx + 2])); + } +} + +if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) { + main().catch((err) => { + console.error(err); + process.exit(1); + }); +} diff --git a/terminal-run b/terminal-run deleted file mode 100755 index 10db773..0000000 --- a/terminal-run +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/bash - -#$1 is the command to be run. -#$2 is the title. -commands="$1" -title="$2" - -#set DEBUG variable to 1 to display which terminal is being used -[ -z "$DEBUG" ] && DEBUG=0 - -#seconds to wait for the terminal to start before giving up -[ -z "$TERMINAL_TIMEOUT" ] && TERMINAL_TIMEOUT=15 - -error() { - echo -e "\e[91m$1\e[39m" 1>&2 - exit 1 -} - -status() { - echo -e "\e[96m$1\e[0m" 1>&2 -} - -resolve_path() { - if command -v realpath >/dev/null ;then - realpath "$1" - elif readlink -f "$1" >/dev/null 2>&1 ;then - readlink -f "$1" - else - printf '%s\n' "$1" - fi -} - -#a private directory keeps the pid file path unpredictable to other local users -temp_pid_dir="$(mktemp -d)" || error "terminal-run: failed to create a temporary directory." -temp_pid_file="$temp_pid_dir/pid" -trap 'rm -rf "$temp_pid_dir"' EXIT - -#prepend a line that records the terminal's PID so this script can wait for it -commands="echo "\$""\$" > $temp_pid_file -$commands" - -if command -v lxterminal >/dev/null ;then - lxterminal --title="$title" -e bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using lxterminal" - -elif command -v xfce4-terminal >/dev/null ;then - xfce4-terminal --title="$title" -x bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using xfce4-terminal" - -elif command -v mate-terminal >/dev/null ;then - mate-terminal --title="$title" -x bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using mate-terminal" - -elif command -v xterm >/dev/null ;then - xterm -T "$title" -e bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using xterm" - -elif command -v konsole >/dev/null ;then - konsole -p tabtitle="$title" -e bash <(echo "$commands") & - [ $DEBUG == 1 ] && status "terminal-run: using konsole" - -elif command -v terminator >/dev/null ;then - terminator -T "$title" -x bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using terminator" - -elif command -v gnome-terminal >/dev/null ;then - #gnome-terminal removed -x in favour of -- - gnome-terminal --title="$title" -- bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using gnome-terminal" - -elif command -v qterminal >/dev/null ;then - qterminal -e bash <(echo "$commands") & - [ $DEBUG == 1 ] && status "terminal-run: using qterminal" - -elif command -v x-terminal-emulator >/dev/null ;then - "$(resolve_path "$(command -v x-terminal-emulator)")" -e bash -c "$commands" & - [ $DEBUG == 1 ] && status "terminal-run: using x-terminal-emulator" - -else - error "terminal-run: Failed to locate any terminal emulators!" -fi - -#A terminal should now be launching and in a few seconds the pid file should appear -if [ ! -z "$temp_pid_file" ];then - attempts=0 - while [ ! -f "$temp_pid_file" ];do - - if [ "$attempts" -gt "$TERMINAL_TIMEOUT" ];then - error "terminal-run: No terminal detected as it never created the pid file within $TERMINAL_TIMEOUT seconds." - fi - sleep 1 - attempts=$((attempts + 1)) - done #pid file now exists - terminalrun_pid="$(cat "$temp_pid_file")" - - #now, wait until the pid stops (terminal closes) - while [ -d /proc/${terminalrun_pid} ];do - sleep 1 - done -fi diff --git a/tests/macos-disk-alerts.test.mjs b/tests/macos-disk-alerts.test.mjs new file mode 100644 index 0000000..7724037 --- /dev/null +++ b/tests/macos-disk-alerts.test.mjs @@ -0,0 +1,197 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync, mkdtempSync, writeFileSync, existsSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { spawnSync } from "node:child_process"; +import vm from "node:vm"; + +const root = fileURLToPath(new URL("../", import.meta.url)); +const context = vm.createContext({}); +vm.runInContext(readFileSync(join(root, "src/lib/macos-disk-alerts.js"), "utf8"), context); +const decide = context.worDiskAlertDecision; +const active = "STEP\t5\t8\tPartitioning\nDISK_WRITE\t1\t/dev/disk99\n"; + +function alert(overrides = {}) { + return { + owner: "UserNotificationCenter", + reference: "system-window", + texts: ["The disk you attached was not readable by this computer."], + buttons: [ + { name: "Eject", enabled: true, reference: "eject" }, + { name: "Initialize...", enabled: true, reference: "initialize" }, + { name: "Ignore", enabled: true, reference: "ignore" }, + ], + ...overrides, + }; +} + +describe("macOS unreadable-disk alert policy", () => { + it("selects only Ignore for the exact system alert during authorized writes", () => { + for (const owner of ["UserNotificationCenter", "DiskArbitrationAgent"]) { + const result = decide(active, false, [alert({ owner })]); + assert.equal(result.action, "ignore"); + assert.equal(result.target.button, "ignore"); + } + }); + + it("does nothing before authentication, during downloads, or after writes finish", () => { + for (const progress of [ + "", + "STEP\t5\t8\tWaiting for administrator access\n", + "STEP\t4\t8\tPreparing Windows\nDISK_WRITE\t1\t/dev/disk99\n", + `${active}DISK_WRITE\t0\t/dev/disk99\n`, + `${active}STEP\t4\t8\tRetry preparation\n`, + ]) { + assert.equal(decide(progress, false, [alert()]).action, "none"); + } + }); + + it("does nothing after completion, cancellation, or loss of the installer", () => { + assert.equal(decide(active, true, [alert()]).action, "none"); + }); + + it("rejects malformed progress and non-whole-disk write markers", () => { + for (const progress of [ + "STEP\t5x\t8\tPartitioning\nDISK_WRITE\t1\t/dev/disk99\n", + "STEP\t5\t9\tPartitioning\nDISK_WRITE\t1\t/dev/disk99\n", + "STEP\t5\t8\tPartitioning\nDISK_WRITE\t1\t/dev/disk99s1\n", + "STEP\t5\t8\tPartitioning\nDISK_WRITE\t1\t/tmp/image\n", + "STATUS\tSTEP\t5\t8\tPartitioning\nDISK_WRITE\t1\t/dev/disk99\n", + ]) { + assert.equal(decide(progress, false, [alert()]).action, "none"); + } + }); + + it("leaves lookalike application dialogs and other system alerts untouched", () => { + assert.equal(decide(active, false, [alert({ owner: "WoR-Flasher" })]).action, "none"); + assert.equal(decide(active, false, [alert({ owner: "Finder" })]).action, "none"); + assert.equal(decide(active, false, [alert({ texts: ["A different warning with an Ignore button."] })]).action, "none"); + assert.equal(decide(active, false, [alert({ texts: ["Do not ignore: The disk you attached was not readable by this computer."] })]).action, "none"); + }); + + it("requires the expected controls and an enabled, unambiguous Ignore button", () => { + const original = alert(); + for (const buttons of [ + original.buttons.slice(1), + original.buttons.filter((button) => button.name !== "Initialize..."), + original.buttons.map((button) => ({ ...button, enabled: false })), + [...original.buttons, original.buttons[2]], + ]) { + assert.equal(decide(active, false, [alert({ buttons })]).action, "none"); + } + }); + + it("tolerates presentation whitespace and ellipses without choosing another action", () => { + const candidate = alert({ + texts: ["The disk you attached was not\nreadable by this computer"], + buttons: [ + { name: "Ignore", enabled: true, reference: "ignore" }, + { name: "Initialize\u2026", enabled: true, reference: "initialize" }, + { name: "Eject", enabled: true, reference: "eject" }, + ], + }); + assert.equal(decide(active, false, [candidate]).target.button, "ignore"); + }); + + it("leaves multiple matching alerts for manual handling", () => { + const result = decide(active, false, [alert(), alert({ reference: "another-disk" })]); + assert.equal(result.action, "ambiguous"); + assert.equal(result.target, undefined); + }); + + it("does not misclassify denied accessibility permission as a disappearing alert", () => { + assert.equal(context.worDiskAlertTransientError({ number: -1719, message: "osascript is not allowed assistive access." }), false); + assert.equal(context.worDiskAlertTransientError({ number: -1743, message: "Not authorized to send Apple events." }), false); + assert.equal(context.worDiskAlertTransientError({ number: -1719, message: "Invalid index." }), true); + assert.equal(context.worDiskAlertTransientError({ number: -1728, message: "Cannot get the missing window." }), true); + assert.equal(context.worDiskAlertTransientError({ number: -600, message: "Application is not running." }), true); + assert.equal(context.worDiskAlertTransientError({ number: -1, message: "Unexpected failure." }), false); + }); + + it("receives authorization only after authentication and finalizer readiness", () => { + const engine = readFileSync(join(root, "install-wor.sh"), "utf8"); + const authenticate = engine.indexOf('sudo -v || error "Administrator authentication failed or was canceled. Enter the macOS password'); + const finalizer = engine.indexOf('darwin_start_partition_finalizer_or_die "$DEVICE"', authenticate); + const begin = engine.indexOf('emit_gui_progress "DISK_WRITE"', finalizer); + const prepare = engine.indexOf('darwin_prepare_disk_or_die "$DEVICE"', begin); + assert.ok(authenticate >= 0 && authenticate < finalizer && finalizer < begin && begin < prepare); + assert.match(engine.slice(prepare), /emit_gui_progress "DISK_WRITE"\$'\\t'"0"/); + }); +}); + +describe("macOS disk-alert helper isolation", () => { + for (const scenario of ["success", "failure", "blocked"]) { + const failed = scenario === "failure"; + const blocked = scenario === "blocked"; + it(`handles helper ${scenario} without changing installer status or hanging cleanup`, () => { + const directory = mkdtempSync(join(tmpdir(), "wor-alert-lifecycle-")); + let completed = false; + try { + const gui = readFileSync(join(root, "install-wor-gui.sh"), "utf8"); + const functions = ["gui_start_disk_alert_handler", "gui_stop_disk_alert_handler"].map((name) => { + const match = gui.match(new RegExp(`^${name}\\(\\) \\{[\\s\\S]*?^\\}`, "m")); + assert.ok(match, `missing ${name}`); + return match[0]; + }).join("\n"); + writeFileSync(join(directory, "functions.sh"), functions); + const result = spawnSync("bash", ["-c", ` + source "$1/functions.sh" + DIRECTORY="$1" progress_file="$1/progress" done_marker="$1/done" abort_marker="$1/abort" + disk_alert_status="$1/status" disk_alert_log="$1/log" disk_alert_done="$1/helper-done" + output_log="$1/installer-log" installer_pid=$$ installer_status=0 disk_alert_warning="" + printf "installer output\\n" > "$output_log" + wor_osascript() { + printf '%s\\n' "$MOCK_ALERT_STATUS" > "$disk_alert_status" + printf "helper output\\n" + if [ "$MOCK_ALERT_BLOCK" == 1 ];then + trap "" TERM + sleep 30 & + printf '%s\\n' "$!" >> "$DIRECTORY/pids" + wait "$!" + fi + return "$MOCK_ALERT_EXIT" + } + gui_start_disk_alert_handler + printf '%s\\n' "$disk_alert_pid" >> "$DIRECTORY/pids" + gui_stop_disk_alert_handler + printf '%s\\n' "$installer_status" "$disk_alert_warning" + `, "bash", directory], { + encoding: "utf8", + timeout: 8000, + env: { + ...process.env, + MOCK_ALERT_BLOCK: blocked ? "1" : "0", + MOCK_ALERT_EXIT: failed ? "23" : "0", + MOCK_ALERT_STATUS: JSON.stringify({ state: failed ? "warning" : "ignored", message: failed ? "mock permission denied" : "mock ignored" }), + }, + }); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout.split("\n")[0], "0"); + if (failed) assert.match(result.stdout, /mock permission denied/); + else if (blocked) assert.match(result.stdout, /Automatic Ignore stopped unexpectedly/); + else assert.equal(result.stdout.split("\n")[1], ""); + const log = readFileSync(join(directory, "installer-log"), "utf8"); + assert.ok(log.startsWith("installer output\n")); + assert.match(log, /helper output/); + for (const name of ["status", "log", "helper-done"]) { + assert.equal(existsSync(join(directory, name)), false); + } + completed = true; + } finally { + if (!completed && existsSync(join(directory, "pids"))) { + for (const value of readFileSync(join(directory, "pids"), "utf8").trim().split("\n")) { + if (!/^[1-9][0-9]*$/.test(value)) continue; + try { + process.kill(Number(value), "SIGKILL"); + } catch (error) { + if (error.code !== "ESRCH") throw error; + } + } + } + rmSync(directory, { recursive: true, force: true }); + } + }); + } +}); diff --git a/tests/node-tools.test.mjs b/tests/node-tools.test.mjs new file mode 100644 index 0000000..63d5b20 --- /dev/null +++ b/tests/node-tools.test.mjs @@ -0,0 +1,656 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { + mkdtempSync, + rmSync, + writeFileSync, + mkdirSync, + readFileSync, + existsSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { spawnSync } from "node:child_process"; +import { + selectPeAsset, + sha256Buffer, + updatePeInstallerMetadata, + verifyPinnedPeInstaller, +} from "../src/check-pe-installer.mjs"; +import { + checkPackageMetadata, + desiredPackageFields, + writePackageMetadata, +} from "../src/sync-package-metadata.mjs"; +import { + parseVersion, + compareVersions, + readProjectMetadata, + readRuntimePaths, + generateRuntimeManifest, + formatRuntimeManifest, + verifyRuntimeManifest, + checkReleaseUpdate, + isSafeRepoSlug, + loadSystemConfig, +} from "../src/lib/node-runtime.mjs"; + +function shellMetadata(name) { + const result = spawnSync( + "bash", + [ + "-c", + `source src/lib/metadata.sh >/dev/null 2>&1; printf '%s' "$${name}"`, + ], + { encoding: "utf8" }, + ); + assert.equal(result.status, 0, result.stderr); + return result.stdout; +} + +function shellNumberMetadata(name) { + const value = shellMetadata(name); + return value === null ? null : Number(value); +} + +function plistString(content, key) { + const match = content.match( + new RegExp(`<key>${key}</key>\\s*<string>([^<]*)</string>`), + ); + return match ? match[1] : null; +} + +describe("Node.js Tooling - Version Parsing & Comparison", () => { + it("should parse semver version strings into tuple arrays", () => { + assert.deepEqual(parseVersion("1.0.2"), [1, 0, 2]); + assert.deepEqual(parseVersion("v1.2.3"), [1, 2, 3]); + assert.deepEqual(parseVersion("2.0"), [2, 0, 0]); + assert.deepEqual(parseVersion("invalid"), [0, 0, 0]); + assert.deepEqual(parseVersion(""), [0, 0, 0]); + }); + + it("should compare version strings correctly", () => { + assert.equal(compareVersions("1.0.2", "1.0.3"), -1); + assert.equal(compareVersions("1.0.2", "1.0.2"), 0); + assert.equal(compareVersions("1.1.0", "1.0.9"), 1); + assert.equal(compareVersions("v2.0.0", "1.9.9"), 1); + assert.equal(compareVersions("1.0.0", "v1.0.0"), 0); + }); +}); + +describe("Node.js Tooling - Release Update Check", () => { + //the slug is interpolated into the update URL, so a loose one could redirect the check + it("should only accept a plain owner/name repository slug", () => { + assert.equal(isSafeRepoSlug("Botspot/wor-flasher"), true); + assert.equal(isSafeRepoSlug("owner/repo.name-1_2"), true); + assert.equal(isSafeRepoSlug("evil.com/a/b"), false); + assert.equal(isSafeRepoSlug("../../etc/passwd"), false); + assert.equal(isSafeRepoSlug("owner/repo?x=1"), false); + assert.equal(isSafeRepoSlug("owner"), false); + assert.equal(isSafeRepoSlug(""), false); + assert.equal(isSafeRepoSlug(undefined), false); + }); + + it("should refuse to contact anything when the slug is unsafe", async () => { + let called = false; + const res = await checkReleaseUpdate({ + currentVersion: "1.0.0", + repoSlug: "https://evil.invalid/x", + fetchImpl: () => { + called = true; + throw new Error("fetch must not run for an unsafe slug"); + }, + }); + assert.equal(called, false); + assert.equal(res.updateAvailable, false); + assert.equal(res.error, "unsafe-repo-slug"); + }); + + it("should report an available update over HTTPS without following redirects", async () => { + let seenUrl = ""; + let seenRedirect = ""; + const res = await checkReleaseUpdate({ + currentVersion: "1.0.2", + repoSlug: "Botspot/wor-flasher", + fetchImpl: (url, options) => { + seenUrl = url; + seenRedirect = options.redirect; + return { + ok: true, + json: async () => ({ + tag_name: "v1.1.0", + html_url: "https://github.com/Botspot/wor-flasher/releases/v1.1.0", + }), + }; + }, + }); + assert.equal( + seenUrl, + "https://api.github.com/repos/Botspot/wor-flasher/releases/latest", + ); + assert.equal(seenRedirect, "error"); + assert.equal(res.updateAvailable, true); + assert.equal(res.latestVersion, "v1.1.0"); + }); + + it("should not report an update for equal or older releases", async () => { + const stub = (tag) => () => ({ + ok: true, + json: async () => ({ tag_name: tag, html_url: "" }), + }); + for (const tag of ["v1.0.2", "v1.0.1", "v0.9.9"]) { + const res = await checkReleaseUpdate({ + currentVersion: "1.0.2", + repoSlug: "Botspot/wor-flasher", + fetchImpl: stub(tag), + }); + assert.equal(res.updateAvailable, false, `tag ${tag} must not update`); + } + }); + + it("should ignore drafts and prereleases", async () => { + for (const flags of [{ draft: true }, { prerelease: true }]) { + const res = await checkReleaseUpdate({ + currentVersion: "1.0.2", + repoSlug: "Botspot/wor-flasher", + fetchImpl: () => ({ + ok: true, + json: async () => ({ tag_name: "v9.9.9", ...flags }), + }), + }); + assert.equal(res.updateAvailable, false); + } + }); + + it("should drop a non-HTTPS release link and survive a failed request", async () => { + const downgraded = await checkReleaseUpdate({ + currentVersion: "1.0.2", + repoSlug: "Botspot/wor-flasher", + fetchImpl: () => ({ + ok: true, + json: async () => ({ + tag_name: "v1.1.0", + html_url: "http://evil.invalid/release", + }), + }), + }); + assert.equal(downgraded.updateAvailable, true); + assert.equal(downgraded.htmlUrl, ""); + + const failed = await checkReleaseUpdate({ + currentVersion: "1.0.2", + repoSlug: "Botspot/wor-flasher", + fetchImpl: () => { + throw new Error("network down"); + }, + }); + assert.equal(failed.updateAvailable, false); + assert.equal(failed.error, "network down"); + }); +}); + +describe("Node.js Tooling - Package Metadata Sync", () => { + it("should report the real package.json as in sync with src/config/metadata.json", () => { + assert.deepEqual(checkPackageMetadata(), []); + }); + + it("should derive package.json fields from metadata.product and systemDefaults.repoSlug", () => { + const desired = desiredPackageFields({ + product: { + version: "9.9.9", + description: "Example description.", + license: "MIT", + keywords: ["a", "b"], + fundingUrls: ["https://github.com/sponsors/example"], + }, + systemDefaults: { repoSlug: "example/repo" }, + }); + assert.equal(desired.version, "9.9.9"); + assert.equal(desired.homepage, "https://github.com/example/repo#readme"); + assert.deepEqual(desired.repository, { + type: "git", + url: "git+https://github.com/example/repo.git", + }); + assert.deepEqual(desired.bugs, { + url: "https://github.com/example/repo/issues", + }); + assert.deepEqual(desired.funding, [ + { type: "github", url: "https://github.com/sponsors/example" }, + ]); + assert.deepEqual(desired.keywords, ["a", "b"]); + }); + + it("should detect drift and rewrite package.json to match metadata.json", () => { + const tempDir = mkdtempSync(join(tmpdir(), "wor-pkg-sync-")); + try { + mkdirSync(join(tempDir, "src", "config"), { recursive: true }); + const metadataFile = join(tempDir, "src", "config", "metadata.json"); + const packageFile = join(tempDir, "package.json"); + writeFileSync( + metadataFile, + JSON.stringify({ + product: { + version: "2.0.0", + description: "New description.", + license: "MIT", + keywords: ["x"], + fundingUrls: ["https://github.com/sponsors/example"], + }, + systemDefaults: { repoSlug: "example/repo" }, + }), + ); + writeFileSync( + packageFile, + JSON.stringify( + { name: "example", version: "1.0.0", private: true }, + null, + 2, + ), + ); + + const drift = checkPackageMetadata(packageFile, metadataFile); + assert.ok(drift.some((d) => d.key === "version")); + assert.ok(drift.some((d) => d.key === "description")); + + writePackageMetadata(packageFile, metadataFile); + const updated = JSON.parse(readFileSync(packageFile, "utf8")); + assert.equal(updated.name, "example"); + assert.equal(updated.version, "2.0.0"); + assert.equal(updated.description, "New description."); + assert.equal(updated.homepage, "https://github.com/example/repo#readme"); + assert.deepEqual(checkPackageMetadata(packageFile, metadataFile), []); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); +}); + +describe("Node.js Tooling - Runtime Paths & Manifests", () => { + it("should verify PE installer metadata hashes without network when given a file URL", async () => { + const tempDir = mkdtempSync(join(tmpdir(), "wor-pe-check-")); + try { + const peFile = join(tempDir, "WoR-PE_Package_fixture.zip"); + const bytes = Buffer.from("fixture pe installer bytes", "utf8"); + writeFileSync(peFile, bytes); + const metadataFile = join(tempDir, "metadata.json"); + writeFileSync( + metadataFile, + JSON.stringify({ + systemDefaults: { + peInstallerUrl: `file://${peFile}`, + peInstallerSha256: sha256Buffer(bytes), + }, + }), + ); + + const result = await verifyPinnedPeInstaller(metadataFile); + assert.equal(result.ok, true); + assert.equal(result.actual, sha256Buffer(bytes)); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("should select the WoR-PE package asset from a GitHub release payload", () => { + const asset = selectPeAsset({ + assets: [ + { + name: "notes.txt", + browser_download_url: "https://example.invalid/notes.txt", + }, + { + name: "WoR-PE_Package_1.2.3.zip", + browser_download_url: + "https://example.invalid/WoR-PE_Package_1.2.3.zip", + }, + ], + }); + assert.equal(asset.name, "WoR-PE_Package_1.2.3.zip"); + }); + + it("should update PE installer metadata from a release payload and downloaded hash", async () => { + const tempDir = mkdtempSync(join(tmpdir(), "wor-pe-update-")); + try { + const peFile = join(tempDir, "WoR-PE_Package_9.9.9.zip"); + const bytes = Buffer.from("updated pe installer bytes", "utf8"); + writeFileSync(peFile, bytes); + const metadataFile = join(tempDir, "metadata.json"); + writeFileSync( + metadataFile, + JSON.stringify({ + systemDefaults: { + peInstallerUrl: "https://example.invalid/old.zip", + peInstallerSha256: "0".repeat(64), + }, + }), + ); + + const previousFetch = globalThis.fetch; + globalThis.fetch = async () => ({ + ok: true, + json: async () => ({ + assets: [ + { + name: "WoR-PE_Package_9.9.9.zip", + browser_download_url: `file://${peFile}`, + }, + ], + }), + }); + try { + const result = await updatePeInstallerMetadata({ + metadataFile, + releaseApiUrl: "https://api.example.invalid/latest", + }); + assert.equal(result.sha256, sha256Buffer(bytes)); + const updated = JSON.parse(readFileSync(metadataFile, "utf8")); + assert.equal(updated.systemDefaults.peInstallerUrl, `file://${peFile}`); + assert.equal( + updated.systemDefaults.peInstallerSha256, + sha256Buffer(bytes), + ); + } finally { + globalThis.fetch = previousFetch; + } + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("should keep project metadata aligned with package metadata, shell metadata and the app plist", () => { + const pkg = JSON.parse(readFileSync("package.json", "utf8")); + const project = readProjectMetadata(); + const plist = readFileSync("src/macos-app/Contents/Info.plist", "utf8"); + + assert.equal(pkg.name, "wor-flasher"); + assert.equal(pkg.license, "GPL-3.0-or-later"); + assert.equal(pkg.version, project.product.version); + assert.equal(project.product.version, shellMetadata("WOR_FLASHER_VERSION")); + assert.equal(project.product.name, shellMetadata("WOR_FLASHER_NAME")); + assert.equal(project.product.iconName, shellMetadata("WOR_ICON_NAME")); + assert.equal( + project.product.iconFilename, + shellMetadata("WOR_ICON_FILENAME"), + ); + assert.equal( + project.product.logoFilename, + shellMetadata("WOR_LOGO_FILENAME"), + ); + assert.equal( + project.product.assetsDirname, + shellMetadata("WOR_ASSETS_DIRNAME"), + ); + assert.equal( + project.systemDefaults.peInstallerUrl, + shellMetadata("WOR_DEFAULT_PE_INSTALLER_URL"), + ); + assert.equal( + project.systemDefaults.peInstallerSha256, + shellMetadata("WOR_DEFAULT_PE_INSTALLER_SHA256"), + ); + assert.equal( + project.systemDefaults.uefiVerPi3, + shellMetadata("WOR_DEFAULT_UEFI_VER_PI3"), + ); + assert.equal( + project.systemDefaults.uefiVerPi4, + shellMetadata("WOR_DEFAULT_UEFI_VER_PI4"), + ); + assert.equal( + project.systemDefaults.uefiVerPi5, + shellMetadata("WOR_DEFAULT_UEFI_VER_PI5"), + ); + assert.equal( + project.systemDefaults.uefiRepoPi3, + shellMetadata("WOR_DEFAULT_UEFI_REPO_PI3"), + ); + assert.equal( + project.systemDefaults.uefiRepoPi4, + shellMetadata("WOR_DEFAULT_UEFI_REPO_PI4"), + ); + assert.equal( + project.systemDefaults.uefiRepoPi5, + shellMetadata("WOR_DEFAULT_UEFI_REPO_PI5"), + ); + assert.equal( + project.systemDefaults.driverVer, + shellMetadata("WOR_DEFAULT_DRIVER_VER"), + ); + assert.equal( + project.systemDefaults.driversRepo, + shellMetadata("WOR_DEFAULT_DRIVERS_REPO"), + ); + assert.equal( + project.systemDefaults.armv80MaxBuild, + shellNumberMetadata("WOR_DEFAULT_ARMV80_MAX_BUILD"), + ); + assert.equal( + project.systemDefaults.win11MinBuild, + shellNumberMetadata("WOR_DEFAULT_WIN11_MIN_BUILD"), + ); + assert.equal( + project.systemDefaults.win10OldestBuild, + shellMetadata("WOR_DEFAULT_WIN10_OLDEST_BUILD"), + ); + assert.equal( + project.systemDefaults.exampleBid, + shellMetadata("WOR_DEFAULT_EXAMPLE_BID"), + ); + assert.equal( + project.systemDefaults.armv80SafeBid, + shellMetadata("WOR_DEFAULT_ARMV80_SAFE_BID"), + ); + assert.equal( + project.systemDefaults.repoSlug, + shellMetadata("WOR_DEFAULT_REPO_SLUG"), + ); + assert.equal( + project.systemDefaults.logDirname, + shellMetadata("WOR_DEFAULT_LOG_DIRNAME"), + ); + assert.equal( + project.product.name, + plistString(plist, "CFBundleDisplayName"), + ); + assert.equal( + project.product.name, + plistString(plist, "CFBundleExecutable"), + ); + assert.equal( + project.product.iconFilename, + plistString(plist, "CFBundleIconFile"), + ); + assert.equal( + project.product.iconName, + plistString(plist, "CFBundleIconName"), + ); + assert.equal(project.product.name, plistString(plist, "CFBundleName")); + assert.equal( + project.product.bundleIdentifier, + plistString(plist, "CFBundleIdentifier"), + ); + assert.equal( + project.product.minimumMacosVersion, + plistString(plist, "LSMinimumSystemVersion"), + ); + assert.equal(pkg.version, plistString(plist, "CFBundleShortVersionString")); + assert.equal(pkg.version, plistString(plist, "CFBundleVersion")); + }); + + it("should load system defaults from project metadata, not the default config template", () => { + const template = JSON.parse( + readFileSync("config-templates/config.json", "utf8"), + ); + assert.equal(template.system, undefined); + + const config = loadSystemConfig(); + assert.ok(config.system); + assert.equal( + config.system.peInstallerUrl, + "https://github.com/worproject/dldserv-mirror/releases/download/13%2F02%2F2024/WoR-PE_Package_1.1.0.zip", + ); + assert.equal( + config.system.peInstallerSha256, + "A039E28FE7E39147899B0634C15E336C3B26A6F76201092EBB9732474CD43D0A", + ); + assert.equal(config.system.uefiVerPi4, "v1.50"); + assert.equal(config.system.driverVer, "v0.17"); + assert.equal(config.system.repoSlug, "blackoutsecure/wor-flasher"); + //the retired git self-updater pins must not come back through the config cascade + assert.equal(config.system.updateRepoUrl, undefined); + assert.equal(config.system.updateRef, undefined); + }); + + it("should let a custom config system block override project metadata defaults", () => { + const tempDir = mkdtempSync(join(tmpdir(), "wor-system-config-")); + try { + mkdirSync(join(tempDir, "config-templates"), { recursive: true }); + mkdirSync(join(tempDir, "src", "config"), { recursive: true }); + writeFileSync( + join(tempDir, "src", "config", "metadata.json"), + JSON.stringify({ + systemDefaults: { + peInstallerUrl: "https://example.invalid/default.zip", + peInstallerSha256: "abc", + repoSlug: "example/default", + }, + }), + ); + writeFileSync( + join(tempDir, "config-templates", "config.json"), + JSON.stringify({ + system: { + peInstallerUrl: "https://example.invalid/override.zip", + repoSlug: "example/override", + }, + }), + ); + + const config = loadSystemConfig(tempDir); + assert.equal( + config.system.peInstallerUrl, + "https://example.invalid/override.zip", + ); + assert.equal(config.system.peInstallerSha256, "abc"); + assert.equal(config.system.repoSlug, "example/override"); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); + + it("should read runtime paths configuration from project metadata", () => { + const paths = readRuntimePaths(); + assert.ok(Array.isArray(paths)); + assert.ok(paths.includes("install-wor.sh")); + assert.ok(paths.includes("src/lib")); + assert.ok(paths.includes("src/updater.mjs")); + assert.ok(!paths.includes("src/build-release.mjs")); + assert.ok(!paths.includes("src/package-macos-app.mjs")); + assert.ok(!paths.includes("src/set-version.mjs")); + }); + + it("should generate and verify runtime manifests for temporary directory structure", () => { + const tempDir = mkdtempSync(join(tmpdir(), "wor-test-manifest-")); + try { + const stageDir = join(tempDir, "Contents", "Resources", "runtime"); + const manifestFile = join( + tempDir, + "Contents", + "Resources", + "runtime-manifest.json", + ); + mkdirSync(stageDir, { recursive: true }); + + const testFile = join(stageDir, "test.txt"); + writeFileSync(testFile, "hello wor-flasher", "utf8"); + + const manifest = generateRuntimeManifest( + stageDir, + "1.0.2", + ["test.txt"], + stageDir, + ); + assert.equal(manifest.schemaVersion, 1); + assert.equal(manifest.version, "1.0.2"); + assert.equal(manifest.files.length, 1); + assert.equal(manifest.files[0].path, "test.txt"); + + writeFileSync(manifestFile, formatRuntimeManifest(manifest)); + assert.equal(verifyRuntimeManifest(tempDir), true); + assert.match( + formatRuntimeManifest(manifest), + /\{"path": "test\.txt", "sha256": "[0-9a-f]{64}", "mode": "[0-7]+"\}/, + ); + + // Tamper with test file + writeFileSync(testFile, "tampered content", "utf8"); + assert.equal(verifyRuntimeManifest(tempDir), false); + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } + }); +}); + +describe("Node.js Tooling - macOS Packaging & Version CLI", () => { + it("should package Linux entry scripts in a versioned tar.gz release", () => { + const pkg = JSON.parse(readFileSync("package.json", "utf8")); + const archiveName = `WoR-Flasher-${pkg.version}-linux.tar.gz`; + const archivePath = join("release", "linux", archiveName); + const buildRes = spawnSync( + "node", + ["src/build-release.mjs", "--platform=linux"], + { encoding: "utf8" }, + ); + assert.equal(buildRes.status, 0, buildRes.stderr); + assert.equal(existsSync(archivePath), true); + + const archiveRes = spawnSync("tar", ["-tzf", archivePath], { + encoding: "utf8", + }); + assert.equal(archiveRes.status, 0, archiveRes.stderr); + for (const script of [ + "wor-flasher/install-wor.sh", + "wor-flasher/install-wor-gui.sh", + "wor-flasher/install-wor-hook.sh", + ]) { + assert.match(archiveRes.stdout, new RegExp(`^${script}$`, "m")); + } + + const sums = readFileSync(join("release", "linux", "SHA256SUMS"), "utf8"); + assert.match(sums, new RegExp(`^[a-f0-9]{64} ${archiveName}$`, "m")); + }); + + it("should pass packaging validation for staged macOS app", () => { + const buildRes = spawnSync( + "node", + ["src/build-release.mjs", "--platform=macos"], + { encoding: "utf8" }, + ); + assert.equal(buildRes.status, 0); + + const checkRes = spawnSync( + "node", + ["src/package-macos-app.mjs", "--check"], + { encoding: "utf8" }, + ); + assert.equal(checkRes.status, 0); + assert.match(checkRes.stdout, /Embedded macOS runtime is current/); + }); + + it("should execute package-macos-app --write cleanly when app bundle exists", () => { + const writeRes = spawnSync( + "node", + ["src/package-macos-app.mjs", "--write"], + { encoding: "utf8" }, + ); + assert.equal(writeRes.status, 0); + assert.match(writeRes.stdout, /Packaged macOS runtime/); + }); + + it("should reject set-version with invalid semver format", () => { + const result = spawnSync("node", ["src/set-version.mjs", "invalid"], { + encoding: "utf8", + }); + assert.notEqual(result.status, 0); + }); +}); diff --git a/tests/publication-review.test.mjs b/tests/publication-review.test.mjs new file mode 100644 index 0000000..5735de2 --- /dev/null +++ b/tests/publication-review.test.mjs @@ -0,0 +1,172 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { + copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, + renameSync, rmSync, symlinkSync, writeFileSync, +} from "node:fs"; +import { tmpdir } from "node:os"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { spawnSync } from "node:child_process"; +import { + formatRuntimeManifest, generateRuntimeManifest, readProjectMetadata, verifyRuntimeManifest, +} from "../src/lib/node-runtime.mjs"; + +const root = fileURLToPath(new URL("../", import.meta.url)); + +function temporary(callback) { + const directory = mkdtempSync(join(tmpdir(), "wor-publication-review-")); + try { + callback(directory); + } finally { + rmSync(directory, { recursive: true, force: true }); + } +} + +function copySource(relative, target) { + mkdirSync(dirname(join(target, relative)), { recursive: true }); + copyFileSync(join(root, relative), join(target, relative)); +} + +function packageFixture(directory) { + for (const path of ["src/package-macos-app.mjs", "src/lib/node-runtime.mjs"]) copySource(path, directory); + mkdirSync(join(directory, "src/config"), { recursive: true }); + writeFileSync(join(directory, "src/config/metadata.json"), JSON.stringify({ + ...readProjectMetadata(), runtimePaths: ["payload.txt"], + })); + writeFileSync(join(directory, "payload.txt"), "current source"); + const app = join(directory, "staged.app"); + const runtime = join(app, "Contents/Resources/runtime"); + mkdirSync(runtime, { recursive: true }); + writeFileSync(join(app, "Contents/Info.plist"), "<plist/>"); + const manifest = generateRuntimeManifest(runtime, readProjectMetadata().product.version, ["payload.txt"], directory); + const manifestPath = join(app, "Contents/Resources/runtime-manifest.json"); + writeFileSync(manifestPath, formatRuntimeManifest(manifest)); + return { app, runtime, manifest, manifestPath }; +} + +describe("Publication runtime integrity", () => { + it("refreshes changed files and removes obsolete staged files on every package write", () => temporary((directory) => { + const source = join(directory, "source"); + const stage = join(directory, "stage"); + mkdirSync(join(source, "tree"), { recursive: true }); + writeFileSync(join(source, "tree/current.txt"), "before"); + writeFileSync(join(source, "tree/obsolete.txt"), "obsolete"); + generateRuntimeManifest(stage, "2.0.0", ["tree"], source); + writeFileSync(join(source, "tree/current.txt"), "after"); + rmSync(join(source, "tree/obsolete.txt")); + const result = generateRuntimeManifest(stage, "2.0.0", ["tree"], source); + assert.equal(readFileSync(join(stage, "tree/current.txt"), "utf8"), "after"); + assert.equal(readFileSync(join(source, "tree/current.txt"), "utf8"), "after"); + assert.equal(existsSync(join(stage, "tree/obsolete.txt")), false); + assert.deepEqual(result.files.map((entry) => entry.path), ["tree/current.txt"]); + })); + + for (const linkParent of [false, true]) { + it(`rejects a matching ${linkParent ? "parent-directory" : "file"} symlink during verification`, () => temporary((directory) => { + const runtime = join(directory, "Contents/Resources/runtime"); + mkdirSync(join(runtime, "tree"), { recursive: true }); + writeFileSync(join(runtime, "tree/payload"), "verified bytes"); + const manifest = generateRuntimeManifest(runtime, "2.0.0", ["tree"], runtime); + writeFileSync(join(directory, "Contents/Resources/runtime-manifest.json"), formatRuntimeManifest(manifest)); + assert.equal(verifyRuntimeManifest(directory), true); + const original = join(runtime, linkParent ? "tree" : "tree/payload"); + const outside = join(directory, "outside"); + renameSync(original, outside); + symlinkSync(outside, original); + assert.equal(verifyRuntimeManifest(directory), false); + })); + } + + it("refuses to refresh a staging path through a symlink without changing the linked data", () => temporary((directory) => { + const source = join(directory, "source"); + const stage = join(directory, "stage"); + const outside = join(directory, "outside"); + mkdirSync(join(source, "src/lib"), { recursive: true }); + mkdirSync(join(outside, "lib"), { recursive: true }); + mkdirSync(stage); + writeFileSync(join(source, "src/lib/file"), "new"); + writeFileSync(join(outside, "lib/file"), "do not overwrite"); + symlinkSync(outside, join(stage, "src")); + assert.throws(() => generateRuntimeManifest(stage, "2.0.0", ["src/lib"], source), /Symbolic link/); + assert.equal(readFileSync(join(outside, "lib/file"), "utf8"), "do not overwrite"); + })); + + it("rejects a valid old manifest version instead of reporting the current release", () => temporary((directory) => { + const fixture = packageFixture(directory); + fixture.manifest.version = "1.0.0"; + writeFileSync(fixture.manifestPath, formatRuntimeManifest(fixture.manifest)); + const result = spawnSync(process.execPath, [join(directory, "src/package-macos-app.mjs"), "--check"], { + encoding: "utf8", env: { ...process.env, WOR_MACOS_APP_ROOT: fixture.app }, + }); + assert.notEqual(result.status, 0); + assert.doesNotMatch(result.stdout, /runtime is current/); + })); + + it("detects stale same-version source and repairs it with the actual package CLI", () => temporary((directory) => { + const fixture = packageFixture(directory); + writeFileSync(join(directory, "payload.txt"), "new source at the same version"); + const run = (mode) => spawnSync(process.execPath, [join(directory, "src/package-macos-app.mjs"), mode], { + encoding: "utf8", env: { ...process.env, WOR_MACOS_APP_ROOT: fixture.app }, + }); + assert.notEqual(run("--check").status, 0); + const write = run("--write"); + assert.equal(write.status, 0, write.stderr); + assert.equal(readFileSync(join(fixture.runtime, "payload.txt"), "utf8"), "new source at the same version"); + const check = run("--check"); + assert.equal(check.status, 0, check.stderr); + })); + + it("propagates a failed macOS build from version:set", () => temporary((directory) => { + for (const path of [ + "src/set-version.mjs", "src/sync-package-metadata.mjs", "src/lib/node-runtime.mjs", + "src/config/metadata.json", "src/macos-app/Contents/Info.plist", "README.md", "install-wor.sh", "package.json", + ]) copySource(path, directory); + writeFileSync(join(directory, "src/build-release.mjs"), 'console.error("mock build failure"); process.exit(42);\n'); + const result = spawnSync(process.execPath, [join(directory, "src/set-version.mjs"), "2.0.1"], { encoding: "utf8" }); + assert.notEqual(result.status, 0); + assert.match(result.stderr, /mock build failure/); + assert.doesNotMatch(result.stdout, /Updated WoR-Flasher version surfaces/); + })); +}); + +describe("Publication defaults and optional configuration", () => { + it("uses the publishing repository for bootstrap and updates, while retaining upstream attribution", () => { + const metadata = readProjectMetadata(); + assert.equal(metadata.systemDefaults.repoSlug, "blackoutsecure/wor-flasher"); + const hook = readFileSync(join(root, "install-wor-hook.sh"), "utf8"); + assert.ok(hook.includes(`WOR_HOOK_REPOSITORY:=https://github.com/${metadata.systemDefaults.repoSlug}.git`)); + assert.match(readFileSync(join(root, "NOTICE"), "utf8"), /https:\/\/github.com\/Botspot\/wor-flasher/); + }); + + it("enforces macOS 13 and documents the current security-supported line", () => { + const metadata = readProjectMetadata(); + assert.equal(metadata.product.minimumMacosVersion, "13.0"); + assert.match(readFileSync(join(root, "src/macos-app/Contents/Info.plist"), "utf8"), /<key>LSMinimumSystemVersion<\/key>\s*<string>13\.0<\/string>/); + const line = metadata.product.version.split(".").slice(0, 2).join("\\."); + assert.match(readFileSync(join(root, "SECURITY.md"), "utf8"), new RegExp(`\\| ${line}\\.x\\s*\\| Yes\\s*\\|`)); + }); + + it("stages and removes the Pi 4 UEFI Shell handoff independently of answer-file options", () => temporary((directory) => { + mkdirSync(join(directory, "peinstaller/winpe/2"), { recursive: true }); + const result = spawnSync("bash", ["-c", ` + source "$DIRECTORY/install-wor.sh" source >/dev/null + RPI_MODEL=4 PI4_UEFI_SHELL_UNLOCK=1 PI4_AUTO_DISABLE_3GB=0 OOBE_NETWORK_BYPASS=0 + WINDOWS_ACCOUNT_SETUP=0 WINDOWS_LOCALE_SETUP=0 + prepare_uefi_shell() { mkdir -p uefi-shell; printf "mock shell" > uefi-shell/Shell.efi; } + configure_pe_prefinalize || exit 1 + [ -s peinstaller/winpe/2/scripts/prefinalize.cmd ] && + [ -s peinstaller/winpe/2/scripts/Shell.efi ] && + [ ! -e peinstaller/winpe/2/scripts/unattend.xml ] || exit 2 + PI4_UEFI_SHELL_UNLOCK=0 + configure_pe_prefinalize || exit 3 + [ ! -e peinstaller/winpe/2/scripts ] || exit 4 + `], { cwd: directory, encoding: "utf8", env: { ...process.env, DIRECTORY: root, NO_UPDATE: "1" } }); + assert.equal(result.status, 0, result.stdout + result.stderr); + const batch = readFileSync(join(root, "config-templates/prefinalize.cmd"), "utf8"); + const missingAnswer = batch.match(/if not exist "%answerSource%" \([\s\S]*?\n\)/); + assert.ok(missingAnswer); + assert.match(missingAnswer[0], /goto :stage_shell/); + assert.doesNotMatch(missingAnswer[0], /goto :end/); + })); +}); diff --git a/tests/release-workflow.test.mjs b/tests/release-workflow.test.mjs new file mode 100644 index 0000000..9ca6e51 --- /dev/null +++ b/tests/release-workflow.test.mjs @@ -0,0 +1,108 @@ +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { spawnSync } from "node:child_process"; +import { readProjectMetadata, readRuntimePaths } from "../src/lib/node-runtime.mjs"; + +const root = fileURLToPath(new URL("../", import.meta.url)); +const release = readFileSync(join(root, ".github/workflows/release.yml"), "utf8"); +const checks = readFileSync(join(root, ".github/workflows/shellcheck.yml"), "utf8"); +const harness = readFileSync(join(root, "tests/run-tests.sh"), "utf8"); + +describe("Release workflow prerequisites", () => { + it("resolves the canonical version in preparation, validation, and packaging", () => { + const lookups = [...release.matchAll(/^\s*(version="\$\(.+\)")$/gm)]; + assert.equal(lookups.length, 3); + for (const [, lookup] of lookups) { + const result = spawnSync("bash", ["-c", `set -euo pipefail\n${lookup}\nprintf '%s' "$version"`], { + cwd: root, + encoding: "utf8", + }); + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout, readProjectMetadata().product.version); + } + }); + + it("scopes version-commit permissions to the prepare job", () => { + assert.match(release, /^permissions:\n contents: read$/m); + const prepare = release.slice(release.indexOf(" prepare:"), release.indexOf(" validate-linux:")); + assert.match(prepare, / permissions:\n contents: write/); + assert.match(prepare, /Co-authored-by: Copilot/); + }); + + it("installs pv before integration checks on both release and push workflows", () => { + for (const [workflow, testStep] of [ + [checks, "- name: Run one-model dry-run integration suite"], + [release, "- name: Run Linux test suite"], + ]) { + const prerequisite = workflow.search(/sudo apt-get install[^\n]*\bpv\b/); + assert.ok(prerequisite >= 0 && prerequisite < workflow.indexOf(testStep)); + } + assert.match(harness, /command -v pv >\/dev\/null \|\| die /); + }); + + it("runs CI for standalone GUI helper and canonical metadata changes", () => { + assert.equal((checks.match(/ - "\*\*\.js"/g) || []).length, 2); + assert.equal((checks.match(/ - "src\/config\/\*\*"/g) || []).length, 2); + }); + + it("includes source attribution and license notices in both runtime distributions", () => { + const paths = readRuntimePaths(); + for (const document of ["README.md", "LICENSE", "NOTICE"]) { + assert.ok(paths.includes(document), `${document} is missing from the runtime manifest`); + } + }); +}); + +describe("CI loop-device verification", () => { + for (const fails of [false, true]) { + it(fails ? "fails explicitly when privileged geometry inspection fails" : "reads partition geometry using the same privileges as the flash", () => { + const directory = mkdtempSync(join(tmpdir(), "wor-release-layout-")); + try { + writeFileSync(join(directory, "sudo"), `#!/bin/bash +[ "$1" == parted ] || exit 99 +shift +export WOR_TEST_PRIVILEGED=1 +exec "$WOR_TEST_BIN/parted" "$@" +`, { mode: 0o755 }); + writeFileSync(join(directory, "parted"), `#!/bin/bash +[ "$WOR_TEST_PRIVILEGED" == 1 ] || { printf "permission denied\\n" >&2; exit 1; } +[ "$WOR_TEST_FAIL" != 1 ] || { printf "mock geometry failure\\n" >&2; exit 42; } +printf 'BYT;\\n/dev/wor-test-loop:52428800s:file:512:512:gpt:fixture:;\\n1:2048s:3147775s:3145728s:fat32:WOR_BOOT:boot, esp;\\n2:3147776s:40013823s:36866048s::WOR_INSTALL:msftdata;\\n' +`, { mode: 0o755 }); + const start = harness.indexOf('partition_layout="$(sudo parted'); + const stop = harness.indexOf('\nboot_mount=', start); + assert.ok(start >= 0 && stop > start); + const result = spawnSync("bash", ["-c", ` + die() { printf '%s\\n' "$*" >&2; exit 1; } + DEV_INSTALL=/dev/wor-test-loop + [ "$(command -v sudo)" == "$WOR_TEST_BIN/sudo" ] || exit 99 + ${harness.slice(start, stop)} + printf '%s|%s\\n' "$partition_count" "$boot_filesystem" + `], { + encoding: "utf8", + env: { + ...process.env, + PATH: `${directory}:${process.env.PATH}`, + WOR_TEST_BIN: directory, + WOR_TEST_PRIVILEGED: "0", + WOR_TEST_FAIL: fails ? "1" : "0", + }, + }); + if (fails) { + assert.equal(result.status, 1); + assert.match(result.stderr, /Could not inspect the test loop-device partition layout/); + assert.equal(result.stdout, ""); + } else { + assert.equal(result.status, 0, result.stderr); + assert.equal(result.stdout.trim(), "2|fat32"); + } + } finally { + rmSync(directory, { recursive: true, force: true }); + } + }); + } +}); diff --git a/tests/run-linux-integration.sh b/tests/run-linux-integration.sh index 4c6caa0..4822b10 100755 --- a/tests/run-linux-integration.sh +++ b/tests/run-linux-integration.sh @@ -46,6 +46,7 @@ if docker run --rm --privileged \ -e TEST_DIR="$CONTAINER_TEST_DIR" \ -e TEST_UID="$TEST_UID" \ -e TEST_GID="$TEST_GID" \ + -e TEST_MODELS="${TEST_MODELS:-}" \ -e CONTAINER_TEST_DIR="$CONTAINER_TEST_DIR" \ "$IMAGE" \ bash -lc ' @@ -55,7 +56,7 @@ if docker run --rm --privileged \ apt-get update -qq printf " ... container: installing test dependencies\n" apt-get install -y -qq --no-install-recommends \ - aria2 bc cabextract ca-certificates chntpw coreutils dosfstools exfat-fuse exfatprogs findutils gawk genisoimage git grep passwd parted sed shellcheck sudo udftools unzip util-linux wget wimtools yad + aria2 bc cabextract ca-certificates chntpw coreutils curl dosfstools exfat-fuse exfatprogs findutils gawk genisoimage git grep jq passwd parted pv python3 sed shellcheck sudo udftools unzip util-linux wget wimtools yad printf " ... container: preparing test user %s:%s\n" "$TEST_UID" "$TEST_GID" if ! getent group "$TEST_GID" >/dev/null; then groupadd -g "$TEST_GID" wor-test diff --git a/tests/run-tests-gui.sh b/tests/run-tests-gui.sh index 5b72e9a..4a375dc 100755 --- a/tests/run-tests-gui.sh +++ b/tests/run-tests-gui.sh @@ -1,7 +1,7 @@ #!/bin/bash -# GUI test harness for wor-flasher. Creates fake drives through run-tests.sh, then -# launches install-wor-gui.sh with DRY_RUN=1 so no storage is written. +# GUI test harness for wor-flasher. On Linux it creates fake drives through +# run-tests.sh; on macOS it uses a real removable drive in DRY_RUN=1 mode. # # Usage: # ./tests/run-tests-gui.sh run the GUI walkthrough test @@ -17,23 +17,31 @@ REPO_DIR="$(script_dir "$TEST_SCRIPT_DIR/..")" info "== GUI test preflight ==" -if [ "$(uname -s)" != Linux ];then - skip "GUI walkthrough needs a Linux desktop host with loop-device support" - summary -fi -pass "Linux host detected" - -if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ];then - skip "GUI walkthrough needs DISPLAY or WAYLAND_DISPLAY" - summary -fi -pass "desktop display detected" - -if ! command -v yad >/dev/null ;then - skip "GUI walkthrough needs yad installed" - summary -fi -pass "yad is available" +case "$(uname -s)" in + Linux) + pass "Linux host detected" + if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ];then + skip "GUI walkthrough needs DISPLAY or WAYLAND_DISPLAY" + summary + fi + pass "desktop display detected" + if ! command -v yad >/dev/null ;then + skip "GUI walkthrough needs yad installed" + summary + fi + pass "yad is available" + ;; + Darwin) + command -v osascript >/dev/null || { skip "macOS GUI walkthrough needs osascript"; summary; } + pass "macOS native GUI is available" + [ -f "$REPO_DIR/partnership.png" ] || die "macOS GUI walkthrough needs partnership.png" + pass "macOS welcome image is available" + ;; + *) + skip "GUI walkthrough supports Linux and macOS hosts only" + summary + ;; +esac info "== GUI walkthrough ==" "$REPO_DIR/tests/run-tests.sh" --gui "$@" diff --git a/tests/run-tests-macos.sh b/tests/run-tests-macos.sh new file mode 100755 index 0000000..8934a94 --- /dev/null +++ b/tests/run-tests-macos.sh @@ -0,0 +1,415 @@ +#!/bin/bash + +#macOS-only checks for the WoR-Flasher test suite. This file is sourced by tests/run-tests.sh, +#never run standalone: it relies on REPO_DIR, pass(), fail(), skip() and run_in_engine() already +#being defined by the caller. It stages and exercises the real macOS app bundle and its native +#launcher script, so it can only run truthfully on a real macOS host. +# +#tests/run-tests.sh calls macos_app_and_launcher_checks() when "$(uname -s)" == Darwin, and +#otherwise emits one skip() per entry in MACOS_ONLY_TEST_NAMES so every skipped check is named +#and the reason ("requires macOS") is explicit in the test output and the final summary count. + +MACOS_ONLY_TEST_NAMES=( + "macOS app packaging and update paths preserve bounded trusted execution" + "packaged macOS engine loads without modifying its immutable runtime" + "macOS runtime bootstrap handles spaces and rejects a tampered active runtime" + "verified macOS runtime update promotes atomically and rolls back to the previous runtime" + "macOS runtime updater preserves pointers across downgrade, digest, and incomplete-archive failures" + "macOS runtime updates accept only newer numeric release versions" + "macOS runtime validation rejects digest, mode, and symlink substitutions" + "macOS runtime archive validation rejects symbolic links" + "version-order test detects an equal-version acceptance mutation" + "runtime-integrity test detects a removed digest guard" + "runtime-integrity test detects a removed symlink guard" + "archive-safety test detects a removed entry-type guard" + "runtime-selection test detects an active/previous priority mutation" + "macOS launcher startup status is source-testable and cleans up its state" + "macOS launcher inventories Homebrew once and installs only missing dependencies" + "macOS startup repairs missing tracked runtime assets without overwriting modifications" + "macOS launcher no longer updates a source checkout from a git remote" + "macOS app bundle metadata matches the shared name, version, and logo" +) + +macos_app_and_launcher_checks() { #Requires: real Darwin host. Stages release/macos/WoR-Flasher.app and exercises its launcher. + if command -v node >/dev/null ;then + node "$REPO_DIR/src/build-release.mjs" --platform=macos >/dev/null 2>&1 \ + || fail "generated macOS app could not be staged for launcher tests" + else + fail "node is required to stage the generated macOS app for launcher tests" + fi + launcher="$REPO_DIR/release/macos/WoR-Flasher.app/Contents/MacOS/WoR-Flasher" + node "$REPO_DIR/src/package-macos-app.mjs" --check >/dev/null 2>&1 \ + && grep -qF 'validate_runtime "$EMBEDDED_RUNTIME" "$EMBEDDED_MANIFEST"' "$launcher" \ + && grep -qF 'actual_digest="$(shasum -a 256 "$archive"' "$launcher" \ + && grep -qF 'archive_entries_are_safe "$archive"' "$launcher" \ + && grep -qF 'mv -f "$temporary" "$pointer"' "$launcher" \ + && grep -qF 'ln -sfn /usr/bin/osascript "$script_host"' "$REPO_DIR/src/lib/metadata.sh" \ + && grep -qF 'wor_osascript -l JavaScript' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qE '^[[:space:]]*osascript -l JavaScript' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'git -C "$REPO_DIR" restore --source=HEAD -- "${missing[@]}"' "$launcher" \ + && grep -qF 'brew install "${missing[@]}"' "$launcher" \ + && grep -qF "open 'https://brew.sh'" "$launcher" \ + && grep -qF "start_startup_window" "$launcher" \ + && grep -qF 'local icon_path="$RESOURCES_DIR/$BUNDLE_ICON_FILENAME"' "$launcher" \ + && grep -qF 'const controller = $.WorStartupController.alloc.init' "$launcher" \ + && grep -qF 'installed_formulae="$(brew list --formula 2>/dev/null)"' "$launcher" \ + && grep -qF '[ "$logo_source" -nt "$icon_resource" ]' "$launcher" \ + && grep -qF 'trap cleanup_startup EXIT INT TERM' "$launcher" \ + && grep -qF "startup_phase 'Checking for updates and repairs...'" "$launcher" \ + && grep -qF 'if [ -d "$CHECKOUT_DIR/.git" ];then' "$launcher" \ + && grep -qF 'set_runtime_paths "$CHECKOUT_DIR"' "$launcher" \ + && grep -qF 'export NO_UPDATE=1' "$launcher" \ + && ! grep -qE 'git (reset|clean)|brew upgrade|curl.+\|.+(ba)?sh' "$launcher" \ + && [ -x "$launcher" ] \ + && pass "macOS app packaging and update paths preserve bounded trusted execution" \ + || fail "macOS app packaging or update path is stale, incomplete, or destructive" + + #install-wor.sh's "source" early-return (before any argument parsing) is not exercised under + #set -e in this repo's own suite anywhere else; drop it here too so a harmless non-zero exit + #from an earlier defensive check does not mask this assertion. + if bash -c ' + DIRECTORY="$1/Contents/Resources/runtime" source "$1/Contents/Resources/runtime/install-wor.sh" source >/dev/null 2>&1 + source "$1/Contents/Resources/runtime/src/lib/metadata.sh" + [ "$WOR_FLASHER_VERSION" == "$(sed -n "s/^[[:space:]]*\"version\": \"\([^\"]*\)\".*/\1/p" "$1/Contents/Resources/runtime-manifest.json")" ] + [ ! -e "$1/Contents/Resources/runtime/cache" ] + ' _ "$REPO_DIR/release/macos/WoR-Flasher.app" ;then + pass "packaged macOS engine loads without modifying its immutable runtime" + else + fail "packaged macOS engine load failed or modified its immutable runtime" + fi + + launcher_test_dir="$(mktemp -d "${TMPDIR:-/tmp}/wor launcher state.XXXXXX")" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_SUPPORT_DIR="$launcher_test_dir/Application Support" bash -c ' + set -e + source "$1" + CHECKOUT_DIR="$2/missing checkout" + select_runtime + [ "$REPO_DIR" == "$EMBEDDED_RUNTIME" ] + bootstrap_embedded_runtime + select_runtime + case "$REPO_DIR" in "$WOR_APP_SUPPORT_DIR"/runtimes/*/runtime) ;; *) exit 1 ;; esac + validate_runtime "$REPO_DIR" "$RUNTIME_MANIFEST" + cp "$EMBEDDED_MANIFEST" "$RUNTIME_MANIFEST" + printf 'stale\n' >> "$REPO_DIR/install-wor.sh" + bootstrap_embedded_runtime + select_runtime + cmp -s "$RUNTIME_MANIFEST" "$EMBEDDED_MANIFEST" + printf "tampered\n" >> "$REPO_DIR/install-wor.sh" + select_runtime + [ "$REPO_DIR" == "$EMBEDDED_RUNTIME" ] + ' _ "$launcher" "$launcher_test_dir" ;then + pass "macOS runtime bootstrap handles spaces and rejects a tampered active runtime" + else + fail "macOS runtime bootstrap or tampered-runtime fallback failed" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_SUPPORT_DIR="$launcher_test_dir/support" WOR_CURL_BIN="$(command -v curl)" bash -c ' + set -e + source "$1" + metadata_value() { sed -n "s/.*\"$2\":\"\([^\"]*\)\".*/\1/p" "$1"; } + CHECKOUT_DIR="$2/no-checkout" + bootstrap_embedded_runtime + select_runtime + update_root="$2/update" + mkdir -p "$update_root/runtime" + cp -R "$EMBEDDED_RUNTIME/." "$update_root/runtime/" + sed "s/\"version\": \"[^\"]*\"/\"version\": \"9.9.9\"/" "$EMBEDDED_MANIFEST" > "$update_root/runtime-manifest.json" + tar -czf "$2/runtime.tar.gz" -C "$update_root" runtime runtime-manifest.json + update_digest="$(shasum -a 256 "$2/runtime.tar.gz" | awk "{print \$1}")" + printf "{\"version\":\"9.9.9\",\"url\":\"file://%s/runtime.tar.gz\",\"sha256\":\"%s\"}\n" "$2" "$update_digest" > "$2/metadata.json" + ask_to_continue() { return 0; } + WOR_CURL_BIN="$(command -v curl)" WOR_UPDATE_METADATA_URL="file://$2/metadata.json" WOR_ALLOW_INSECURE_UPDATE=1 check_for_runtime_update + [ "$(cat "$ACTIVE_RUNTIME_FILE")" == 9.9.9 ] + [ "$(cat "$PREVIOUS_RUNTIME_FILE")" == "$(manifest_version "$EMBEDDED_MANIFEST")" ] + select_runtime + [ "$(manifest_version "$RUNTIME_MANIFEST")" == 9.9.9 ] + printf "broken\n" >> "$REPO_DIR/install-wor.sh" + select_runtime + [ "$(manifest_version "$RUNTIME_MANIFEST")" == "$(manifest_version "$EMBEDDED_MANIFEST")" ] + ' _ "$launcher" "$launcher_test_dir" ;then + pass "verified macOS runtime update promotes atomically and rolls back to the previous runtime" + else + fail "verified macOS runtime promotion or rollback failed" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_SUPPORT_DIR="$launcher_test_dir/support" WOR_CURL_BIN="$(command -v curl)" bash -c ' + set -e + source "$1" + metadata_value() { sed -n "s/.*\"$2\":\"\([^\"]*\)\".*/\1/p" "$1"; } + CHECKOUT_DIR="$2/no-checkout" + bootstrap_embedded_runtime + select_runtime + active_before="$(cat "$ACTIVE_RUNTIME_FILE")" + prompts=0 + ask_to_continue() { prompts=$((prompts + 1)); return 0; } + for candidate in "$active_before" 0.9.9 ;do + printf "{\"version\":\"%s\",\"url\":\"file://%s/missing.tar.gz\",\"sha256\":\"%064d\"}\n" "$candidate" "$2" 0 > "$2/metadata.json" + WOR_CURL_BIN="$(command -v curl)" WOR_UPDATE_METADATA_URL="file://$2/metadata.json" WOR_ALLOW_INSECURE_UPDATE=1 check_for_runtime_update + done + [ "$prompts" -eq 0 ] + [ "$(cat "$ACTIVE_RUNTIME_FILE")" == "$active_before" ] + [ ! -e "$PREVIOUS_RUNTIME_FILE" ] + + mkdir -p "$2/incomplete/runtime" + printf "incomplete\n" > "$2/incomplete/runtime/README" + cp "$EMBEDDED_MANIFEST" "$2/incomplete/runtime-manifest.json" + tar -czf "$2/incomplete.tar.gz" -C "$2/incomplete" runtime runtime-manifest.json + incomplete_digest="$(shasum -a 256 "$2/incomplete.tar.gz" | awk "{print \$1}")" + + printf "{\"version\":\"9.9.8\",\"url\":\"file://%s/incomplete.tar.gz\",\"sha256\":\"%064d\"}\n" "$2" 0 > "$2/metadata.json" + WOR_CURL_BIN="$(command -v curl)" WOR_UPDATE_METADATA_URL="file://$2/metadata.json" WOR_ALLOW_INSECURE_UPDATE=1 check_for_runtime_update + printf "{\"version\":\"9.9.9\",\"url\":\"file://%s/incomplete.tar.gz\",\"sha256\":\"%s\"}\n" "$2" "$incomplete_digest" > "$2/metadata.json" + WOR_CURL_BIN="$(command -v curl)" WOR_UPDATE_METADATA_URL="file://$2/metadata.json" WOR_ALLOW_INSECURE_UPDATE=1 check_for_runtime_update + + [ "$prompts" -eq 2 ] + [ "$(cat "$ACTIVE_RUNTIME_FILE")" == "$active_before" ] + [ ! -e "$PREVIOUS_RUNTIME_FILE" ] + select_runtime + validate_runtime "$REPO_DIR" "$RUNTIME_MANIFEST" + ' _ "$launcher" "$launcher_test_dir" ;then + pass "macOS runtime updater preserves pointers across downgrade, digest, and incomplete-archive failures" + else + fail "macOS runtime updater changes state after a rejected update" + fi + rm -rf "$launcher_test_dir" + + if WOR_LAUNCHER_SOURCE_ONLY=1 bash -c ' + source "$1" + version_is_newer 1.0.3 1.0.2 + version_is_newer 1.1 1.0.99 + version_is_newer 2 1.99.99 + ! version_is_newer 1.0.2 1.0.2 + ! version_is_newer 1.0.1 1.0.2 + ! version_is_newer 1.0.2-beta 1.0.1 + ' _ "$launcher" ;then + pass "macOS runtime updates accept only newer numeric release versions" + else + fail "macOS runtime version ordering permits a downgrade or invalid release" + fi + + launcher_test_dir="$(mktemp -d)" + if WOR_LAUNCHER_SOURCE_ONLY=1 bash -c ' + set -e + source "$1" + cp -R "$EMBEDDED_RUNTIME" "$2/runtime" + cp "$EMBEDDED_MANIFEST" "$2/runtime-manifest.json" + validate_runtime "$2/runtime" "$2/runtime-manifest.json" + printf "tampered\n" >> "$2/runtime/install-wor.sh" + ! validate_runtime "$2/runtime" "$2/runtime-manifest.json" + cp "$EMBEDDED_RUNTIME/install-wor.sh" "$2/runtime/install-wor.sh" + chmod 644 "$2/runtime/install-wor.sh" + ! validate_runtime "$2/runtime" "$2/runtime-manifest.json" + rm "$2/runtime/install-wor.sh" + ln -s "$EMBEDDED_RUNTIME/install-wor.sh" "$2/runtime/install-wor.sh" + ! validate_runtime "$2/runtime" "$2/runtime-manifest.json" + ' _ "$launcher" "$launcher_test_dir" ;then + pass "macOS runtime validation rejects digest, mode, and symlink substitutions" + else + fail "macOS runtime validation accepts a tampered manifest entry" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + mkdir -p "$launcher_test_dir/link-archive" + ln -s /tmp "$launcher_test_dir/link-archive/escape" + tar -czf "$launcher_test_dir/link.tar.gz" -C "$launcher_test_dir/link-archive" escape + if WOR_LAUNCHER_SOURCE_ONLY=1 bash -c ' + source "$1" + ! archive_entries_are_safe "$2/link.tar.gz" + ' _ "$launcher" "$launcher_test_dir" ;then + pass "macOS runtime archive validation rejects symbolic links" + else + fail "macOS runtime archive validation accepts symbolic links" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + resources_dir="$REPO_DIR/release/macos/WoR-Flasher.app/Contents/Resources" + info_plist="$REPO_DIR/release/macos/WoR-Flasher.app/Contents/Info.plist" + + cp "$launcher" "$launcher_test_dir/version-mutant" + perl -0pi -e 's/done\n return 1\n}\n\nvalidate_runtime/done\n return 0\n}\n\nvalidate_runtime/' "$launcher_test_dir/version-mutant" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_RESOURCES_DIR="$resources_dir" WOR_APP_INFO_PLIST="$info_plist" bash -c ' + source "$1" + version_is_newer 1.0.2 1.0.2 + ' _ "$launcher_test_dir/version-mutant" ;then + pass "version-order test detects an equal-version acceptance mutation" + else + fail "version-order mutation did not alter the focused test behavior" + fi + + cp "$launcher" "$launcher_test_dir/integrity-mutant" + sed 's/\[ "$actual_digest" == "$expected_digest" \] && \[ "$actual_mode" == "$expected_mode" \]/[ "$actual_mode" == "$expected_mode" ]/' "$launcher_test_dir/integrity-mutant" > "$launcher_test_dir/integrity-mutant.tmp" + mv "$launcher_test_dir/integrity-mutant.tmp" "$launcher_test_dir/integrity-mutant" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_RESOURCES_DIR="$resources_dir" WOR_APP_INFO_PLIST="$info_plist" bash -c ' + set -e + source "$1" + cp -R "$EMBEDDED_RUNTIME" "$2/runtime" + cp "$EMBEDDED_MANIFEST" "$2/runtime-manifest.json" + printf "tampered\n" >> "$2/runtime/install-wor.sh" + validate_runtime "$2/runtime" "$2/runtime-manifest.json" + ' _ "$launcher_test_dir/integrity-mutant" "$launcher_test_dir" ;then + pass "runtime-integrity test detects a removed digest guard" + else + fail "digest mutation did not alter the focused test behavior" + fi + + cp "$launcher" "$launcher_test_dir/symlink-mutant" + sed 's/\[ -f "$runtime_root\/$path" \] && \[ ! -L "$runtime_root\/$path" \]/[ -f "$runtime_root\/$path" ]/' "$launcher_test_dir/symlink-mutant" > "$launcher_test_dir/symlink-mutant.tmp" + mv "$launcher_test_dir/symlink-mutant.tmp" "$launcher_test_dir/symlink-mutant" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_RESOURCES_DIR="$resources_dir" WOR_APP_INFO_PLIST="$info_plist" bash -c ' + set -e + source "$1" + cp -R "$EMBEDDED_RUNTIME" "$2/symlink-runtime" + cp "$EMBEDDED_MANIFEST" "$2/symlink-manifest.json" + rm "$2/symlink-runtime/install-wor.sh" + ln -s "$EMBEDDED_RUNTIME/install-wor.sh" "$2/symlink-runtime/install-wor.sh" + validate_runtime "$2/symlink-runtime" "$2/symlink-manifest.json" + ' _ "$launcher_test_dir/symlink-mutant" "$launcher_test_dir" ;then + pass "runtime-integrity test detects a removed symlink guard" + else + fail "symlink mutation did not alter the focused test behavior" + fi + + cp "$launcher" "$launcher_test_dir/archive-mutant" + sed 's/substr($1, 1, 1) != "-" && substr($1, 1, 1) != "d"/0/' "$launcher_test_dir/archive-mutant" > "$launcher_test_dir/archive-mutant.tmp" + mv "$launcher_test_dir/archive-mutant.tmp" "$launcher_test_dir/archive-mutant" + mkdir -p "$launcher_test_dir/mutation-link-archive" + ln -s /tmp "$launcher_test_dir/mutation-link-archive/escape" + tar -czf "$launcher_test_dir/mutation-link.tar.gz" -C "$launcher_test_dir/mutation-link-archive" escape + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_RESOURCES_DIR="$resources_dir" WOR_APP_INFO_PLIST="$info_plist" bash -c ' + source "$1" + archive_entries_are_safe "$2/mutation-link.tar.gz" + ' _ "$launcher_test_dir/archive-mutant" "$launcher_test_dir" ;then + pass "archive-safety test detects a removed entry-type guard" + else + fail "archive entry-type mutation did not alter the focused test behavior" + fi + + awk ' + /selected=.*ACTIVE_RUNTIME_FILE/ { sub("ACTIVE_RUNTIME_FILE", "PREVIOUS_RUNTIME_FILE") } + /\|\| selected=.*PREVIOUS_RUNTIME_FILE/ { sub("PREVIOUS_RUNTIME_FILE", "ACTIVE_RUNTIME_FILE") } + { print } + ' "$launcher" > "$launcher_test_dir/fallback-mutant" + if WOR_LAUNCHER_SOURCE_ONLY=1 WOR_APP_SUPPORT_DIR="$launcher_test_dir/support" WOR_APP_RESOURCES_DIR="$resources_dir" WOR_APP_INFO_PLIST="$info_plist" bash -c ' + set -e + source "$1" + CHECKOUT_DIR="$2/no-checkout" + bootstrap_embedded_runtime + mkdir -p "$RUNTIMES_DIR/9.9.9" + cp -R "$EMBEDDED_RUNTIME" "$RUNTIMES_DIR/9.9.9/runtime" + sed "s/\"version\": \"[^\"]*\"/\"version\": \"9.9.9\"/" "$EMBEDDED_MANIFEST" > "$RUNTIMES_DIR/9.9.9/runtime-manifest.json" + write_runtime_pointer "$PREVIOUS_RUNTIME_FILE" 9.9.9 + select_runtime + [ "$(manifest_version "$RUNTIME_MANIFEST")" == 9.9.9 ] + ' _ "$launcher_test_dir/fallback-mutant" "$launcher_test_dir" ;then + pass "runtime-selection test detects an active/previous priority mutation" + else + fail "fallback-order mutation did not alter the focused test behavior" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + if TMPDIR="$launcher_test_dir" WOR_LAUNCHER_SOURCE_ONLY=1 bash -c ' + source "$1" + [ -z "$(find "$2" -mindepth 1 -print -quit)" ] || exit 1 + STARTUP_STATUS="$2/status" + STARTUP_DONE="$2/status.done" + startup_phase "Checking dependencies..." + [ "$(cat "$STARTUP_STATUS")" == "Checking dependencies..." ] || exit 1 + cleanup_startup + [ ! -e "$STARTUP_STATUS" ] && [ ! -e "$STARTUP_DONE" ] + ' _ "$launcher" "$launcher_test_dir" ;then + pass "macOS launcher startup status is source-testable and cleans up its state" + else + fail "macOS launcher startup status leaks state or runs during source-only tests" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + if TMPDIR="$launcher_test_dir" WOR_LAUNCHER_SOURCE_ONLY=1 BREW_CALLS="$launcher_test_dir/brew-calls" bash -c ' + source "$1" + WOR_MACOS_BREW_FORMULAE=(git jq wimlib) + brew() { + printf "%s\n" "$*" >> "$BREW_CALLS" + if [ "$1 $2" == "list --formula" ];then + printf "git\n" + fi + } + ask_to_continue() { [ "$2" == Install ]; } + prepare_dependencies || exit 1 + [ "$(grep -c "^list --formula$" "$BREW_CALLS")" -eq 1 ] || exit 1 + [ "$(grep -c "^install jq wimlib$" "$BREW_CALLS")" -eq 1 ] || exit 1 + [ "$(wc -l < "$BREW_CALLS" | tr -d " ")" -eq 2 ] || exit 1 + [ "$HOMEBREW_NO_AUTO_UPDATE" == 1 ] + ' _ "$launcher" ;then + pass "macOS launcher inventories Homebrew once and installs only missing dependencies" + else + fail "macOS launcher repeats Homebrew inventory or installs the wrong dependencies" + fi + rm -rf "$launcher_test_dir" + + launcher_test_dir="$(mktemp -d)" + repair_checkout="$launcher_test_dir/checkout" + mkdir -p "$repair_checkout/assets" + printf 'original script\n' > "$repair_checkout/install-wor.sh" + printf 'next steps\n' > "$repair_checkout/assets/next-steps.png" + git -C "$repair_checkout" init -q + git -C "$repair_checkout" add install-wor.sh assets/next-steps.png + git -C "$repair_checkout" -c user.name=Test -c user.email=test@example.com commit -qm fixture + printf 'modified script\n' > "$repair_checkout/install-wor.sh" + rm "$repair_checkout/assets/next-steps.png" + if WOR_LAUNCHER_SOURCE_ONLY=1 bash -c ' + set -e + source "$1" + REPO_DIR="$2" + ask_to_continue() { [ "$2" == Repair ]; } + show_error() { printf "unexpected error: %s\n" "$1" >&2; return 1; } + repair_missing_files + [ "$(cat "$REPO_DIR/assets/next-steps.png")" == "next steps" ] + [ "$(cat "$REPO_DIR/install-wor.sh")" == "modified script" ] + ' _ "$launcher" "$repair_checkout" ;then + pass "macOS startup repairs missing tracked runtime assets without overwriting modifications" + else + fail "macOS startup repair misses assets or overwrites a modified file" + fi + rm -rf "$launcher_test_dir" + + #the git source updater was removed outright; only local repair and the verified runtime update remain + if WOR_LAUNCHER_SOURCE_ONLY=1 bash -c ' + source "$1" + declare -F check_for_update >/dev/null && exit 1 + declare -F repair_missing_files >/dev/null || exit 1 + declare -F check_for_runtime_update >/dev/null || exit 1 + ' _ "$launcher" \ + && ! grep -qF 'ls-remote' "$launcher" \ + && ! grep -qF 'merge --ff-only' "$launcher" ;then + pass "macOS launcher no longer updates a source checkout from a git remote" + else + fail "macOS launcher still carries the git source updater" + fi + + metadata_name="$(run_in_engine 'printf %s "$WOR_FLASHER_NAME"')" + metadata_version="$(run_in_engine 'printf %s "$WOR_FLASHER_VERSION"')" + metadata_logo="$(run_in_engine 'printf %s "$WOR_LOGO_FILENAME"')" + metadata_assets="$(run_in_engine 'printf %s "$WOR_ASSETS_DIRNAME"')" + metadata_icon="$(run_in_engine 'printf %s "$WOR_ICON_FILENAME"')" + info_plist="$REPO_DIR/release/macos/WoR-Flasher.app/Contents/Info.plist" + plutil -lint "$info_plist" >/dev/null 2>&1 \ + && [ "$(plutil -extract CFBundleDisplayName raw -o - "$info_plist")" == "$metadata_name" ] \ + && [ "$(plutil -extract CFBundleExecutable raw -o - "$info_plist")" == "$metadata_name" ] \ + && [ "$(plutil -extract CFBundleIconFile raw -o - "$info_plist")" == "$metadata_icon" ] \ + && [ "$(plutil -extract CFBundleIconName raw -o - "$info_plist")" == "$metadata_name" ] \ + && [ -s "$REPO_DIR/release/macos/WoR-Flasher.app/Contents/Resources/$metadata_icon" ] \ + && [ -f "$REPO_DIR/release/macos/WoR-Flasher.app/Contents/Resources/$metadata_logo" ] \ + && cmp -s "$REPO_DIR/$metadata_assets/$metadata_logo" "$REPO_DIR/release/macos/WoR-Flasher.app/Contents/Resources/$metadata_logo" \ + && [ "$(plutil -extract CFBundleName raw -o - "$info_plist")" == "$metadata_name" ] \ + && [ "$(plutil -extract CFBundleShortVersionString raw -o - "$info_plist")" == "$metadata_version" ] \ + && [ "$(plutil -extract CFBundleVersion raw -o - "$info_plist")" == "$metadata_version" ] \ + && pass "macOS app bundle metadata matches the shared name, version, and logo" \ + || fail "macOS app bundle metadata differs from the shared name, version, or logo" +} diff --git a/tests/run-tests.sh b/tests/run-tests.sh index e107bc4..9c0944a 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -5,9 +5,10 @@ # #Usage: # ./tests/run-tests.sh run the automated suite; uses Docker for Linux integration on non-Linux hosts when available +# ./tests/run-tests.sh --macos-auth run only mocked macOS finalizer authorization tests; no sudo or disks # ./tests/run-tests.sh --walkthrough create fake drives, then run the CLI interactively -# ./tests/run-tests.sh --gui create fake drives, then launch the GUI (needs a display) -# ./tests/run-tests-gui.sh run the GUI walkthrough test with display preflight +# ./tests/run-tests.sh --gui launch the GUI in DRY_RUN mode (Linux creates fake drives; macOS needs a removable drive) +# ./tests/run-tests-gui.sh run the GUI walkthrough with host-specific preflight # ./tests/run-tests.sh --full also download the real Windows image (several GB) # ./tests/run-tests.sh --keep leave the fake drives and downloads in place afterwards # ./tests/run-tests.sh --clean remove the test workspace and detach fake drives @@ -20,9 +21,9 @@ source "$TEST_SCRIPT_DIR/test-lib.sh" REPO_DIR="$(script_dir "$TEST_SCRIPT_DIR/..")" -#Source-checkout updates are opt-in. Export this explicitly for every invocation below, -#including sourced library functions. The self-updater test further down deliberately opts in -#against a disposable clone, never against this working tree. +#Source-checkout updates are enabled by default. Disable them explicitly for every test +#invocation, including sourced library functions. The self-updater test further down +#deliberately re-enables updates against a disposable clone, never against this working tree. export NO_UPDATE=1 #Everything this script creates lives here. It is listed in .gitignore. @@ -76,14 +77,67 @@ cleanup() { static_checks() { info "== Static checks ==" - git -C "$REPO_DIR" diff --check >/dev/null 2>&1 \ - && pass "working tree has no whitespace errors" || fail "working tree has whitespace errors" - for f in install-wor.sh install-wor-gui.sh terminal-run ;do + #a git that cannot read the repo (e.g. an unusable checkout in a container) is not a whitespace error + if ! command -v git >/dev/null || ! git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 ;then + skip "git cannot read this checkout; cannot check for whitespace errors" + else + git -C "$REPO_DIR" diff --check >/dev/null 2>&1 \ + && pass "working tree has no whitespace errors" || fail "working tree has whitespace errors" + fi + for f in src/lib/metadata.sh src/lib/dependencies.sh src/lib/paths.sh src/lib/cleanup.sh src/lib/gui.sh install-wor.sh install-wor-gui.sh install-wor-hook.sh 'src/macos-app/Contents/MacOS/WoR-Flasher' ;do bash -n "$REPO_DIR/$f" 2>/dev/null && pass "$f parses" || fail "$f has a syntax error" done + jq empty "$REPO_DIR/src/config/metadata.json" >/dev/null 2>&1 \ + && jq empty "$REPO_DIR/src/config/metadata.schema.json" >/dev/null 2>&1 \ + && pass "project metadata JSON and schema parse" \ + || fail "project metadata JSON or schema is invalid" + grep -qF 'readRuntimePaths' "$REPO_DIR/src/build-release.mjs" \ + && grep -qF '"runtimePaths"' "$REPO_DIR/src/config/metadata.json" \ + && grep -qF '"pe:check": "node src/check-pe-installer.mjs --check"' "$REPO_DIR/package.json" \ + && grep -qF '"pe:update": "node src/check-pe-installer.mjs --write-latest"' "$REPO_DIR/package.json" \ + && grep -qF 'npm run package:all' "$REPO_DIR/.github/workflows/release.yml" \ + && grep -qF 'release/linux/wor-flasher' "$REPO_DIR/.github/workflows/release.yml" \ + && grep -qF 'release/macos/WoR-Flasher.app' "$REPO_DIR/.github/workflows/release.yml" \ + && pass "release tooling shares the project metadata runtime path manifest" \ + || fail "release tooling duplicates the runtime path list" + release_package_line="$(grep -nF ' - name: Package release artifacts' "$REPO_DIR/.github/workflows/release.yml" | cut -d: -f1)" + release_tag_line="$(grep -nF ' - name: Create immutable release tag' "$REPO_DIR/.github/workflows/release.yml" | cut -d: -f1)" + release_publish_line="$(grep -nF ' - name: Publish GitHub Release' "$REPO_DIR/.github/workflows/release.yml" | cut -d: -f1)" + if [ -n "$release_package_line" ] && [ -n "$release_tag_line" ] && [ -n "$release_publish_line" ] \ + && [ "$release_package_line" -lt "$release_tag_line" ] && [ "$release_tag_line" -lt "$release_publish_line" ] ;then + pass "release artifacts are packaged before the immutable tag is pushed" + else + fail "release workflow mutates the repository before packaging succeeds" + fi + if command -v node >/dev/null ;then + node --check "$REPO_DIR/src/build-release.mjs" >/dev/null 2>&1 \ + && node --check "$REPO_DIR/src/check-pe-installer.mjs" >/dev/null 2>&1 \ + && node --check "$REPO_DIR/src/sync-package-metadata.mjs" >/dev/null 2>&1 \ + && node --check "$REPO_DIR/src/updater.mjs" >/dev/null 2>&1 \ + && node --check "$REPO_DIR/src/package-macos-app.mjs" >/dev/null 2>&1 \ + && node --check "$REPO_DIR/src/set-version.mjs" >/dev/null 2>&1 \ + && node --check "$REPO_DIR/src/lib/node-runtime.mjs" >/dev/null 2>&1 \ + && pass "src/*.mjs and shared Node library scripts parse cleanly" || fail "Node tooling scripts have syntax errors" + if node --test "$REPO_DIR"/tests/*.test.mjs >/dev/null 2>&1 ;then + pass "Node.js unit test suites passed" + else + fail "Node.js unit test suite failed" + fi + release_check_tmp="$(mktemp -d "${TMPDIR:-/tmp}/wor-release-check-test.XXXXXX")" + if TMPDIR="$release_check_tmp" node "$REPO_DIR/src/build-release.mjs" --check --platform=invalid >/dev/null 2>&1 ;then + fail "release tooling accepted an unsupported platform" + elif find "$release_check_tmp" -mindepth 1 -print -quit | grep -q . ;then + fail "failed release check left temporary staging files behind" + else + pass "failed release checks reject invalid input and clean temporary staging" + fi + rm -rf "$release_check_tmp" + else + skip "node is not installed; skipping release-tool syntax check" + fi if command -v shellcheck >/dev/null ;then - shellcheck --severity=error "$REPO_DIR"/install-wor.sh "$REPO_DIR"/install-wor-gui.sh "$REPO_DIR"/terminal-run >/dev/null 2>&1 \ + shellcheck --severity=error "$REPO_DIR"/src/lib/metadata.sh "$REPO_DIR"/src/lib/dependencies.sh "$REPO_DIR"/src/lib/paths.sh "$REPO_DIR"/src/lib/cleanup.sh "$REPO_DIR"/src/lib/gui.sh "$REPO_DIR"/install-wor.sh "$REPO_DIR"/install-wor-gui.sh "$REPO_DIR"/install-wor-hook.sh "$REPO_DIR"/src/macos-app/Contents/MacOS/WoR-Flasher >/dev/null 2>&1 \ && pass "shellcheck reports no errors" || fail "shellcheck reports errors" else skip "shellcheck is not installed" @@ -96,15 +150,2124 @@ static_checks() { pass "deprecated updater sentinel file hook is not referenced" fi - grep -qF 'register_mount_cleanup "$PWD/isomount"' "$REPO_DIR/install-wor.sh" \ + grep -qF 'register_device_cleanup "$ISO_DEVICE"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'register_mount_cleanup "$isomount"' "$REPO_DIR/install-wor.sh" \ && grep -qF 'register_mount_cleanup "$mntpnt/bootpart"' "$REPO_DIR/install-wor.sh" \ && grep -qF 'register_mount_cleanup "$mntpnt/winpart"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'diskutil unmount force "$mountpoint"' "$REPO_DIR/src/lib/cleanup.sh" \ + && ! grep -qF 'sudo diskutil unmount force "$mountpoint"' "$REPO_DIR/src/lib/cleanup.sh" \ + && grep -qF 'hdiutil detach "$mountpoint"' "$REPO_DIR/src/lib/cleanup.sh" \ + && ! grep -qF 'sudo hdiutil detach' "$REPO_DIR/src/lib/cleanup.sh" \ + && ! grep -qF 'sudo hdiutil detach' "$REPO_DIR/install-wor.sh" \ && pass "all temporary mounts use the shared cleanup handler" \ || fail "a temporary mount bypasses the shared cleanup handler" + grep -qF 'if [ "$RUN_MODE" != gui ] && ! command sudo -n -v >/dev/null 2>&1 && ! sudo -v >/dev/null 2>&1;then' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Administrator authentication failed or was canceled.' "$REPO_DIR/install-wor.sh" \ + && pass "macOS authenticates administrator access before partitioning" \ + || fail "macOS does not authenticate administrator access before partitioning" + + grep -qF 'require_free_space()' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'required_download_space' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'mkdir -p "$DL_DIR"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'require_free_space "$required_download_space" "$DL_DIR"' "$REPO_DIR/install-wor.sh" \ + && pass "download directory free-space check runs before downloads" \ + || fail "download directory free-space check is missing" + + #prove the behaviour rather than the wording: winfiles_from_iso_* is also matched by winfiles_* + cache_clear_dir="$(mktemp -d)" + mkdir -p "$cache_clear_dir/dl/peinstaller" "$cache_clear_dir/dl/driverpackage" \ + "$cache_clear_dir/dl/pi4-uefipackage" "$cache_clear_dir/dl/winfiles_22631_en-us" \ + "$cache_clear_dir/dl/winfiles_from_iso_22631_en-us" "$cache_clear_dir/dl/keep-me" "$cache_clear_dir/repo/cache" + ( + cd "$cache_clear_dir/dl" || exit 1 + #shellcheck disable=SC1090 + DIRECTORY="$cache_clear_dir/repo" source "$REPO_DIR/install-wor.sh" source >/dev/null 2>&1 + DIRECTORY="$cache_clear_dir/repo" clear_cached_components >/dev/null 2>&1 + ) + [ ! -e "$cache_clear_dir/dl/winfiles_22631_en-us" ] \ + && [ ! -e "$cache_clear_dir/dl/winfiles_from_iso_22631_en-us" ] \ + && [ ! -e "$cache_clear_dir/dl/peinstaller" ] \ + && [ ! -e "$cache_clear_dir/dl/driverpackage" ] \ + && [ ! -e "$cache_clear_dir/dl/pi4-uefipackage" ] \ + && [ -d "$cache_clear_dir/dl/keep-me" ] \ + && [ -d "$cache_clear_dir/repo/cache" ] \ + && pass "USE_CACHE=0 clears extracted Windows image caches" \ + || fail "USE_CACHE=0 leaves extracted Windows image caches in place" + rm -rf "$cache_clear_dir" + + grep -qF '[ -z "$USE_CACHE" ] && USE_CACHE=1' "$REPO_DIR/install-wor.sh" \ + && pass "validated cache reuse is the default" \ + || fail "validated cache reuse is not the default" + + grep -qF "printf 'Downloaded files\\t%s\\n' \"\$(cache_mode_label \"\$USE_CACHE\")\"" "$REPO_DIR/install-wor.sh" \ + && pass "startup summary shows cache policy" \ + || fail "startup summary does not show cache policy" + grep -qF 'sources/install.esd' "$REPO_DIR/install-wor.sh" \ && pass "ISO import accepts install.esd media" \ || fail "ISO import does not accept install.esd media" + + grep -qF 'mkfs.fat -F 32 -n WOR_BOOT' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'mkfs.exfat -n WOR_INSTALL' "$REPO_DIR/install-wor.sh" \ + && pass "Linux writes stable partition labels" \ + || fail "Linux does not write stable partition labels" + + grep -qF 'WOR_MACOS_BREW_FORMULAE=(aria2 cabextract jq wget wimlib gptfdisk pv)' "$REPO_DIR/src/lib/dependencies.sh" \ + && grep -qF 'WOR_LINUX_PACKAGES=(yad aria2 cabextract wimtools chntpw genisoimage exfat-fuse wget udftools bc parted dosfstools unzip git pv)' "$REPO_DIR/src/lib/dependencies.sh" \ + && pass "supported hosts install the progress utility" \ + || fail "a supported host does not install the progress utility" + + grep -qF 'copy_startup_environment_with_progress' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'copy_local_file_with_progress "$(basename "$install_image")"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sha1_file_with_progress downloaded-esd' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sha256_file_with_progress downloaded-esd' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'errors="$(wimextract' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'errors="$(wimexport' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'errors="$(wimdelete' "$REPO_DIR/install-wor.sh" \ + && pass "long image operations stream progress" \ + || fail "a long image operation still hides progress" + + esd_delete_block="$(sed -n '/#Remove first 3 partitions from ESD file/,/mv -f .*install.wim/p' "$REPO_DIR/install-wor.sh")" + [ "$(printf '%s\n' "$esd_delete_block" | grep -cF 'wimdelete "$SOURCE_FILE" 1 --soft')" == 2 ] \ + && printf '%s\n' "$esd_delete_block" | grep -qF 'wimdelete "$SOURCE_FILE" 1 || error' \ + && pass "ESD conversion compacts the image after two soft index deletions" \ + || fail "ESD conversion does not compact the image on its final index deletion" + + awk '/#install dependencies before using them/{deps=NR} /#check for internet connection/{net=NR} END{exit !(deps && net && deps < net)}' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Missing required dependency: wget.' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'No internet connection!' "$REPO_DIR/install-wor.sh" \ + && pass "dependency checks run before internet check with specific wget error" \ + || fail "dependency checks do not run before internet check or wget error is misleading" + + grep -qF "'.DiskSize // .TotalSize // .Size'" "$REPO_DIR/install-wor.sh" \ + && pass "Darwin drive sizing supports current diskutil metadata" \ + || fail "Darwin drive sizing does not support current diskutil metadata" + + grep -qF 'darwin_apfs_volume_names()' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'diskutil apfs list -plist' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Labels: %s' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'labels = labels ? labels ", " $0 : $0' "$REPO_DIR/install-wor.sh" \ + && pass "Darwin device choices include user-facing disk labels" \ + || fail "Darwin device choices do not include disk labels" + + grep -qF 'MACOS_ASKPASS="$(mktemp)"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'register_file_cleanup "$MACOS_ASKPASS"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'To continue, WoR-Flasher needs administrator access for disk preparation, formatting, and flashing.' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Target: " & targetDevice' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Only continue if this is the drive you intend to erase.' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'WOR_FLASH_TARGET="$DEVICE" WOR_METADATA_FILE="$WOR_METADATA_FILE" WOR_APP_TITLE="$WOR_APP_TITLE" WOR_WINDOW_TITLE="$WOR_WINDOW_TITLE" SUDO_ASKPASS="$MACOS_ASKPASS" command sudo -A "$@"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'SUDO_ASKPASS="$MACOS_ASKPASS" command sudo -A "$@"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wor_osascript - "$WOR_WINDOW_TITLE" "$WOR_FLASH_TARGET"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'with title windowTitle' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'export RUN_MODE=gui' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'exec /usr/bin/open -W "$DIRECTORY/release/macos/WoR-Flasher.app"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '[ "${WOR_USE_PACKAGED_APP:-0}" == 1 ]' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '[ "${WOR_NATIVE_APP:-0}" != 1 ]' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'export WOR_NATIVE_APP=1' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF "name: 'WorErrorController'" "$REPO_DIR/install-wor.sh" \ + && grep -qF 'app.requestUserAttention($.NSInformationalRequest)' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'display alert' "$REPO_DIR/install-wor.sh" \ + && pass "macOS GUI shows a native password dialog instead of a terminal prompt" \ + || fail "macOS GUI does not use a native password dialog" + + cli_intro_block="$(sed -n '/^cli_intro() {/,/^}/p' "$REPO_DIR/install-wor.sh")" + grep -qF '"$WOR_FLASHER_NAME"' <<< "$cli_intro_block" \ + && (cd "$REPO_DIR" && ./install-wor.sh --help | head -n1 | grep -qF 'WoR-Flasher') \ + && ! grep -qiE 'web.?watcher' <<< "$cli_intro_block" \ + && [ "$(sed -n "/<<'BANNER'/,/^BANNER$/p" <<< "$cli_intro_block" | awk '{ if (length > max) max = length } END { print max }')" -le 79 ] \ + && pass "CLI ASCII banner names WoR-Flasher" \ + || fail "CLI ASCII banner does not name WoR-Flasher" + + erase_line="$(grep -anF '/usr/sbin/diskutil eraseVolume MS-DOS WOR_BOOT "$part1"' "$REPO_DIR/install-wor.sh" | tail -n1 | cut -d: -f1)" + type_line="$(grep -anF '"$sgdisk_bin" -og' "$REPO_DIR/install-wor.sh" | tail -n1 | cut -d: -f1)" + verify_line="$(grep -anF 'verify_written_image "$DEVICE" "$PART1" "$PART2" "$boot_mount" "$win_mount"' "$REPO_DIR/install-wor.sh" | tail -n1 | cut -d: -f1)" + finalizer_start_line="$(grep -anF 'darwin_start_partition_finalizer_or_die "$DEVICE" "$sgdisk_bin"' "$REPO_DIR/install-wor.sh" | tail -n1 | cut -d: -f1)" + prepare_call_line="$(grep -anF ' darwin_prepare_disk_or_die "$DEVICE" "$sgdisk_bin"' "$REPO_DIR/install-wor.sh" | cut -d: -f1)" + finalize_call_line="$(grep -anxF ' darwin_finalize_partition_types_or_die' "$REPO_DIR/install-wor.sh" | tail -n1 | cut -d: -f1)" + final_verify_line="$(grep -anF 'darwin_verify_final_partition_types_or_die "$PART1" "$PART2"' "$REPO_DIR/install-wor.sh" | tail -n1 | cut -d: -f1)" + [ -n "$erase_line" ] && [ -n "$type_line" ] && [ -n "$verify_line" ] && [ -n "$finalizer_start_line" ] && [ -n "$finalize_call_line" ] && [ -n "$final_verify_line" ] \ + && [ "$type_line" -lt "$erase_line" ] && [ "$finalizer_start_line" -lt "$verify_line" ] \ + && [ "$finalizer_start_line" -lt "$prepare_call_line" ] \ + && [ "$verify_line" -lt "$finalize_call_line" ] && [ "$finalize_call_line" -lt "$final_verify_line" ] \ + && grep -qF -- '-A 1:clear:63 -A 2:clear:63' "$REPO_DIR/install-wor.sh" \ + && pass "macOS verifies the mounted files, then retags the EFI partition and clears GPT bit 63" \ + || fail "macOS retags too early, skips clearing GPT bit 63, or leaves WOR_BOOT as Microsoft Basic Data" + + #shellcheck disable=SC1091 + source "$TEST_SCRIPT_DIR/run-tests-macos.sh" + if [ "$(uname -s)" == Darwin ];then + macos_app_and_launcher_checks + else + for macos_only_name in "${MACOS_ONLY_TEST_NAMES[@]}" ;do + skip "$macos_only_name (requires macOS; detected host is $(uname -s))" + done + fi + + grep -qF 'macos_start_cli()' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'WOR_GUI_INSTANCE_DIR="${TMPDIR:-/tmp}/wor-flasher-gui-${UID}.lock"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'kill -0 "$owner_pid"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '[[ "$owner_command" == *install-wor-gui.sh* ]]' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'activate_macos_gui "$owner_pid"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '$.NSRunningApplication.runningApplicationWithProcessIdentifier(pid)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if (!runningApp.activateWithOptions($.NSApplicationActivateAllWindows | $.NSApplicationActivateIgnoringOtherApps)) $.exit(1)' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the macOS GUI enforces a single instance and refocuses the running one" \ + || fail "macOS GUI single-instance handling is missing or does not refocus the running app" + + grep -qF 'macos_show_announcement()' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'partnership.png' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Proceed with WoR-Flasher' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "textView:clickedOnLink:atIndex:" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addPromptLink('Blackout Secure', 'https://blackoutsecure.app/')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addPromptLink('Botspot', 'https://github.com/Botspot')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addPromptLink('Windows on R', 'https://worproject.com/')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addPromptLink('Botspot/wor-flasher', 'https://github.com/Botspot/wor-flasher')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addPromptLink('sponsoring Botspot', 'https://github.com/sponsors/Botspot')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addPromptLink('buying Blackout Secure a coffee', 'https://github.com/sponsors/blackoutsecure?frequency=one-time&amount=8')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '$.NSForegroundColorAttributeName, $.NSColor.linkColor, range' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Blackout Secure is proud to partner with Botspot' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "printf -v announcement_text '%s\\n\\n%s\\n\\n%s\\n'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'yad "${yadflags[@]}" --width="$(wor_yad_width 840)" --height="$(wor_yad_height 720)" --center' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--image="$announcement_image" --image-on-top --text-align=center' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--form --align=center --buttons-layout=center --timeout="$WOR_ANNOUNCEMENT_TIMEOUT"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'announcement_image="$(wor_yad_image_for_screen' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- "--field=\$'<a href=\"https://blackoutsecure.app/\">Blackout Secure</a>" "$REPO_DIR/install-wor-gui.sh" \ + && pass "the partnership announcement renders with its attribution links" \ + || fail "the partnership announcement, its copy, or an attribution link is missing" + + #yad draws --image at its native size, so an oversized banner grows the dialog past the screen + png_dimension() { #Input: png path and 0 for width or 4 for height. Output: pixel count from the IHDR chunk. + od -An -tu1 -j "$((16 + $2))" -N4 "$1" | awk 'NF{print $1 * 16777216 + $2 * 65536 + $3 * 256 + $4; exit}' + } + banner_width="$(png_dimension "$REPO_DIR/assets/partnership.png" 0)" + banner_height="$(png_dimension "$REPO_DIR/assets/partnership.png" 4)" + [ -n "$banner_width" ] && [ -n "$banner_height" ] && [ "$banner_width" -le 1000 ] && [ "$banner_height" -le 700 ] \ + && pass "the partnership banner fits a yad dialog at its native size" \ + || fail "assets/partnership.png is ${banner_width}x${banner_height}; yad cannot scale it down" + + responsive_yad="$(run_in_engine 'WOR_YAD_SCREEN_WIDTH=800; WOR_YAD_SCREEN_HEIGHT=600; wor_init_yad_flags; printf "%s|%s|%s|%s" "$(wor_yad_width 840)" "$(wor_yad_height 720)" "$(wor_yad_image_for_screen preferred fallback 880 740)" "$(WOR_YAD_SCREEN_WIDTH=1024 WOR_YAD_SCREEN_HEIGHT=768 wor_yad_image_for_screen preferred fallback 880 740)"')" + [ "$responsive_yad" == '760|540|fallback|preferred' ] \ + && grep -qF 'wor_detect_yad_screen() {' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'command -v xrandr' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'command -v xdpyinfo' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'command -v xwininfo' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'export GIO_USE_VOLUME_MONITOR=unix' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'yad "${yadflags[@]}" --width="$(wor_yad_width 720)" --height="$(wor_yad_height 700)" --image="$WOR_ASSETS_DIR/overview.png"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'output="$(yad "${yadflags[@]}" --use-markup --changed-action="$changed_action" --width="$(wor_yad_width 720)" --height="$(wor_yad_height 720)"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- "--text=\$'<big><b>Advanced Options</b></big>" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'config_editor_title="$WOR_WINDOW_TITLE | config.txt"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--text-info --editable --in-place' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Choose Cached Windows Files' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Current download folder:' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '<b>Change Download Folder</b>' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '<b>Use download folder</b>' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--field=Download folder:DIR' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--field=Windows files:RO' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'Working directory: (DL' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'Choose DL_DIR' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -cE -- '--(width|height)=[0-9]+' "$REPO_DIR/install-wor-gui.sh")" == 1 ] \ + && pass "Linux dialogs clamp to the detected screen and use smaller artwork when needed" \ + || fail "Linux dialog sizing is fixed or can place content outside the screen: '$responsive_yad'" + + #gtk_window_resize asserts height > 0, so a zero height logs a Gtk-CRITICAL on every dialog + ! grep -qF -- '--height=0' "$REPO_DIR/install-wor-gui.sh" \ + && pass "no yad dialog asks GTK for a zero height" \ + || fail "a yad dialog uses --height=0, which trips a gtk_window_resize assertion" + + grep -qF 'WOR_WINDOW_TITLE' "$REPO_DIR/src/lib/metadata.sh" \ + && grep -qF '"$WOR_WINDOW_TITLE"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"${10:-$WOR_WINDOW_TITLE}"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"${13:-$WOR_APP_TITLE}"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"$WOR_WINDOW_TITLE" Back 0 "$WOR_APP_TITLE"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--title="$WOR_WINDOW_TITLE"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--class="$WOR_ICON_NAME"' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'ensure_linux_desktop_identity()' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'StartupWMClass=$WOR_ICON_NAME' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Icon=$icon_path' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const isMessageMode = choices.length === 0' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const iconPath = ObjC.unwrap(args.objectAtIndex(12))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const cancelValue = ObjC.unwrap(args.objectAtIndex(14))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "function cancelAndExit()" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'attributedPrompt.mutableString.appendString($(promptText))' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'NSMutableAttributedString.alloc.initWithString' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"$WOR_LOGO_PATH"' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(run_in_engine 'printf %s "$WOR_LOGO_FILENAME"')" == 'logo-full.png' ] \ + && [ "$(run_in_engine 'printf %s "$WOR_ASSETS_DIRNAME"')" == 'assets' ] \ + && grep -qF 'WOR_LOGO_PATH="$WOR_ASSETS_DIR/$WOR_LOGO_FILENAME"' "$REPO_DIR/install-wor.sh" \ + && grep -qF ': "${WOR_ANNOUNCEMENT_TIMEOUT:=30}"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "'countdownTick:'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "nextButton.title = nextLabel + ' (' + countdownSeconds + ')'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "selectedValue = defaultChoice" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addTimerForMode(countdownTimer, $.NSModalPanelRunLoopMode)" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--timeout="$WOR_ANNOUNCEMENT_TIMEOUT"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'function writeResult(value)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'fileHandleWithStandardOutput.writeData(data)' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'echo -e "\\\\e[91m' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'console.log(selectedValue)' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the shared macos_choose dialog returns its result on stdout and honours the countdown" \ + || fail "the macos_choose dialog contract, its countdown, or its stdout result protocol is broken" + + grep -qF 'Choose Windows and Raspberry Pi target' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "'Windows 11'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Raspberry Pi model:' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'macos_choose_target()' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Choose Windows and Raspberry Pi target' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'selectedValue = windows[windowsIdx] + '\''\t'\'' + piModels[piIdx]' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'current_windows_ver='\''Windows 11'\''' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'target_choice="$(macos_choose_target "$current_windows_ver" "$current_rpi_model")"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'windowWillClose:' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "NSButton.buttonWithTitleTargetAction(cancelLabel, controller, 'cancelClicked:')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Choose Windows language' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "macos_choose '' 'No external, physical, writable drive was found. Connect a removable drive, then click Refresh.' __REFRESH__ Back" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'darwin_list_device_choices' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const actionValue = ObjC.unwrap(args.objectAtIndex(9))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "NSButton.buttonWithTitleTargetAction(actionLabel, controller, 'actionClicked:')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Back Refresh __REFRESH__' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'Refresh detected devices' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'tkinter' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Choose installation mode' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'macos_confirm_flash() {' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const heading = $.NSTextField.labelWithString' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "const warning = $.NSTextField.labelWithString('All data on the target drive will be erased.')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "const flashButton = $.NSButton.buttonWithTitleTargetAction('Flash'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'confirmation="$(macos_confirm_flash)"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '[ "$confirmation" == Cancel ] && exit 0' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'macos_advanced_options' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'display alert' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'display dialog' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'All data on the target drive will be erased.' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the macOS wizard collects every choice and confirms before flashing" \ + || fail "a macOS wizard step, the drive refresh, or the flash confirmation is missing" + + chooser_function="$(sed -n '/^macos_choose_device() {/,/^}$/p' "$REPO_DIR/install-wor-gui.sh")" + chooser_called="$(mktemp)" + if [ -z "$chooser_function" ];then + fail "the macOS device chooser function could not be loaded for behavioral testing" + elif CHOOSER_CALLED="$chooser_called" bash -c ' + eval "$1" + #a real Next/Refresh click in message mode returns the default (3rd arg); a real Next click + #in list mode returns the selected row, simulated here as the last row in the list + macos_choose() { + printf "%s\n" "$*" > "$CHOOSER_CALLED" + if [ -z "$1" ];then printf "%s\n" "$3"; else printf "%s\n" "$1" | tail -n1; fi + } + [ "$(macos_choose_device "")" == __REFRESH__ ] || exit 1 + grep -qF "No external, physical, writable drive was found." "$CHOOSER_CALLED" || exit 1 + choices="disk4 First drive +disk5 Second drive" + [ "$(macos_choose_device "$choices")" == "disk5 Second drive" ] + ' _ "$chooser_function" 2>/dev/null;then + pass "the macOS device chooser refreshes an empty list and returns a selected drive" + else + fail "the macOS device chooser mishandles an empty list or selected drive" + fi + + #a genuine Quit/close-box still must propagate as a failure from macos_choose_device, not be + #swallowed and reported as if Refresh was clicked + if [ -z "$chooser_function" ];then + fail "the macOS device chooser cancel path could not be loaded for behavioral testing" + elif bash -c ' + eval "$1" + macos_choose() { return 1; } + ! macos_choose_device "" + ' _ "$chooser_function" 2>/dev/null;then + pass "Quit on the no-drive screen is treated as a cancel, not a Refresh" + else + fail "Quit on the no-drive screen is swallowed and reported as Refresh" + fi + + #clicking Back must succeed with the literal cancelValue instead of failing like Quit does, + #so the wizard step machine can go back a step instead of exiting the whole program + if [ -z "$chooser_function" ];then + fail "the macOS device chooser Back path could not be loaded for behavioral testing" + elif bash -c ' + eval "$1" + macos_choose() { shift 10; printf "%s\n" "$1"; } #echo the 11th arg: cancelValue + [ "$(macos_choose_device "")" == Back ] || exit 1 + choices="disk4 First drive +disk5 Second drive" + [ "$(macos_choose_device "$choices")" == Back ] + ' _ "$chooser_function" 2>/dev/null;then + pass "Back on the device chooser screens succeeds with a literal value, unlike Quit" + else + fail "Back on a device chooser screen does not carry a distinct cancelValue" + fi + + rm -f "$chooser_called" + + grep -qF "name: 'WorCompletionController'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'wor_osascript -l JavaScript - "$completion_text"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'next-steps.png' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const imagePath = ObjC.unwrap(args.objectAtIndex(7)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'exit "$installer_status"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'WOR_GUI_ERROR_MARKER="$error_marker"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'The Windows on Raspberry script stopped unexpectedly (exit code $installer_status).' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "name: 'WorProgressController'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'NSTimer.scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeats' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'tell application "Terminal" to close front window' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF "terminal_runner=\"\$(mktemp)\"" "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'do script (item 1 of argv)' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'choose from list' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the macOS progress and completion windows report installer status natively" \ + || fail "a macOS progress or completion window is missing, or a spawned-terminal fallback returned" + + grep -qF 'device_tree_address=0x3e0000' "$REPO_DIR/config-templates/pi4.config.txt" \ + && grep -qF 'device_tree_end=0x400000' "$REPO_DIR/config-templates/pi4.config.txt" \ + && grep -qF 'read_config_template "pi$1.config.txt"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'set_default_config_txt' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Pi 4 GUI config matches the current UEFI memory range" \ + || fail "Pi 4 GUI config uses stale device-tree addresses" + + #stepping back to change the model must not carry the previous model's boot config onto the drive, + #while a config.txt the user has edited stays theirs + run_in_engine ' + RPI_MODEL=5; CONFIG_TXT=""; CONFIG_TXT_MODEL="" + set_default_config_txt + pi5="$CONFIG_TXT" + RPI_MODEL=4 + set_default_config_txt + [ "$CONFIG_TXT" == "$(default_config_txt 4)" ] || exit 1 + [ "$CONFIG_TXT" != "$pi5" ] || exit 1 + CONFIG_TXT="$CONFIG_TXT +#edited by the user" + edited="$CONFIG_TXT" + RPI_MODEL=3 + set_default_config_txt + [ "$CONFIG_TXT" == "$edited" ] || exit 1 + ' \ + && pass "changing the Pi model reloads that model config.txt without discarding user edits" \ + || fail "changing the Pi model keeps the previous model config.txt, or overwrites a user edit" + #v1.51/v1.52 report a zero MAC (pftf/RPi4#283); v1.52/v1.53 do not boot from microSD (pftf/RPi4#285) + [ "$(run_in_engine 'printf %s "$WOR_DEFAULT_UEFI_VER_PI4"')" == 'v1.50' ] \ + && ! grep -qF '"uefiVerPi4": "v1.51"' "$REPO_DIR/src/config/metadata.json" \ + && ! grep -qF '"uefiVerPi4": "v1.52"' "$REPO_DIR/src/config/metadata.json" \ + && ! grep -qF '"uefiVerPi4": "v1.53"' "$REPO_DIR/src/config/metadata.json" \ + && pass "Pi 4 pins the only UEFI release with a working MAC address and microSD boot" \ + || fail "Pi 4 pins a UEFI release with a zero Ethernet MAC or a microSD boot regression" + + grep -qF '#Raspberry Pi 4 only; this setting is ignored for every other model.' "$REPO_DIR/install-wor.sh" \ + && grep -qF '[ -z "$PI4_AUTO_DISABLE_3GB" ] && PI4_AUTO_DISABLE_3GB=1' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'SetFirmwareEnvironmentVariableEx("RamLimitTo3GB", "{CD7CC258-31DB-22E6-9F22-63B0B8EED6B5}"' "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" \ + && grep -qF '<settings pass="specialize">' "$REPO_DIR/config-templates/pi4-ram-unlock-specialize.xml" \ + && grep -qF '<Path>powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%WINDIR%\Setup\Scripts\Pi4Disable3GB.ps1"</Path>' "$REPO_DIR/config-templates/pi4-ram-unlock-specialize.xml" \ + && grep -qF '<WillReboot>Always</WillReboot>' "$REPO_DIR/config-templates/pi4-ram-unlock-specialize.xml" \ + && grep -qF 'read_config_template pi4-ram-unlock.ps1' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'read_config_template pi4-ram-unlock-specialize.xml' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'PI4_AUTO_DISABLE_3GB OOBE_NETWORK_BYPASS' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'Prepare a one-time post-install RAM unlock' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Pi 4 RAM unlock is config-only and runs automatically after the PE reboot" \ + || fail "Pi 4 automatic RAM unlock is unsafe or incomplete" + + grep -qF 'Pi 4 driver package is incomplete:' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/bcmgenet/bcmgenet.inf' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/mcci_dwchsotg/mcci_dwchsotg_hcd.inf' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/mcci_dwchsotg/mcci_dwchsotg_hub.inf' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wimdir "$boot_mount/sources/boot.wim" 2 --path=/drivers/rpiuxflt/rpiuxflt.inf' "$REPO_DIR/install-wor.sh" \ + && pass "Pi 4 Ethernet, USB, and DMA filter drivers are checked before and after flashing" \ + || fail "Pi 4 Ethernet, USB, and DMA filter driver verification is incomplete" + + grep -qF 'if [ "$RPI_MODEL" == 4 ] && [ "$PI4_AUTO_DISABLE_3GB" == 1 ];then' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'after the injected drivers are installed' "$REPO_DIR/README.md" \ + && grep -qF 'CM4 requires the RAM limit set to 1 GB' "$REPO_DIR/README.md" \ + && grep -qF 'bcdedit /deletevalue' "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" \ + && pass "Pi 4 RAM unlock runs only after Setup has installed the DMA filter, and clears the BCD memory cap" \ + || fail "Pi 4 RAM unlock may run before the DMA filter is installed, or CM4 guidance is missing" + + grep -qF '[ -z "$OOBE_NETWORK_BYPASS" ] && OOBE_NETWORK_BYPASS=1' "$REPO_DIR/install-wor.sh" \ + && grep -qF '<HideOnlineAccountScreens>true</HideOnlineAccountScreens>' "$REPO_DIR/config-templates/oobe-network-bypass.xml" \ + && grep -qF '<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>' "$REPO_DIR/config-templates/oobe-network-bypass.xml" \ + && grep -qF 'read_config_template oobe-network-bypass.xml' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'WINDOWS_ACCOUNT_SETUP' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'WINDOWS_LOCALE_SETUP' "$REPO_DIR/install-wor.sh" \ + && grep -qF '<LocalAccount wcm:action="add">' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Microsoft-Windows-International-Core' "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF 'install_windows_setup_configuration ' "$REPO_DIR/install-wor.sh")" == 2 ] \ + && grep -qF 'cmp -s "$boot_mount/Autounattend.xml" "$install_mount/Autounattend.xml"' "$REPO_DIR/install-wor.sh" \ + && pass "Windows OOBE network bypass is default-on, configurable, written to both partitions, and verified" \ + || fail "Windows OOBE network bypass is incomplete" + + account_xml="$(run_in_engine 'WINDOWS_ACCOUNT_SETUP=1 WINDOWS_ACCOUNT_USERNAME="Test&User" WINDOWS_ACCOUNT_PASSWORD="safe<password>" WINDOWS_LOCALE_SETUP=1 WINDOWS_LOCALE=en-GB unattend_xml')" + printf '%s' "$account_xml" | grep -qF '<Name>Test&amp;User</Name>' \ + && printf '%s' "$account_xml" | grep -qF '<Value>safe&lt;password&gt;</Value>' \ + && printf '%s' "$account_xml" | grep -qF '<InputLocale>en-GB</InputLocale>' \ + && ! printf '%s' "$account_xml" | grep -qF 'WINDOWS_ACCOUNT_PASSWORD' \ + && pass "optional Windows account and locale settings render safely in unattended XML" \ + || fail "optional Windows account or locale settings render incorrectly" + + [ -f "$REPO_DIR/config-templates/pi3.config.txt" ] \ + && [ -f "$REPO_DIR/config-templates/pi4.config.txt" ] \ + && [ -f "$REPO_DIR/config-templates/pi5.config.txt" ] \ + && [ -f "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" ] \ + && [ -f "$REPO_DIR/config-templates/pi4-ram-unlock-specialize.xml" ] \ + && grep -qF 'PI4_UEFI_SHELL_UNLOCK' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'ShellBinPkg/UefiShell/AArch64/Shell.efi' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'BOOTAA64.WOR' "$REPO_DIR/config-templates/prefinalize.cmd" \ + && grep -qF 'setvar RamLimitTo3GB' "$REPO_DIR/config-templates/prefinalize.cmd" \ + && grep -qF 'rm \EFI\BOOT\BOOTAA64.EFI' "$REPO_DIR/config-templates/prefinalize.cmd" \ + && [ -f "$REPO_DIR/config-templates/oobe-network-bypass.xml" ] \ + && [ -f "$REPO_DIR/config-templates/prefinalize.cmd" ] \ + && [ -f "$REPO_DIR/config-templates/config.json" ] \ + && [ -f "$REPO_DIR/config-templates/config.schema.json" ] \ + && grep -qF 'read_config_template() {' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'sync_repo_template' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'raw.githubusercontent.com' "$REPO_DIR/install-wor.sh" \ + && pass "config-templates/ files exist as static, locally-editable files with no redundant per-file repo sync" \ + || fail "config-templates/ files are missing, or the removed per-file sync mechanism is still present" + + [ "$(jq -r 'has("system")' "$REPO_DIR/config-templates/config.json" 2>/dev/null)" == false ] \ + && grep -qF '"peInstallerUrl"' "$REPO_DIR/src/config/metadata.json" \ + && grep -qF '"peInstallerSha256"' "$REPO_DIR/src/config/metadata.json" \ + && grep -qF '"uefiVerPi4"' "$REPO_DIR/src/config/metadata.json" \ + && grep -qF '"driverVer"' "$REPO_DIR/src/config/metadata.json" \ + && grep -qF '"repoSlug"' "$REPO_DIR/src/config/metadata.json" \ + && pass "default config omits project pins; runtime metadata supplies PE, firmware, driver and update defaults" \ + || fail "default config still carries project pins, or metadata defaults are missing" + + [ "$(jq -r '.userAccount.localeSetup' "$REPO_DIR/config-templates/config.json")" == true ] \ + && [ "$(jq -r '.userAccount.locale' "$REPO_DIR/config-templates/config.json")" == '' ] \ + && grep -qF 'leave empty to use the host locale or en-US' "$REPO_DIR/config-templates/config.schema.json" \ + && pass "default config enables host-detected Windows regional settings" \ + || fail "default config does not enable host-detected Windows regional settings" + + #these must be committed: a fresh clone without them silently writes a blank config.txt and the Pi will not boot + if command -v git >/dev/null && git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 ;then + [ "$(git -C "$REPO_DIR" ls-files config-templates/ | wc -l | tr -d ' ')" == 9 ] \ + && grep -qF 'This file ships with WoR-Flasher and is required to write a bootable drive.' "$REPO_DIR/install-wor.sh" \ + && pass "config-templates/ files are tracked by git and a missing one aborts instead of writing a blank config.txt" \ + || fail "config-templates/ files are untracked, or a missing template does not abort" + else + skip "git is unavailable; cannot verify that config-templates/ is tracked" + fi + + grep -qF 'macos_advanced_options() {' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "name: 'WorAdvancedController'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'NSButton.checkboxWithTitleTargetAction' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Automatically disable the Pi 4 3 GB RAM limit after install' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'Skip flashing the device (dry run)' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'function addSectionHeader(title)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Windows setup')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Firmware and drivers')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Validation')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Downloads')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Windows account')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Regional settings')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "addSectionHeader('Raspberry Pi boot config')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "accountCheckbox = $.NSButton.checkboxWithTitleTargetAction('Create a local Windows administrator account'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'accountUsernameField.enabled = enabled' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'accountPasswordField.enabled = enabled' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'updateAccountEditableState()' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "localeCheckbox = $.NSButton.checkboxWithTitleTargetAction('Configure Windows keyboard and regional settings'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'localePopup.enabled = enabled' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'localePopup.addItemWithTitle' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const localeSpec = ObjC.unwrap(args.objectAtIndex(18))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const windowTitle = ObjC.unwrap(args.objectAtIndex(19))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "title: windowTitle + ' | Advanced Options'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"$WIN_LANG" "$WINDOWS_ACCOUNT_SETUP" "$WINDOWS_LOCALE_SETUP" "$locale_spec" "$WOR_WINDOW_TITLE"' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -cF 'export_installer_settings' "$REPO_DIR/install-wor-gui.sh")" == 1 ] \ + && grep -qF 'export "${WOR_INSTALLER_SETTINGS[@]}"' "$REPO_DIR/install-wor.sh" \ + && grep -qF "editMenu.addItemWithTitleActionKeyEquivalent('Copy', 'copy:', 'c')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'app.mainMenu = mainMenu' "$REPO_DIR/src/lib/gui.sh" \ + && pass "macOS and Linux GUIs expose an Advanced Options window for site-documented customizations" \ + || fail "Advanced Options window is missing or incomplete" + + grep -qF 'linux_choose_one() {' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--use-markup' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -anF -- '--field=Windows setup:LBL' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" -lt "$(grep -anF -- '--field=Firmware and drivers:LBL' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" ] \ + && [ "$(grep -anF -- '--field=Firmware and drivers:LBL' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" -lt "$(grep -anF -- '--field=Validation:LBL' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" ] \ + && [ "$(grep -anF -- '--field=Validation:LBL' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" -lt "$(grep -anF -- '--field=Downloads:LBL' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" ] \ + && grep -qF -- "--field='Windows version:CB' 'Windows 11!Windows 10!More options'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- "--field='Raspberry Pi model:CB' 'Raspberry Pi 5!Raspberry Pi 4 / Pi 400!Raspberry Pi 3 / Pi 2 v1.2'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "RPI_MODEL=''" "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF -- '--form --columns=2' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF -- "--button='<b>View / Edit config.txt...</b>':3" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--field=<b>View / Edit config.txt</b>:BTN' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--changed-action="$changed_action"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'account_username_value=' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "account_username_value='@disabled@'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "account_password_value='@disabled@'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "locale_value='@disabled@'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "config_button_value='@disabled@'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'account_checkbox_field=$((${#fields[@]} / 2 + 1))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'locale_checkbox_field=$((${#fields[@]} / 2 + 1))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'config_checkbox_field=$((${#fields[@]} / 2 + 1))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'account_username_field=$((${#fields[@]} / 2 + 1))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'config_button_field=$((${#fields[@]} / 2 + 1))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'yad_field_value()' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF '$(echo "$output" | sed -n 7p)' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF '$(echo "$output" | sed -n 18p)' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -cF -- '--form --scroll' "$REPO_DIR/install-wor-gui.sh")" -ge 2 ] \ + && grep -qF 'yadflags=(--center --fixed --buttons-layout=center' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF -- "--button='<b>Abort</b>':1" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if ! kill -0 "$yad_pid"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'kill_process_tree "$installer_pid"' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Linux follows the macOS screen route, bounded forms, fixed windows, and safe progress close" \ + || fail "Linux still uses its old combined route, embedded editor, resizable windows, or unsafe progress close" + + linux_repair_dir="$(mktemp -d)" + mkdir -p "$linux_repair_dir/assets" + cp "$REPO_DIR/install-wor-gui.sh" "$linux_repair_dir/install-wor-gui.sh" + printf 'original script\n' > "$linux_repair_dir/install-wor.sh" + printf 'next steps\n' > "$linux_repair_dir/assets/next-steps.png" + git -C "$linux_repair_dir" init -q + git -C "$linux_repair_dir" add install-wor-gui.sh install-wor.sh assets/next-steps.png + git -C "$linux_repair_dir" -c user.name=Test -c user.email=test@example.com commit -qm fixture + printf 'modified script\n' > "$linux_repair_dir/install-wor.sh" + rm "$linux_repair_dir/assets/next-steps.png" + awk '{print} /repair_missing_checkout_runtime \|\| exit 1/{exit}' "$linux_repair_dir/install-wor-gui.sh" > "$linux_repair_dir/repair-probe.sh" + chmod +x "$linux_repair_dir/repair-probe.sh" + if DIRECTORY="$linux_repair_dir" bash "$linux_repair_dir/repair-probe.sh" >/dev/null 2>&1 \ + && [ "$(cat "$linux_repair_dir/assets/next-steps.png")" == 'next steps' ] \ + && [ "$(cat "$linux_repair_dir/install-wor.sh")" == 'modified script' ];then + pass "Linux startup repairs missing tracked runtime files before loading shared libraries" + else + fail "Linux startup repair is too late, misses assets, or overwrites modified files" + fi + rm -rf "$linux_repair_dir" + + ! grep -qF 'OVERRIDE_CONFIG_TXT' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'OVERRIDE_PI4_RAM_UNLOCK' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'OVERRIDE_OOBE_TEMPLATE' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Redundant per-file repo-sync checkboxes were removed from both GUIs" \ + || fail "Redundant per-file repo-sync checkboxes are still present" + + #WSL reports uname -s as Linux, so it would otherwise pass the host gate and offer + #WSL's own virtual disks as erasable targets + grep -qF 'is_wsl() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'WSL_DISTRO_NAME' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'WSLENV' "$REPO_DIR/install-wor.sh" \ + && grep -qF '/proc/version' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if is_wsl ;then' "$REPO_DIR/install-wor.sh" \ + && pass "WSL hosts are rejected before any drive is listed or erased" \ + || fail "WSL is not detected, so it would pass the Linux host gate" + + #a modal session never services default-mode run loop sources, so the Dock's quit Apple Event + #is only delivered because each window registers a handler and pumps default mode from a timer + [ "$(grep -cF "'handleQuitEvent:withReplyEvent:': {" "$REPO_DIR/install-wor-gui.sh")" == 6 ] \ + && [ "$(grep -cF "'pumpEvents:': {" "$REPO_DIR/install-wor-gui.sh")" == 6 ] \ + && [ "$(grep -cF 'worInstallWindowHandlers(controller)' "$REPO_DIR/install-wor-gui.sh")" == 6 ] \ + && grep -qF '0x61657674, 0x71756974' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'addTimerForMode(pumpTimer' "$REPO_DIR/src/lib/gui.sh" \ + && [ "$(grep -cF 'app.runModalForWindow(window)' "$REPO_DIR/install-wor-gui.sh")" == 6 ] \ + && pass "every macOS window responds to the Dock's Quit menu item" \ + || fail "a macOS window cannot receive the Dock's quit Apple Event" + + #these heredocs sit inside "$( ... )", so bash still tracks quote state through the body: + #a lone apostrophe (e.g. "doesn't") silently swallows every function defined after it + [ -z "$(awk '/<<.JXA.$/{inh=1; next} inh && /^JXA$/{inh=0; next} inh{n=gsub(/'"'"'/,""); if(n%2==1) print NR}' "$REPO_DIR/install-wor-gui.sh")" ] \ + && pass "JXA heredoc bodies contain no unpaired apostrophes" \ + || fail "an unpaired apostrophe in a JXA heredoc will corrupt shell parsing" + + #guards against the same class of breakage from any cause: run the real script far enough to + #register its function definitions, then confirm every macos_* helper actually became a function + macos_fn_expected="$(grep -ac '^macos_[a-z_]*() {' "$REPO_DIR/install-wor-gui.sh")" + macos_fn_probe="$(mktemp)" + awk -v line="$(grep -an '^macos_start_cli() {' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" \ + 'NR==line{print "declare -F | grep -c \"^declare -f macos_\"; exit 0"} {print}' \ + "$REPO_DIR/install-wor-gui.sh" > "$macos_fn_probe" + #macos_start_cli itself is not defined yet at the probe point. + #DIRECTORY is supplied because the probe is a copy: the GUI resolves install-wor.sh relative to its own path. + #TMPDIR is isolated so the single-instance lock is free: a GUI already open would otherwise + #make the probe hand off to it and exit before reaching the marker. + macos_fn_tmpdir="$(mktemp -d)" + [ "$(cd "$REPO_DIR" && DIRECTORY="$REPO_DIR" WOR_NATIVE_APP=1 TMPDIR="$macos_fn_tmpdir" bash "$macos_fn_probe" 2>/dev/null | tail -n1)" == "$((macos_fn_expected - 1))" ] \ + && pass "all macOS helper functions parse as separate top-level definitions" \ + || fail "a macOS function definition is being swallowed by a preceding heredoc" + rm -rf "$macos_fn_probe" "$macos_fn_tmpdir" + + #the engine ignores PI4_AUTO_DISABLE_3GB unless RPI_MODEL is 4, so the GUIs must not offer it as a live choice + grep -qF 'checked: parts[1]' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "enabled: parts[2] !== '0'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'checkbox.enabled = rows[i].enabled' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -cF 'pi4_applicable=1 || pi4_applicable=0' "$REPO_DIR/install-wor-gui.sh")" == 2 ] \ + && grep -qF 'not applicable to the Pi %s' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF '2) [ "$pi4_applicable" == 1 ] && PI4_AUTO_DISABLE_3GB="$line" ;;' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the Pi 4 RAM-unlock toggle is greyed out and ignored on other Pi models" \ + || fail "the Pi 4 RAM-unlock toggle is not gated on the selected Pi model" + + #in recovery mode the custom config.txt only boots the installer media + grep -qF "printf 'boot partition'" "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'not the Windows drive' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF "printf 'Apply the customized config.txt to the %s'" "$REPO_DIR/src/lib/gui.sh" \ + && pass "the config.txt editor states its scope for the selected installation mode" \ + || fail "the config.txt editor does not state its scope per installation mode" + + grep -qF '[ -z "$SKIP_IMAGE_VERIFICATION" ] && SKIP_IMAGE_VERIFICATION=0' "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF 'if [ "$SKIP_IMAGE_VERIFICATION" == 1 ];then' "$REPO_DIR/install-wor.sh")" == 2 ] \ + && [ "$(grep -cF 'verify_written_image "$DEVICE" "$PART1" "$PART2"' "$REPO_DIR/install-wor.sh")" == 2 ] \ + && grep -qF 'Use the latest UEFI firmware instead of the tested pinned version (%s)' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'Use the latest Windows ARM64 drivers instead of the pinned version (%s)' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'Skip verifying the written image after flashing' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'Flash begins immediately after administrator approval. Use Advanced to change these settings.' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Skip-verification option defaults off, wraps both verify_written_image calls, and confirm screens show pinned versions and guidance" \ + || fail "Skip-verification option or confirm-screen guidance is missing or incomplete" + + grep -qF '[ -z "$APPLY_CUSTOM_CONFIG_TXT" ] && APPLY_CUSTOM_CONFIG_TXT=1' "$REPO_DIR/install-wor.sh" \ + && grep -qF '[ -z "$CONFIG_TXT" ] || [ "$APPLY_CUSTOM_CONFIG_TXT" != 1 ] || printf' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'mkdir -p "$boot_mount/efi"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'cp -R "$PWD/peinstaller/efi/." "$boot_mount/efi"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'cp -RX "$PWD/pi${RPI_MODEL}-uefipackage"/* "$boot_mount"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if is_macos;then' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'copy_mounted_file_with_progress()' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'mkdir -p "$destination/boot" "$destination/efi"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'cp -R "$source/boot/." "$destination/boot"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'cp -R "$source/efi/." "$destination/efi"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sudo cp -R "$source/boot" "$source/efi" "$destination"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'mounted_wimverify()' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'mounted_wimdir "$boot_mount/sources/boot.wim"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if [ ! -z "$CONFIG_TXT" ] && [ "$APPLY_CUSTOM_CONFIG_TXT" == 1 ];then' "$REPO_DIR/install-wor.sh" \ + && grep -qF "applyConfigCheckbox = \$.NSButton.checkboxWithTitleTargetAction(applyConfigLabel, controller, 'applyConfigToggled:')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'function updateConfigEditableState() {' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "editConfigButton = \$.NSButton.buttonWithTitleTargetAction('View / Edit…', controller, 'editConfigClicked:')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'editConfigButton.enabled = enabled' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "dialog.messageText = \$('View / Edit config.txt')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'configTxtValue = ObjC.unwrap(editor.string)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'out.push(configTxtValue)' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Applying the customized config.txt is a togglable checkbox that enables its separate editor on macOS" \ + || fail "Apply-customized-config.txt toggle is missing or incomplete" + apply_config_line="$(grep -anF "applyConfigCheckbox = $.NSButton.checkboxWithTitleTargetAction(applyConfigLabel," "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" + config_editor_line="$(grep -anF "editConfigButton = $.NSButton.buttonWithTitleTargetAction('View / Edit…'" "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" + [ -n "$apply_config_line" ] && [ -n "$config_editor_line" ] && [ "$apply_config_line" -lt "$config_editor_line" ] \ + && [ "$((config_editor_line - apply_config_line))" -lt 10 ] \ + && pass "Use customized config.txt and its editor button share one settings row" \ + || fail "Use customized config.txt is not grouped with its editor button" + + grep -qF '[ -z "$HIDE_EMPTY_DRIVES" ] && HIDE_EMPTY_DRIVES=1' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'configure_pe_settings_ini() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF "settings_ini=\"\$PWD/peinstaller/winpe/2/settings.ini\"" "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF 'configure_pe_settings_ini' "$REPO_DIR/install-wor.sh")" == 3 ] \ + && pass "HideEmptyDrives is written into the cached PE settings.ini before boot.wim assembly" \ + || fail "HideEmptyDrives support is missing or incomplete" + + grep -qF 'Allow Windows setup to continue without a network connection' "$REPO_DIR/src/lib/gui.sh" \ + && ! grep -qF "step=oobe" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'OOBE_NETWORK_BYPASS' "$REPO_DIR/install-wor.sh" \ + && pass "macOS and Linux GUIs expose the OOBE network choice only in Advanced Options" \ + || fail "GUI OOBE network choice is incomplete" + + grep -qF 'emit_gui_progress() { #Input: line.' "$REPO_DIR/install-wor.sh" \ + && grep -qF $'STEP\t$STEP_NUM\t$STEP_TOTAL\t$1' "$REPO_DIR/install-wor.sh" \ + && grep -qF $'STATUS\t$1' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'LINUX_ASKPASS="$(mktemp)"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'WOR_FLASH_TARGET="$DEVICE" WOR_ICON_PATH="$WOR_LOGO_PATH" SUDO_ASKPASS="$LINUX_ASKPASS" command sudo -A "$@"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sudo parted -ms "$device" unit B print' "$REPO_DIR/install-wor.sh" \ + && grep -qF -- "--progress --image=\"\$WOR_LOGO_PATH\" --text=\$'<big><b>Preparing flash...</b></big>" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--bar="Overall:NORM" --bar="Sub-progress:NORM"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '1:# Overall' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '2:# Sub-progress' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF '"$DIRECTORY/terminal-run"' "$REPO_DIR/install-wor-gui.sh" \ + && pass "GUI mode runs the installer without a visible terminal on macOS and Linux" \ + || fail "GUI mode still depends on a visible terminal" + + #a subshell cannot wait on a sibling, so `( wait "$installer_pid" ... )` wrote 127 immediately: + #the progress window closed at once and the GUI reported failure while the flash kept running. + #Both front-ends now go through one shared launcher, so there is a single copy to get right. + [ "$(grep -cF '{ "$cli_script" > "$output_log" 2>&1; echo $? > "$done_marker"; } &' "$REPO_DIR/install-wor-gui.sh")" == 1 ] \ + && [ "$(grep -cF 'gui_start_installer' "$REPO_DIR/install-wor-gui.sh")" == 3 ] \ + && ! grep -qF '( wait "$installer_pid"; echo $? > "$done_marker" ) &' "$REPO_DIR/install-wor-gui.sh" \ + && pass "both GUIs record the installer exit status from the job itself, not a sibling wait" \ + || fail "a GUI still waits on a sibling process, so it reports completion immediately" + + #After Flash, the GUI opens progress immediately and defers the single sudo prompt until the + #destructive disk step, so long setup/download/prep work cannot expire it before use. + gui_auth_wait_line="$(grep -an 'while \[ ! -e "\$auth_marker" \] && \[ ! -f "\$done_marker" \] ;do' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" + macos_progress_line="$(grep -an '<<<"\$progress_jxa"' "$REPO_DIR/install-wor-gui.sh" | head -n1 | cut -d: -f1)" + preauth_line="$(grep -anF '[ "$RUN_MODE" == gui ] && gui_preauthenticate' "$REPO_DIR/install-wor.sh" | cut -d: -f1)" + setup_line="$(grep -anF 'setup || exit 1' "$REPO_DIR/install-wor.sh" | cut -d: -f1)" + [ -n "$gui_auth_wait_line" ] && [ -n "$macos_progress_line" ] \ + && [ -n "$preauth_line" ] && [ -n "$setup_line" ] && [ "$preauth_line" -lt "$setup_line" ] \ + && grep -aqF 'export WOR_GUI_AUTH_MARKER="$auth_marker"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -aqF 'export GUI_PROGRESS_EARLY=1' "$REPO_DIR/install-wor-gui.sh" \ + && grep -aqF 'gui_preauthenticate() {' "$REPO_DIR/install-wor.sh" \ + && grep -aqF 'deferred until Step 5' "$REPO_DIR/install-wor.sh" \ + && ! grep -aqF 'WOR_GUI_SUDO_PREAUTH_DONE=1' "$REPO_DIR/install-wor.sh" \ + && grep -aqF 'command sudo -n -v >/dev/null 2>&1 || true; sleep 30' "$REPO_DIR/install-wor.sh" \ + && pass "GUI defers the single sudo prompt until the destructive disk step" \ + || fail "GUI still authenticates too early or can hide a sudo prompt behind progress" + + #under `set -e` an unguarded refresh ends the keepalive on its first failure, and the session then + #lapses mid-flash into the refusal to prompt again + keepalive_iterations="$( + set -e + ( iterations=0 + while [ "$iterations" -lt 3 ] ;do + iterations=$((iterations + 1)) + false >/dev/null 2>&1 || true + printf '%s\n' "$iterations" + done ) | tail -n1 + )" + [ "$keepalive_iterations" == 3 ] \ + && grep -aqF '[ "${BASH_SUBSHELL:-0}" -eq 0 ] || return 0' "$REPO_DIR/install-wor.sh" \ + && grep -aqF 'sleep 30; done ) >/dev/null 2>&1 </dev/null &' "$REPO_DIR/install-wor.sh" \ + && grep -aqF ') >/dev/null 2>&1 </dev/null &' "$REPO_DIR/install-wor.sh" \ + && ! grep -aqE 'command sudo -n -v >/dev/null 2>&1; sleep 30' "$REPO_DIR/install-wor.sh" \ + && pass "the sudo keepalive survives a failed refresh instead of ending the session" \ + || fail "a failed sudo refresh can still end the keepalive and strand the flash" + + keepalive_hang_dir="$(mktemp -d)" + cat > "$keepalive_hang_dir/sudo" <<'SH' +#!/bin/bash +printf '%s\n' "$*" >> "$KEEPALIVE_HANG_LOG" +case "$*" in + '-n -v') exit 1 ;; + '-A echo ok') exit 0 ;; +esac +exit 0 +SH + chmod +x "$keepalive_hang_dir/sudo" + keepalive_hang_log="$keepalive_hang_dir/log" + keepalive_hang_status=0 + PATH="$keepalive_hang_dir:$PATH" KEEPALIVE_HANG_LOG="$keepalive_hang_log" run_in_engine 'RUN_MODE=gui; MACOS_ASKPASS=/tmp/wor-test-askpass; captured="$(sudo echo ok 2>&1)"; printf "%s\n" "$captured"' >/dev/null 2>&1 || keepalive_hang_status=$? + [ "$keepalive_hang_status" == 0 ] \ + && grep -qF -- '-A echo ok' "$keepalive_hang_log" \ + && pass "GUI sudo does not start a keepalive inside command substitution" \ + || fail "GUI sudo can hang when first used inside command substitution" + rm -rf "$keepalive_hang_dir" + + #exactly one place may prompt: the main installer shell authenticates immediately before disk preparation. + [ "$(grep -cE '(^|[^n]) *sudo -v' "$REPO_DIR/install-wor-gui.sh")" == 0 ] \ + && [ "$(grep -cF 'sudo -v ||' "$REPO_DIR/install-wor.sh")" == 1 ] \ + && grep -qF 'sudo -v || error "Administrator authentication failed or was canceled.' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sudo -v || error "Administrator authentication failed or was canceled.'$'\n'' darwin_start_partition_finalizer_or_die' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Administrator access: requesting macOS password with the native WoR-Flasher dialog.' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if command sudo -n -v >/dev/null 2>&1;then' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'export WOR_GUI_SUDO_PROMPTED=1' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'command sudo -n "$@"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'refusing to prompt in the console.' "$REPO_DIR/install-wor.sh" \ + && pass "the flash asks for the password once before captured disk helpers run" \ + || fail "a credential is collected in more than one place, so the user is asked twice" + + #a frozen "Creating WOR_BOOT..." line while the password dialog is up reads as a hang; say what it's waiting on + grep -qF "emit_gui_task_progress 0 'Waiting for administrator access...'" "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF "emit_gui_task_progress 0 'Waiting for administrator access...'" "$REPO_DIR/install-wor.sh")" == 2 ] \ + && pass "the GUI progress window explains the administrator password dialog instead of appearing frozen" \ + || fail "the GUI progress window gives no indication it is waiting on the password dialog" + + sudo_retry_dir="$(mktemp -d)" + cat > "$sudo_retry_dir/sudo" <<'SH' +#!/bin/bash +printf '%s\n' "$*" >> "$SUDO_RETRY_LOG" +case "$*" in + '-n -v') exit 0 ;; + '-n failing-command') exit 42 ;; + '-A failing-command') exit 99 ;; +esac +exit 0 +SH + chmod +x "$sudo_retry_dir/sudo" + sudo_retry_log="$sudo_retry_dir/log" + sudo_retry_status=0 + PATH="$sudo_retry_dir:$PATH" SUDO_RETRY_LOG="$sudo_retry_log" run_in_engine 'RUN_MODE=gui MACOS_ASKPASS=/tmp/wor-test-askpass sudo failing-command' >/dev/null 2>&1 || sudo_retry_status=$? + [ "$sudo_retry_status" == 42 ] \ + && grep -qF -- '-n -v' "$sudo_retry_log" \ + && grep -qF -- '-n failing-command' "$sudo_retry_log" \ + && ! grep -qF -- '-A failing-command' "$sudo_retry_log" \ + && pass "GUI sudo does not ask again when an authenticated command fails" \ + || fail "GUI sudo retries with askpass after a real command failure" + rm -rf "$sudo_retry_dir" + + #after the first GUI sudo prompt, never ask again behind/near the progress window: either sudo -n works or the run fails + reauth_dir="$(mktemp -d)" + cat > "$reauth_dir/sudo" <<'SH' +#!/bin/bash +printf '%s\n' "$*" >> "$REAUTH_LOG" +case "$*" in + '-n -v') exit 1 ;; + '-A -v') exit 99 ;; + '-n failing-command') exit 42 ;; +esac +exit 1 +SH + chmod +x "$reauth_dir/sudo" + reauth_log="$reauth_dir/log" + reauth_status=0 + PATH="$reauth_dir:$PATH" REAUTH_LOG="$reauth_log" run_in_engine 'RUN_MODE=gui WOR_GUI_SUDO_PROMPTED=1 MACOS_ASKPASS=/tmp/wor-test-askpass sudo failing-command' >/dev/null 2>&1 || reauth_status=$? + [ "$reauth_status" == 1 ] \ + && ! grep -qF -- '-A -v' "$reauth_log" \ + && ! grep -qF -- '-A failing-command' "$reauth_log" \ + && grep -qF 'refusing to show a second password dialog during the flash' "$REPO_DIR/install-wor.sh" \ + && pass "GUI sudo refuses a second password prompt after first use" \ + || fail "GUI sudo can still ask for a second password after preauth" + rm -rf "$reauth_dir" + + macos_auth_checks + + #the late GPT retag runs through a helper started while authentication is fresh; it must wait for + #the go signal, retag and clear bit 63, and never touch the drive if the installer dies first + finalizer_dir="$(mktemp -d)" + cat > "$finalizer_dir/sudo" <<'SH' +#!/bin/bash +[ "${FINALIZER_EXIT_EARLY:-0}" == 1 ] && exit 0 +while [ "$1" == -n ];do shift; done +[ "$1" == -v ] && exit 0 +exec "$@" </dev/null +SH + cat > "$finalizer_dir/sgdisk" <<'SH' +#!/bin/bash +printf 'sgdisk %s\n' "$*" >> "$FINALIZER_LOG" +SH + chmod +x "$finalizer_dir/sudo" "$finalizer_dir/sgdisk" + finalizer_log="$finalizer_dir/calls" + finalizer_result="$(PATH="$finalizer_dir:$PATH" FINALIZER_LOG="$finalizer_log" run_in_engine ' + HOST_OS=Darwin RUN_MODE=gui MACOS_ASKPASS=/tmp/wor-unused-askpass WOR_GUI_SUDO_KEEPALIVE_PID=mock + darwin_start_partition_finalizer_or_die /dev/disk99 "$(command -v sgdisk)" + [ -f "$DARWIN_FINALIZE_DONE" ] && [ -O "$DARWIN_FINALIZE_DONE" ] && [ -r "$DARWIN_FINALIZE_DONE" ] && printf "precreated\n" + sleep 2 + [ -s "$FINALIZER_LOG" ] && echo retagged-early + darwin_finalize_partition_types_or_die && echo finalized' 2>/dev/null)" + orphan_log="$finalizer_dir/orphan" + ( PATH="$finalizer_dir:$PATH" FINALIZER_LOG="$orphan_log" run_in_engine ' + HOST_OS=Darwin RUN_MODE=gui MACOS_ASKPASS=/tmp/wor-unused-askpass WOR_GUI_SUDO_KEEPALIVE_PID=mock + darwin_start_partition_finalizer_or_die /dev/disk99 "$(command -v sgdisk)" + printf "%s\n" "$DARWIN_FINALIZE_GO" > "'"$finalizer_dir"'/orphan-go"' >/dev/null 2>&1 ) + sleep 3 + [ -f "$finalizer_dir/orphan-go" ] && : > "$(cat "$finalizer_dir/orphan-go")" + sleep 2 + [ "$finalizer_result" == $'precreated\nfinalized' ] \ + && grep -qF 'sgdisk -t 1:ef00 -c 1:WOR_BOOT -t 2:0700 -c 2:WOR_INSTALL /dev/rdisk99' "$finalizer_log" \ + && grep -qF 'sgdisk -A 1:clear:63 -A 2:clear:63 /dev/rdisk99' "$finalizer_log" \ + && [ ! -s "$orphan_log" ] \ + && pass "the GUI finalizer works without stdin, waits for its signal, and exits with the installer" \ + || fail "the partition finalizer retags early, skips bit 63, or outlives an aborted installer: '$finalizer_result'" + finalizer_start_result="$(PATH="$finalizer_dir:$PATH" FINALIZER_EXIT_EARLY=1 run_in_engine ' + error() { printf "%s\n" "$*"; exit 1; } + darwin_start_partition_finalizer_or_die /dev/disk99 "$(command -v sgdisk)" + printf "unexpected success\n"' 2>&1)" + [[ "$finalizer_start_result" == *'failed to start for /dev/disk99 (worker exit 0)'* ]] \ + && [[ "$finalizer_start_result" != *'unexpected success'* ]] \ + && pass "a worker exiting without readiness fails before disk preparation" \ + || fail "finalizer startup accepts a worker that never became ready: '$finalizer_start_result'" + rm -rf "$finalizer_dir" + + finalizer_failure_dir="$(mktemp -d)" + finalizer_failure_result="$(FINALIZER_FAILURE_DIR="$finalizer_failure_dir" run_in_engine ' + error() { printf "%s\n" "$*"; exit 1; } + DARWIN_FINALIZE_GO="$FINALIZER_FAILURE_DIR/go" + DARWIN_FINALIZE_DONE="$FINALIZER_FAILURE_DIR/done" + DARWIN_FINALIZE_LOG="$FINALIZER_FAILURE_DIR/log" + ( printf "mock authorization failure\n" > "$DARWIN_FINALIZE_LOG"; exit 42 ) & + DARWIN_FINALIZE_PID=$! + darwin_finalize_partition_types_or_die' 2>&1)" + [[ "$finalizer_failure_result" == *'status 42 without a readable result'* ]] \ + && [[ "$finalizer_failure_result" == *'mock authorization failure'* ]] \ + && pass "a failed finalizer preserves the worker exit status and diagnostics" \ + || fail "a failed finalizer hides its worker failure: '$finalizer_failure_result'" + rm -rf "$finalizer_failure_dir" + + #a failed flash must leave the log behind; the GUI has no terminal to fall back on + grep -qF 'saved_log="$(wor_log_file)"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Installer log saved to $saved_log' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -cF 'saved_log="$(gui_save_failure_log)"' "$REPO_DIR/install-wor-gui.sh")" == 4 ] \ + && pass "a failed run keeps its installer log for diagnosis" \ + || fail "a failed run deletes the only record of what went wrong" + + #canceling the password dialog fails before anything destructive runs; the completion screen must say so + #plainly instead of the generic "stopped unexpectedly" wording, which reads like a real crash + grep -qF "grep -qF 'Administrator authentication was canceled or unavailable' \"\$saved_log\"" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'No changes have been made to $DEVICE yet.' "$REPO_DIR/install-wor-gui.sh" \ + && pass "canceling the administrator password dialog is not reported as a script crash" \ + || fail "canceling the administrator password dialog is reported as if the script crashed" + + #mistyping the password 3 times gets the same friendly treatment, and both cases offer a retry + #instead of forcing a full app restart when nothing has been written to disk yet + grep -qF "grep -qF 'incorrect password attempts' \"\$saved_log\"" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "macos_choose '' \"\$password_retry_reason" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "retry Abort '' '' '' 'Try Again'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if [ "$password_retry_choice" == retry ];then' "$REPO_DIR/install-wor-gui.sh" \ + && pass "a failed password attempt offers to try again instead of only an OK button" \ + || fail "a failed password attempt does not offer to try again" + + #the downloads and extraction all finish before the first sudo call, so a retry must resume at the + #password step instead of repeating an entire run's worth of work for one mistyped password + grep -qF 'resume_at_flash=1' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'export WOR_RESUME_AT_FLASH="${resume_at_flash:-0}"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if [ "${WOR_RESUME_AT_FLASH:-0}" == 1 ] && flash_files_already_prepared ;then' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if [ "$RESUME_AT_FLASH" != 1 ];then' "$REPO_DIR/install-wor.sh" \ + && pass "retrying a password does not repeat the downloads and extraction" \ + || fail "retrying a password starts the whole run over" + + #the GUI sources install-wor.sh, which runs the update check; the installer subprocess must not + #repeat it, or every run (and every password retry) pays for a second network round-trip + grep -qF 'source "$cli_script" source' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'export NO_UPDATE=1' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the GUI checks for updates once, not again in the installer subprocess" \ + || fail "the GUI checks for updates twice per run" + + #the success screen stacks banner, message and button down the middle at the image's own size. + #Number() and the literal 1 are load-bearing: JXA returns these as strings, so '650' + 40 built a + #bogus window frame, and the bridged NSTextAlignmentCenter constant (2) draws right-aligned here. + grep -qF "const primaryLabel = imagePath.length > 0 ? 'Complete' : 'OK'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'bannerWidth = rep ? Number(rep.pixelsWide) : Number(bannerImage.size.width)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'width = Math.max(600, bannerWidth + contentMargin * 2)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if (imagePath.length > 0) label.setAlignment(1)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Math.round((width - bannerWidth) / 2), textY + textHeight + 24' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const okX = imagePath.length > 0 ? Math.round((width - okWidth) / 2) : width - 20 - okWidth' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the completion screen centres the banner, message and button in one column" \ + || fail "the completion screen no longer centres its banner, message and button" + + [ "$(grep -cF 'It is now safe to remove your USB drive.' "$REPO_DIR/install-wor-gui.sh")" == 2 ] \ + && grep -qF 'completion_text="Process completed successfully.' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'linux_completion_image="$(wor_yad_image_for_screen' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--form --align=center --image-on-top --buttons-layout=center --image="$linux_completion_image"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF -- '--field="It is now safe to remove your USB drive.":LBL' "$REPO_DIR/install-wor-gui.sh" \ + && pass "both success screens say when the USB drive is safe to remove" \ + || fail "a success screen does not tell the user the USB drive is safe to remove" + + #macOS can drop a freshly formatted FAT/exFAT volume part-way through the copy, so a mount point + #resolved once at the start goes stale and the next write dies with "No such file or directory" + grep -qF 'darwin_mount_point_or_die() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_mount_partition_or_die "$partition"' "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF 'darwin_mount_point_or_die "$PART1"' "$REPO_DIR/install-wor.sh")" -ge 4 ] \ + && [ "$(grep -cF 'darwin_mount_point_or_die "$PART2"' "$REPO_DIR/install-wor.sh")" -ge 3 ] \ + && pass "each copy step re-checks its mount instead of trusting a stale path" \ + || fail "a dropped volume mid-copy still fails the whole flash" + + #a flash runs long enough to walk away from, so both front-ends announce the result. soundNamed + #hands back a truthy wrapper for a missing name, so isNil is the only guard that actually works. + grep -qF 'const completionSound = $.NSSound.soundNamed(soundName)' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if (!completionSound.isNil()) completionSound.play' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'wor_play_result_sound() {' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'wor_play_result_sound success' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'wor_play_result_sound failure' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; PATH=/nonexistent wor_play_result_sound success >/dev/null 2>&1; echo \$?")" == 0 ] \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; PLAY_SOUND=0 wor_play_result_sound success >/dev/null 2>&1; echo \$?")" == 0 ] \ + && pass "finishing a flash plays a result sound without being able to fail the run" \ + || fail "the completion screen is silent, or its sound can fail the run" + + #the completion sound is configurable per platform, so one config file suits a Mac and a Linux box + sound_cfg_ok=0 + if command -v jq >/dev/null ;then + [ "$(jq -r '.notifications.playSound' "$REPO_DIR/config-templates/config.json")" == true ] \ + && [ "$(jq -r '.notifications.sounds.macos' "$REPO_DIR/config-templates/config.json")" == Glass ] \ + && [ "$(jq -r '.notifications.sounds.linux' "$REPO_DIR/config-templates/config.json")" == complete ] \ + && [ "$(jq -r '.notifications.sounds | has("windows")' "$REPO_DIR/config-templates/config.json")" == true ] \ + && [ "$(jq -r '.properties.notifications.properties.sounds.properties | keys | join(",")' "$REPO_DIR/config-templates/config.schema.json")" == 'linux,macos,windows' ] \ + && sound_cfg_ok=1 + else + sound_cfg_ok=1 #jq is what reads the config at runtime; without it there is nothing to assert + fi + #an unplayable name must never be handed to the player, and the catalogue must match the platform + sound_fallback="$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; COMPLETION_SOUND=NotARealSound wor_completion_sound")" + sound_explicit="$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; COMPLETION_SOUND=\"\$(wor_sound_options | sed -n 2p | cut -f1)\" wor_completion_sound")" + sound_platform_default="$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; wor_sound_default")" + [ "$sound_cfg_ok" == 1 ] \ + && [ -n "$sound_platform_default" ] \ + && [ "$sound_fallback" == "$sound_platform_default" ] \ + && [ "$sound_explicit" != "$sound_platform_default" ] \ + && grep -qF "set_bool_if_unset \"PLAY_SOUND\"" "$REPO_DIR/install-wor.sh" \ + && grep -qF ".notifications.sounds.macos" "$REPO_DIR/install-wor.sh" \ + && grep -qF ".notifications.sounds.linux" "$REPO_DIR/install-wor.sh" \ + && pass "the completion sound is configurable per platform and rejects an unplayable name" \ + || fail "the completion sound is not configurable, or an unplayable name reaches the player" + + #both front-ends must offer the same on/off switch and the same platform-appropriate menu + grep -qF "playSoundCheckbox = \$.NSButton.checkboxWithTitleTargetAction('Play a sound when the flash finishes'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'soundPopup = $.NSPopUpButton.alloc.initWithFramePullsDown' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "fields+=(\"--field=Play a sound when the flash finishes\":CHK" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'fields+=("--field=Completion sound":CB "$sound_items")' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const soundName = imagePath.length > 0 ? successSound : ' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'if (soundName.length > 0) {' "$REPO_DIR/install-wor-gui.sh" \ + && pass "both front-ends can turn the completion sound off and choose which one plays" \ + || fail "the completion sound cannot be turned off or chosen in both front-ends" + + #a flash is long enough to walk away from, so the desktop reports the result even when the app is + #buried. A denied permission or a missing notifier must never turn a finished flash into a failure. + notify_ok=0 + if command -v jq >/dev/null ;then + [ "$(jq -r '.notifications.showNotification' "$REPO_DIR/config-templates/config.json")" == true ] \ + && [ "$(jq -r '.properties.notifications.properties | has("showNotification")' "$REPO_DIR/config-templates/config.schema.json")" == true ] \ + && notify_ok=1 + else + notify_ok=1 + fi + [ "$notify_ok" == 1 ] \ + && grep -qF 'wor_show_result_notification() {' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF "set_bool_if_unset \"SHOW_NOTIFICATION\"" "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wor_show_result_notification success' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'wor_show_result_notification failure' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "notificationCheckbox = \$.NSButton.checkboxWithTitleTargetAction('Show a notification when the flash finishes'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "fields+=(\"--field=Show a notification when the flash finishes\":CHK" "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/metadata.sh'; source '$REPO_DIR/src/lib/gui.sh'; SHOW_NOTIFICATION=0 wor_show_result_notification success >/dev/null 2>&1; echo \$?")" == 0 ] \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/metadata.sh'; source '$REPO_DIR/src/lib/gui.sh'; PATH=/nonexistent wor_show_result_notification failure >/dev/null 2>&1; echo \$?")" == 0 ] \ + && pass "finishing a flash posts a desktop notification without being able to fail the run" \ + || fail "the flash result is not announced, or its notification can fail the run" + + #a device path reaching AppleScript as source rather than argv would let it close the string and run + grep -qF 'display notification (item 2 of argv) with title (item 1 of argv)' "$REPO_DIR/src/lib/gui.sh" \ + && ! grep -qE 'display notification "\$' "$REPO_DIR/src/lib/gui.sh" \ + && pass "the notification passes its text as argv, never as AppleScript source" \ + || fail "the notification interpolates text into AppleScript source" + + #a resume must never be taken on trust: the files the disk step reads have to be there, and the + #step counter has to continue rather than restarting the progress bar at step 1 + resume_dir="$(mktemp -d)" + mkdir -p "$resume_dir/winfiles_22631.2861_en-us/bootpart" "$resume_dir/peinstaller/efi" \ + "$resume_dir/peinstaller/winpe/2" "$resume_dir/pi4-uefipackage" "$resume_dir/driverpackage" + touch "$resume_dir/winfiles_22631.2861_en-us/alldone" + echo data > "$resume_dir/winfiles_22631.2861_en-us/install.wim" + resume_with_files="$(run_in_engine 'cd "'"$resume_dir"'"; flash_files_already_prepared && printf %s "ready:$winfiles" || printf missing')" + rm -rf "$resume_dir/driverpackage" + resume_without_drivers="$(run_in_engine 'cd "'"$resume_dir"'"; flash_files_already_prepared && printf ready || printf missing')" + resume_pi5_no_drivers="$(run_in_engine 'cd "'"$resume_dir"'"; RPI_MODEL=5; mkdir -p pi5-uefipackage; flash_files_already_prepared && printf ready || printf missing')" + [ "$resume_with_files" == 'ready:winfiles_22631.2861_en-us' ] \ + && [ "$resume_without_drivers" == missing ] \ + && [ "$resume_pi5_no_drivers" == ready ] \ + && grep -qF '[ "$RPI_MODEL" == 5 ] && STEP_NUM=3 || STEP_NUM=4' "$REPO_DIR/install-wor.sh" \ + && pass "a resume is refused unless every prepared file is still present" \ + || fail "a resume can skip preparation when files are missing" + rm -rf "$resume_dir" + + #Tool output can include raw bytes; macOS sed must not reject the failure log before its dialog appears. + grep -qF "LC_ALL=C sed 's/\\x1b\\[[0-9;]*[A-Za-z]//g; s/\\r//g' \"\$1\"" "$REPO_DIR/install-wor-gui.sh" \ + && pass "GUI failure-log sanitization tolerates non-UTF-8 tool output" \ + || fail "GUI failure-log sanitization can fail on non-UTF-8 tool output" + + #Advanced Options returns user-editable config and account fields; parse those bytes after AppKit + #returns so malformed input cannot make BSD sed abort and close the settings flow. + advanced_result_line="$(grep -anF 'result="$(wor_osascript -l JavaScript - "$checkbox_spec"' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" + advanced_byte_mode_line="$(grep -anF ' LC_ALL=C' "$REPO_DIR/install-wor-gui.sh" | awk -F: -v start="$advanced_result_line" '$1 > start {print $1; exit}')" + advanced_status_line="$(grep -anF 'status="$(printf' "$REPO_DIR/install-wor-gui.sh" | cut -d: -f1)" + [ -n "$advanced_result_line" ] && [ -n "$advanced_byte_mode_line" ] && [ -n "$advanced_status_line" ] \ + && [ "$advanced_result_line" -lt "$advanced_byte_mode_line" ] && [ "$advanced_byte_mode_line" -lt "$advanced_status_line" ] \ + && pass "Advanced Options parses user-editable result bytes without locale-dependent sed failures" \ + || fail "Advanced Options can exit when BSD sed rejects a user-editable byte" + + #the bar should advance inside a step, not jump once per step + grep -qF 'emit_gui_substep() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'emit_gui_task_progress() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'emit_gui_progress "TASK' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'with_progress_capture pv -f -N' "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF 'with_progress_capture pv -f -N' "$REPO_DIR/install-wor.sh")" == 3 ] \ + && grep -qF 'bar.maxValue = stepTotal * 100' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'bar.doubleValue = (stepNum - 1) * 100 + within' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'taskBar = $.NSProgressIndicator.alloc.initWithFrame' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'taskBar.maxValue = 100' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'taskBar.doubleValue = Math.max(0, Math.min(100, currentPercent))' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'taskPercentLabel.stringValue = taskPercentText' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "const taskLine = lastMatch(lines, 'TASK\\t')" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'taskText = taskLabel' "$REPO_DIR/install-wor-gui.sh" \ + && pass "progress is captured within each step and carried into both progress bars" \ + || fail "progress still jumps a whole step at a time" + + copy_error_message="$(run_in_engine 'copy_local_file_with_progress test /tmp/does-not-exist /tmp/wor-flasher-copy-test; darwin_report_copy_failure /tmp "copy test" user' 2>&1)" + printf '%s' "$copy_error_message" | grep -qF 'Last copy error: source file does not exist: /tmp/does-not-exist' \ + && pass "copy failure diagnostics keep the real copy error in the GUI message" \ + || fail "copy failure diagnostics hide the real copy error: $copy_error_message" + rm -f /tmp/wor-flasher-copy-test /tmp/.wor-flasher-write-probe + + copy_io_message="$(run_in_engine 'COPY_WITH_PROGRESS_ERROR="target reported Input/output error while copying a to b"; darwin_report_copy_failure /tmp "copy test" user' 2>&1)" + printf '%s' "$copy_io_message" | grep -qF 'target volume reported an Input/output error while writing' \ + && pass "copy failure diagnostics identify target media I/O errors" \ + || fail "copy failure diagnostics misclassify target media I/O errors: $copy_io_message" + + #sub() is a built-in awk function, so using it as a variable is a syntax error + linux_awk="$(sed -n '/# LINUX_PROGRESS_AWK_BEGIN/,/# LINUX_PROGRESS_AWK_END/p' "$REPO_DIR/install-wor-gui.sh" | sed '1d; /^awk -F/d; $d')" + if [ -n "$linux_awk" ] && printf 'STEP\t3\t8\tThird\nSUBSTEP\t50\nTASK\t50\tinstall.wim\n' | awk -F'\t' "$linux_awk" >/dev/null 2>&1 ;then + [ "$(printf 'STEP\t3\t8\tThird\nSUBSTEP\t50\nTASK\t50\tinstall.wim\n' | awk -F'\t' "$linux_awk" | grep -E '^1:' | grep -v '#' | tail -n1 | cut -d: -f2)" == 31 ] \ + && printf 'STEP\t3\t8\tThird\nSUBSTEP\t50\nTASK\t50\tinstall.wim\n' | awk -F'\t' "$linux_awk" | grep -qF '1:# Overall - Step 3/8: install.wim (50%)' \ + && printf 'STEP\t3\t8\tThird\nSUBSTEP\t50\nTASK\t50\tinstall.wim\n' | awk -F'\t' "$linux_awk" | grep -qF '2:# Sub-progress: install.wim (50%)' \ + && pass "the Linux progress program runs and maps a mid-step percentage correctly" \ + || fail "the Linux progress program computes the wrong overall percentage" + else + fail "the Linux progress awk program has a syntax error" + fi + + #wimverify does not consistently report byte progress, so verification needs deterministic milestones. + grep -qF 'report_verification_task() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'report_verification_task 45 "Verifying boot.wim integrity"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'report_verification_task 60 "Verifying install.wim integrity"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'report_verification_task 100 "Written image verified"' "$REPO_DIR/install-wor.sh" \ + && pass "verification reports milestones when wimverify is silent" \ + || fail "verification can stall the GUI progress bar while wimverify is silent" + + #USE_CACHE has three values, so both GUIs need a menu, and it has to reach the installer + grep -qF 'cachePopup.addItemWithTitle' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "out.push(String(cachePopup.indexOfSelectedItem))" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'case "$(printf' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"--field=Downloaded files":CB "$cache_items"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"--field=Create an optional local Windows administrator account":CHK' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"--field=Windows password":H' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"--field=Windows locale":CB "$locale_value"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "awk -F': ' '{print \$1}'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'SKIP_IMAGE_VERIFICATION HIDE_EMPTY_DRIVES USE_CACHE' "$REPO_DIR/install-wor.sh" \ + && pass "both GUIs can choose the download cache mode and pass it to the installer" \ + || fail "the download cache mode is not adjustable from the GUI" + + #a one-line `case` inside $( ) is mis-parsed at the first ')', leaking raw shell into the dialog + if ! grep -qE '\$\(case .* in [^)]*\)' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qE '\$\(case .* in [^)]*\)' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'cache_mode_label() {' "$REPO_DIR/install-wor.sh" ;then + #render it the way the confirmation screens do + cache_label_fn="$(sed -n '/^cache_mode_label() {/,/^}/p' "$REPO_DIR/install-wor.sh")" + cache_rendered="$(bash -c "$cache_label_fn"$'\n''cache_mode_label 2' 2>/dev/null)" + [ "$cache_rendered" == 'Trust the cache without checking' ] \ + && pass "the confirmation screens show the cache mode as words, not shell source" \ + || fail "the cache mode does not render correctly on the confirmation screens" + else + fail "an inline case in a command substitution will leak shell source into a dialog" + fi + + #clearing several GB used to sit on a dead bar with no indication anything was happening + grep -qF 'clear_cached_components() {' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'status "Deleting $(basename "$target") ($((removed+1)) of $total)"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'emit_gui_substep $((removed * 100 / total))' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'rm -rf "$PWD/peinstaller" "$PWD/driverpackage"' "$REPO_DIR/install-wor.sh" \ + && grep -qF '} else if (subLine.length > 0) {' "$REPO_DIR/install-wor-gui.sh" \ + && pass "clearing the cache reports each deletion and moves the bar before step 1" \ + || fail "clearing the cache gives no progress feedback" + + #the progress window needs a way out, a step counter, and the usual window buttons + grep -qF "abortClicked:" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "'windowShouldClose:'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'let style = $.NSWindowStyleMaskTitled' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'if (options.miniaturizable !== false) style = style | $.NSWindowStyleMaskMiniaturizable' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'stepLabel.stringValue = stepStr' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'abort_marker="$(mktemp -u)"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'kill_process_tree "$installer_pid"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Stop flashing this drive?' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the progress window can be aborted, shows step x of y, and has close and minimise" \ + || fail "the progress window cannot be aborted or lacks its window controls" + + grep -qF 'const width = 680' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const height = 330' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const logoWidth = 56' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const logoHeight = 173' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const progressX = 96' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'logoView.imageScaling = $.NSImageScaleProportionallyUpOrDown' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'bar = $.NSProgressIndicator.alloc.initWithFrame($.NSMakeRect(progressX' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'noteLabel.alignment = 1' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'abortButton.frame = $.NSMakeRect(Math.round((width - abortWidth) / 2)' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the macOS progress window is larger, branded, and has a centered footer" \ + || fail "the macOS progress window lost its larger branded layout" + + #clicking the Dock icon sends aevt/rapp; without a handler a minimised window can never be restored + gui_windows="$(grep -cF 'window = worMakeWindow({' "$REPO_DIR/install-wor-gui.sh")" + [ "$(grep -cF "'handleReopenEvent:withReplyEvent:': {" "$REPO_DIR/install-wor-gui.sh")" == "$gui_windows" ] \ + && [ "$(grep -cF 'worInstallWindowHandlers(controller)' "$REPO_DIR/install-wor-gui.sh")" == "$gui_windows" ] \ + && grep -qF '0x61657674, 0x72617070' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'if (window.isMiniaturized) window.deminiaturize(null)' "$REPO_DIR/install-wor-gui.sh" \ + && pass "clicking the Dock icon restores a minimised window" \ + || fail "a minimised window cannot be restored from the Dock" + + #every screen must build its window through the one shared helper, or their title bars drift apart again + [ "$gui_windows" == 6 ] \ + && grep -qF 'window = worMakeWindow({' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'NSWindow.alloc.initWithContentRectStyleMaskBackingDefer' "$REPO_DIR/install-wor-gui.sh" \ + && ! grep -qF 'NSWindow.alloc.initWithContentRectStyleMaskBackingDefer' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'wor_jxa_window_lib() {' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'function worMakeWindow(options) {' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'function worInstallAppMenu(app, appTitle, windowTitle, iconPath) {' "$REPO_DIR/src/lib/gui.sh" \ + && [ "$(grep -cF 'worInstallAppMenu(app, appTitle, windowTitle, iconPath)' "$REPO_DIR/install-wor-gui.sh")" == "$gui_windows" ] \ + && [ "$(grep -cF 'wor_jxa_window_lib' "$REPO_DIR/install-wor-gui.sh")" == "$gui_windows" ] \ + && pass "every window is built by the shared window helper" \ + || fail "a window is built by hand instead of the shared helper" + + #macOS does not deliver app-menu action dispatch to ANY item - custom or native, including Quit - + #while a screen's own app.runModalForWindow session is active, and every screen sharing this menu + #is modal; a clickable-looking item that silently does nothing is worse than an informational one + ! grep -qF "'showAbout:'" "$REPO_DIR/src/lib/gui.sh" \ + && ! grep -qF "addItemWithTitleActionKeyEquivalent('Hide " "$REPO_DIR/src/lib/gui.sh" \ + && ! grep -qF "'unhideAllApplications:'" "$REPO_DIR/src/lib/gui.sh" \ + && ! grep -qF "addItemWithTitleActionKeyEquivalent('Quit " "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF 'infoItem.enabled = false' "$REPO_DIR/src/lib/gui.sh" \ + && pass "the shared app menu shows product info instead of non-functional items during a modal screen" \ + || fail "the shared app menu still offers items that cannot respond while a modal screen is showing" + + #the launcher's own startup window uses a plain app.run() loop, not app.runModalForWindow, so its + #app-menu items are not subject to the same modal-session dispatch restriction and can stay wired + #to nil-target native actions the normal way + launcher_file="$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" + grep -qF 'hideItem.enabled = true' "$launcher_file" \ + && grep -qF 'hideOthersItem.enabled = true' "$launcher_file" \ + && grep -qF 'showAllItem.enabled = true' "$launcher_file" \ + && grep -qF 'quitItem.enabled = true' "$launcher_file" \ + && ! grep -qF 'hideItem.target' "$launcher_file" \ + && pass "the launcher's own app menu explicitly enables its native app-menu actions" \ + || fail "the launcher's app menu has an action left disabled, so it silently does nothing" + + #the launcher runs before any runtime is verified, so it cannot load the helper; keep it in step by hand + grep -qF 'window.title = windowTitle' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF 'NSWindowStyleMaskTitled | $.NSWindowStyleMaskMiniaturizable' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF 'if (zoomButton) zoomButton.hidden = true' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF "appMenu.addItemWithTitleActionKeyEquivalent('About ' + appTitle, 'showAbout:', '')" "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF 'WorLauncherAboutController' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF 'appMenu.autoenablesItems = false' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF 'aboutItem.enabled = true' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && ! grep -qF 'orderFrontStandardAboutPanel:' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF "appMenu.addItemWithTitleActionKeyEquivalent('Quit ' + appTitle, 'terminate:', 'q')" "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && pass "the launcher startup window matches the rest of the app" \ + || fail "the launcher startup window has drifted from the other windows" + + #the close box quits the launcher outright instead of hiding it while verification keeps running unattended + grep -qF '$.NSWindowStyleMaskMiniaturizable | $.NSWindowStyleMaskClosable' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF "'windowShouldClose:': {" "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && grep -qF 'window.setDelegate(controller)' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" \ + && pass "the launcher's close box quits instead of only hiding the window" \ + || fail "the launcher's close box is disabled or silently hides the window" + + #an option that departs from, or matches, the tested defaults must say so rather than look like every other checkbox + grep -qF "caution: parts[3] === '1'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'function worAnnotateCheckbox(checkbox, label, note, color)' "$REPO_DIR/src/lib/gui.sh" \ + && grep -qF "worAnnotateCheckbox(checkbox, rows[i].label, 'Not recommended', \$.NSColor.systemRedColor)" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "'Recommended', \$.NSColor.systemGreenColor)" "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; wor_advanced_caution uefi")" == 1 ] \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; wor_advanced_caution verify")" == 1 ] \ + && [ "$(bash -c "source '$REPO_DIR/src/lib/gui.sh'; wor_advanced_caution drivers")" == 0 ] \ + && pass "advanced options are marked recommended or not recommended" \ + || fail "an option that departs from the tested defaults is not flagged" + + #Quit during a flash used to exit on the spot: a bogus failure dialog, with the flash left running + progress_block="$(sed -n "/^ progress_jxa=\"\$(wor_jxa_window_lib; cat <<'JXA'\$/,/^JXA\$/p" "$REPO_DIR/install-wor-gui.sh")" + ! printf '%s' "$progress_block" | grep -qF '$.exit(0)' \ + && printf '%s' "$progress_block" | grep -qF 'if (confirmAbort()) app.stopModalWithCode($.NSCancelButton)' \ + && [ "$(grep -c ' \$.exit(0)' "$REPO_DIR/install-wor-gui.sh")" == 4 ] \ + && grep -qF '[ -f "$done_marker" ] || touch "$abort_marker"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'wait "$installer_pid" 2>/dev/null' "$REPO_DIR/install-wor-gui.sh" \ + && pass "quitting mid-flash confirms first and any unexpected window exit stops the installer" \ + || fail "quitting mid-flash reports a bogus failure or leaves the installer running" + + #the completion dialog has an "Open Log" button that extracts the log path and opens it in the default editor + completion_block="$(sed -n "/^ completion_jxa=\"\$(wor_jxa_window_lib; cat <<'JXA'\$/,/^JXA\$/p" "$REPO_DIR/install-wor-gui.sh")" + printf '%s' "$completion_block" | grep -qF "'openLogClicked:': {" \ + && printf '%s' "$completion_block" | grep -qF "openButton = $.NSButton.buttonWithTitleTargetAction('Open Log'" \ + && printf '%s' "$completion_block" | grep -qF "$.NSWorkspace.sharedWorkspace.openFileWithApplication" \ + && printf '%s' "$completion_block" | grep -qF "const logMatch = message.match(/Full log: (.+)$/)" \ + && pass "completion dialog can open the log file from the message" \ + || fail "completion dialog cannot open the log file" + + #the completion dialog has a "Copy" button that copies the log path to the clipboard + printf '%s' "$completion_block" | grep -qF "'copyLogClicked:': {" \ + && printf '%s' "$completion_block" | grep -qF "copyButton = $.NSButton.buttonWithTitleTargetAction('Copy'" \ + && printf '%s' "$completion_block" | grep -qF "$.NSPasteboard.generalPasteboard" \ + && printf '%s' "$completion_block" | grep -qF 'pb.setStringForType($(logPath)' \ + && pass "completion dialog can copy the log path to the clipboard" \ + || fail "completion dialog cannot copy the log path" + + #macOS privacy denials should let the user jump straight to the relevant Settings pane from the failure dialog. + printf '%s' "$completion_block" | grep -qF "'openSettingsClicked:': {" \ + && printf '%s' "$completion_block" | grep -qF "settingsButton = $.NSButton.buttonWithTitleTargetAction('Open Settings'" \ + && printf '%s' "$completion_block" | grep -qF '$.NSWorkspace.sharedWorkspace.openURL' \ + && grep -qF "privacy_settings_url='x-apple.systempreferences:com.apple.preference.security?Privacy_FilesAndFolders'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "privacy_settings_url='x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles'" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Click Open Settings, then in Files and Folders enable Removable Volumes for bash' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'click +, press Shift-Command-G, enter /bin/bash, click Open' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Click Open Settings, then enable Full Disk Access' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Then quit WoR-Flasher completely and try again.' "$REPO_DIR/install-wor-gui.sh" \ + && pass "completion dialog can open macOS privacy settings after access denials" \ + || fail "completion dialog cannot open macOS privacy settings after access denials" + + #The banner carries alpha; do not put a full white panel behind it, and never bridge Core Animation CGColor through JXA. + printf '%s' "$completion_block" | grep -qF 'const imageView = $.NSImageView.alloc.initWithFrame' \ + && ! printf '%s' "$completion_block" | grep -qF 'imageBacking' \ + && ! printf '%s' "$completion_block" | grep -qF '.CGColor' \ + && pass "completion banner preserves its alpha without a crash-prone backing layer" \ + || fail "completion banner has an opaque or crash-prone backing layer" + + #when an error occurs in the installer, gui_error_dialog writes to WOR_GUI_ERROR_MARKER with touch+sync before showing its dialog + gui_error_block="$(sed -n "/^gui_error_dialog() {/,/^}/p" "$REPO_DIR/install-wor.sh" | head -n 20)" + printf '%s' "$gui_error_block" | grep -qF 'mkdir -p "$(dirname "$WOR_GUI_ERROR_MARKER")' \ + && printf '%s' "$gui_error_block" | grep -qF 'touch "$WOR_GUI_ERROR_MARKER"' \ + && printf '%s' "$gui_error_block" | grep -qF 'sync' \ + && pass "gui_error_dialog reliably creates error_marker with touch+sync before dialog" \ + || fail "gui_error_dialog does not reliably create error_marker" + + #the error marker is a synchronization signal; gui_error_dialog creates it before opening its own native dialog + marker_check_fn="$(sed -n '/^installer_showed_own_error() {/,/^}/p' "$REPO_DIR/install-wor-gui.sh")" + printf '%s' "$marker_check_fn" | grep -qF '[ -e "$error_marker" ]' \ + && ! printf '%s' "$marker_check_fn" | grep -qF '[ -s "$error_marker" ]' \ + && [ "$(grep -cF 'if installer_showed_own_error ;then' "$REPO_DIR/install-wor-gui.sh")" == 2 ] \ + && pass "both GUIs trust the native error marker before skipping the completion dialog" \ + || fail "GUI ignores the native error marker and can show a duplicate failure dialog" + + error_fn="$(sed -n '/^error() {/,/^}/p' "$REPO_DIR/install-wor.sh")" + duplicate_dialog_count="$(run_in_engine 'RUN_MODE=gui; WOR_GUI_PROGRESS_FILE=/tmp/progress; gui_error_dialog() { echo duplicate-dialog; }; error broken' 2>&1 | grep -c duplicate-dialog || true)" + printf '%s' "$error_fn" | grep -qF '[ -z "${WOR_GUI_PROGRESS_FILE:-}" ]' \ + && [ "$duplicate_dialog_count" == 0 ] \ + && pass "progress-owned GUI failures do not stack an engine modal over the progress window" \ + || fail "an installer failure can still leave a duplicate modal over the progress window" + + #aborting must take down the sudo-owned children too, not just the top-level job + kill_tree_dir="$(mktemp -d)" + + sed -n '/^kill_process_tree() {/,/^}/p' "$REPO_DIR/install-wor-gui.sh" > "$kill_tree_dir/fn.sh" + cat > "$kill_tree_dir/child.sh" <<'SH' +#!/bin/bash +sleep 60 & +printf '%s\n' "$!" >> "$KILL_TREE_PID_FILE" +wait +SH + cat > "$kill_tree_dir/parent.sh" <<'SH' +#!/bin/bash +"$(dirname "$0")/child.sh" & +printf '%s\n' "$!" >> "$KILL_TREE_PID_FILE" +wait +SH + : > "$kill_tree_dir/pids" + chmod +x "$kill_tree_dir/child.sh" "$kill_tree_dir/parent.sh" + ( + #shellcheck disable=SC1090 + . "$kill_tree_dir/fn.sh" + KILL_TREE_PID_FILE="$kill_tree_dir/pids" "$kill_tree_dir/parent.sh" & tree_pid=$! + printf '%s\n' "$tree_pid" >> "$kill_tree_dir/pids" + sleep 1 + kill_process_tree "$tree_pid" 2>/dev/null + sleep 1 + kill -0 "$tree_pid" 2>/dev/null && exit 1 + pgrep -f "$kill_tree_dir/child.sh" >/dev/null 2>&1 && exit 1 + exit 0 + ) >/dev/null 2>&1 \ + && pass "aborting stops the installer and every process it started" \ + || fail "aborting leaves the flash running in the background" + while IFS= read -r owned_pid ;do + if kill -0 "$owned_pid" 2>/dev/null;then + kill -TERM "$owned_pid" 2>/dev/null || ! kill -0 "$owned_pid" 2>/dev/null \ + || fail "could not stop test-owned process $owned_pid" + fi + done < "$kill_tree_dir/pids" + rm -rf "$kill_tree_dir" + + abort_marker_dir="$(mktemp -d)" + abort_marker_path="$abort_marker_dir/abort" + : > "$abort_marker_path" + abort_signal_out="$(run_in_engine 'RUN_MODE=gui WOR_GUI_ABORT_MARKER="'"$abort_marker_path"'"; gui_error_dialog() { echo unexpected-error-dialog; }; handle_interrupt' 2>&1; echo "rc=$?")" + grep -qF 'export WOR_GUI_ABORT_MARKER="${abort_marker:-}"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'Interrupted at GUI request.' <<< "$abort_signal_out" \ + && ! grep -qF 'unexpected-error-dialog' <<< "$abort_signal_out" \ + && [ "$(tail -n1 <<< "$abort_signal_out")" == 'rc=130' ] \ + && pass "GUI abort does not stack a generic Interrupted error dialog" \ + || fail "GUI abort still shows the generic Interrupted error dialog" + rm -rf "$abort_marker_dir" + + shared_function_checks +} + +#Runs the real functions out of install-wor.sh rather than restating their logic here, so a test can +#never pass against behaviour the shipped script no longer has. +run_in_engine() { #Input: shell code. Runs it with install-wor.sh sourced and a representative run configured. + env -u CONFIG_TXT -u DEVICE -u DL_DIR -u SOURCE_FILE NO_UPDATE=1 DIRECTORY="$REPO_DIR" \ + bash -c ' + #shellcheck disable=SC1090 + source "$DIRECTORY/install-wor.sh" source >/dev/null 2>&1 + RPI_MODEL=4 BID=22631.2861 WIN_LANG=en-us DEVICE=/dev/does-not-exist + CAN_INSTALL_ON_SAME_DRIVE=1 DL_DIR=/tmp/wor-test-dl + '"$1" +} + +macos_auth_checks() { + info "== macOS finalizer authorization (mocked) ==" + local auth_dir scenario case_dir result result_status prompts cleanup_result + auth_dir="$(mktemp -d)" || die "Could not create the authorization-test workspace." + cat > "$auth_dir/sudo" <<'SH' +#!/bin/bash +if [ "$*" == '-A -v' ];then + printf 'prompt\n' >> "$AUTH_TEST_DIR/calls" + if [ "$AUTH_TEST_SCENARIO" == canceled ];then + printf 'mock password dialog canceled\n' >&2 + exit 1 + fi + printf '%s\n' "$PPID" > "$AUTH_TEST_DIR/authorized-parent" + exit 0 +fi +[ "${1:-}" == -n ] || { printf 'mock sudo: refusing an interactive worker invocation\n' >&2; exit 99; } +while [ "${1:-}" == -n ];do shift; done +if [ "$*" == -v ] && [ "$AUTH_TEST_SCENARIO" == cached ] && [ ! -f "$AUTH_TEST_DIR/authorized-parent" ];then + printf '%s\n' "$PPID" > "$AUTH_TEST_DIR/authorized-parent" +fi +authorized_parent='' +[ ! -f "$AUTH_TEST_DIR/authorized-parent" ] || read -r authorized_parent < "$AUTH_TEST_DIR/authorized-parent" +if [ "$authorized_parent" != "$PPID" ];then + printf 'mock sudo: credentials unavailable for parent %s (authenticated parent %s)\n' "$PPID" "${authorized_parent:-none}" >&2 + exit 1 +fi +[ "$*" == -v ] && exit 0 +if [ "$#" != 14 ] || [ "$1" != bash ] || [ "$2" != -c ] || [ "$4" != wor-partition-finalizer ] \ + || [ "${11}" != "$AUTH_TEST_DIR/worker/ready" ];then + printf 'mock sudo: refusing unexpected command or readiness path\n' >&2 + exit 99 +fi +printf 'worker\n' >> "$AUTH_TEST_DIR/calls" +[ "$AUTH_TEST_SCENARIO" != early-exit ] || exit 0 +case "$AUTH_TEST_SCENARIO" in + pi3-expired|pi3-failure) + [ "$(type -P dd)" == "${AUTH_TEST_DIR%/*}/dd" ] || exit 99 + exec "$@" </dev/null ;; +esac +#Simulate only the process handshake; never execute the privileged script or touch a disk. +printf 'ready\n' > "${11}" +attempt=0 +while [ ! -e "$8" ] && [ "$attempt" -lt 200 ];do + kill -0 "${10}" 2>/dev/null || exit 1 + sleep 0.1 + attempt=$((attempt + 1)) +done +if [ -e "$8" ];then + printf '0\n' > "$9" + exit 0 +fi +printf 'mock finalizer timed out waiting for the test\n' >&2 +exit 124 +SH + cat > "$auth_dir/dd" <<'SH' +#!/bin/bash +if [ "$#" != 3 ] || [ "$1" != "if=$AUTH_TEST_DIR/gptpatch.img" ] \ + || [ "$2" != of=/dev/rdoes-not-exist ] || [ "$3" != conv=fsync ];then + printf 'mock dd: refusing unexpected patch arguments\n' >&2 + exit 99 +fi +printf 'dd\n' >> "$AUTH_TEST_DIR/disk-commands" +if [ "$AUTH_TEST_SCENARIO" == pi3-failure ];then + printf 'mock dd failed\n' >&2 + exit 42 +fi +SH + chmod +x "$auth_dir/sudo" "$auth_dir/dd" || die "Could not make the authorization mocks executable." + for scenario in fresh cached canceled unavailable early-exit expired-after-start pi3-expired pi3-failure ;do + case_dir="$auth_dir/$scenario" + mkdir -p "$case_dir/worker" || die "Could not create the authorization-test case." + : > "$case_dir/calls" + : > "$case_dir/disk-commands" + printf 'mock Pi3 GPT patch\n' > "$case_dir/gptpatch.img" + result_status=0 + result="$(PATH="$auth_dir:$PATH" AUTH_MOCK_SUDO="$auth_dir/sudo" AUTH_TEST_DIR="$case_dir" AUTH_TEST_SCENARIO="$scenario" run_in_engine ' + error() { printf "%s\n" "$*"; exit 1; } + [ "$(type -P sudo)" == "$AUTH_MOCK_SUDO" ] || error "Refusing to use real sudo in an authorization test." + mktemp() { + [ "$*" == -d ] || return 1 + printf "%s\n" "$AUTH_TEST_DIR/worker" + } + HOST_OS=Darwin RUN_MODE=gui MACOS_ASKPASS=/tmp/wor-unused-askpass + unset WOR_GUI_SUDO_KEEPALIVE_PID + WOR_GUI_SUDO_PROMPTED=0 WOR_GUI_PROGRESS_FILE="" + sudo -v || error "mock authentication was canceled" + if [ -n "${WOR_GUI_SUDO_KEEPALIVE_PID:-}" ];then + kill -STOP "$WOR_GUI_SUDO_KEEPALIVE_PID" + for child in $(pgrep -P "$WOR_GUI_SUDO_KEEPALIVE_PID");do + kill -TERM "$child" 2>/dev/null || ! kill -0 "$child" 2>/dev/null + done + kill -TERM "$WOR_GUI_SUDO_KEEPALIVE_PID" + kill -CONT "$WOR_GUI_SUDO_KEEPALIVE_PID" + wait "$WOR_GUI_SUDO_KEEPALIVE_PID" + error "macOS unexpectedly started a timestamp keepalive" + fi + [ "$AUTH_TEST_SCENARIO" != unavailable ] || rm -f "$AUTH_TEST_DIR/authorized-parent" + patch="" + case "$AUTH_TEST_SCENARIO" in + pi3-*) patch="$AUTH_TEST_DIR/gptpatch.img" ;; + esac + darwin_start_partition_finalizer_or_die /dev/does-not-exist /not-executed/sgdisk "$patch" + [ -s "$DARWIN_FINALIZE_READY" ] && [ -O "$DARWIN_FINALIZE_DONE" ] \ + || error "Finalizer returned without readiness or a user-owned result file." + if [ -n "$patch" ];then + [ ! -s "$AUTH_TEST_DIR/disk-commands" ] || error "Pi3 patch ran before its signal." + rm -f "$AUTH_TEST_DIR/authorized-parent" + darwin_apply_pi3_gpt_patch_or_die + darwin_apply_pi3_gpt_patch_or_die + sleep 2 + kill -TERM "$DARWIN_FINALIZE_PID" || error "Could not stop the mock worker." + wait "$DARWIN_FINALIZE_PID" + [ "$?" == 143 ] || error "Mock worker did not stop as requested." + : > "$AUTH_TEST_DIR/verify-reached" + printf "patched\n" + exit 0 + fi + : > "$AUTH_TEST_DIR/prepare-reached" + if [ "$AUTH_TEST_SCENARIO" == expired-after-start ];then + rm -f "$AUTH_TEST_DIR/authorized-parent" + darwin_finalize_partition_types_or_die + else + : > "$DARWIN_FINALIZE_GO" + wait "$DARWIN_FINALIZE_PID" || error "Mock worker failed." + fi + printf "ready\n"' 2>&1)" || result_status=$? + prompts="$(awk '/^prompt$/ { count++ } END { print count + 0 }' "$case_dir/calls")" + case "$scenario" in + fresh|cached|expired-after-start) + if [ "$result_status" == 0 ] && grep -qFx ready <<<"$result" \ + && [ -f "$case_dir/prepare-reached" ] \ + && { [ "$scenario:$prompts" == fresh:1 ] || [ "$scenario:$prompts" == cached:0 ] || [ "$scenario:$prompts" == expired-after-start:1 ]; };then + pass "$scenario GUI authorization remains usable by the background finalizer" + else + fail "$scenario GUI authorization did not reach finalizer readiness (exit $result_status): $result" + fi ;; + canceled) + if [ "$result_status" == 1 ] && [ "$prompts" == 1 ] \ + && [[ "$result" == *'mock authentication was canceled'* ]] \ + && ! grep -qFx worker "$case_dir/calls" && [ ! -e "$case_dir/prepare-reached" ];then + pass "canceling authorization does not start a finalizer or prepare the disk" + else + fail "canceled authorization reached disk preparation or prompted again: $result" + fi ;; + unavailable) + if [ "$result_status" == 1 ] && [ "$prompts" == 1 ] \ + && [[ "$result" == *'worker exit 1'* ]] && [[ "$result" == *'mock sudo: credentials unavailable'* ]] \ + && ! grep -qFx worker "$case_dir/calls" && [ ! -e "$case_dir/prepare-reached" ];then + pass "unavailable credentials fail before preparation without a second prompt" + else + fail "unavailable credentials were hidden, prompted again, or reached preparation: $result" + fi ;; + early-exit) + if [ "$result_status" == 1 ] && [ "$prompts" == 1 ] \ + && [[ "$result" == *'worker exit 0'* ]] && [ ! -e "$case_dir/prepare-reached" ];then + pass "a successful worker exit without readiness still prevents preparation" + else + fail "an unready worker was accepted or its exit status was lost: $result" + fi ;; + pi3-expired) + if [ "$result_status" == 0 ] && [ "$prompts" == 1 ] && grep -qFx patched <<<"$result" \ + && [ -f "$case_dir/verify-reached" ] && [ "$(wc -l < "$case_dir/disk-commands" | tr -d ' ')" == 1 ];then + pass "the Pi3 patch runs once at its signal after cached authorization expires" + else + fail "the Pi3 patch repeated, ran early, or needed fresh authorization: $result" + fi ;; + pi3-failure) + if [ "$result_status" == 1 ] && [ "$prompts" == 1 ] \ + && [[ "$result" == *'status 42'* ]] && [[ "$result" == *'mock dd failed'* ]] \ + && [ ! -e "$case_dir/verify-reached" ];then + pass "a failed Pi3 patch preserves its error and prevents verification" + else + fail "a failed Pi3 patch was hidden or prompted for authorization again: $result" + fi ;; + esac + rm -f "$case_dir/authorized-parent" "$case_dir/calls" "$case_dir/prepare-reached" "$case_dir/verify-reached" \ + "$case_dir/gptpatch.img" "$case_dir/disk-commands" "$case_dir/worker/go" "$case_dir/worker/done" \ + "$case_dir/worker/log" "$case_dir/worker/ready" "$case_dir/worker/patch-go" "$case_dir/worker/patch-done" + rmdir "$case_dir/worker" "$case_dir" || die "Could not clean the authorization-test case." + done + cleanup_result="$(AUTH_CLEANUP_LOG="$auth_dir/cleanup-log" run_in_engine ' + HOST_OS=Darwin + CLEANUP_DEVICES=(/dev/mock-iso) + hdiutil() { printf "user detach: %s %s\n" "$1" "$2" >> "$AUTH_CLEANUP_LOG"; } + sudo() { printf "unexpected elevation\n" >> "$AUTH_CLEANUP_LOG"; return 1; } + cleanup_mounts + cat "$AUTH_CLEANUP_LOG"' 2>&1)" + [ "$cleanup_result" == 'user detach: detach /dev/mock-iso' ] \ + && pass "macOS detaches user-mounted ISO images without elevation" \ + || fail "macOS ISO cleanup requested fresh authorization: $cleanup_result" + grep -qF '[ "$RPI_MODEL" != 3 ] || darwin_apply_pi3_gpt_patch_or_die' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF '|| sudo dd if="$PWD/peinstaller/pi3/gptpatch.img"' "$REPO_DIR/install-wor.sh" \ + && pass "the macOS Pi3 flash path uses the pre-authorized patch worker" \ + || fail "the macOS Pi3 flash still depends on a late sudo timestamp" + rm -f "$auth_dir/sudo" "$auth_dir/dd" "$auth_dir/cleanup-log" + rmdir "$auth_dir" || die "Could not clean the authorization-test workspace." +} + +shared_function_checks() { + info "== Shared engine functions ==" + + #the CLI banner and both GUI overviews must describe a run from one place, or they drift apart + summary_labels="$(run_in_engine 'settings_summary | cut -f1 | tr "\n" ","')" + [ "$summary_labels" == 'WoR-Flasher version,Target drive,Target hardware,Operating system,Installation mode,Offline OOBE,Windows local account,Windows keyboard and regional settings,Pi 4 RAM unlock,UEFI firmware,Windows ARM64 drivers,Custom config.txt,Hide empty drives,Verify written image,Downloaded files,Dry run,Download directory,Log file,' ] \ + && [ "$(run_in_engine 'settings_summary | awk -F"\t" "NF != 2" | wc -l | tr -d " "')" == 0 ] \ + && pass "settings_summary emits one tab-separated label/value pair per setting" \ + || fail "settings_summary is missing settings or emits malformed lines: $summary_labels" + + #every toggle the Advanced Options windows offer has to be visible on the confirmation screen + [ "$(run_in_engine 'WOR_RUN_ID=summary-test DRY_RUN=1 SKIP_IMAGE_VERIFICATION=1 USE_CACHE=2 APPLY_CUSTOM_CONFIG_TXT=0 UEFI_USE_LATEST=1 DRIVERS_USE_LATEST=0 OOBE_NETWORK_BYPASS=0 PI4_AUTO_DISABLE_3GB=0 HIDE_EMPTY_DRIVES=0 WINDOWS_LOCALE_SETUP=0 settings_summary | tail -n +2 | cut -f2 | tr "\n" "|"')" \ + == "/dev/does-not-exist|Raspberry Pi 4|Windows 11 (en-US) arm64 build 22631.2861|Install Windows onto this drive|Disabled|Windows setup will ask|Windows setup defaults|Disabled|Latest|Pinned (v0.17)|Using the firmware default|No|No (skipped)|Trust the cache without checking|Yes (no changes will be written)|/tmp/wor-test-dl|/tmp/wor-test-dl/logs/wor-flasher-summary-test.log|" ] \ + && pass "every Advanced Options toggle changes what the confirmation screens show" \ + || fail "a setting is not reflected in settings_summary" + + #the summary must never abort a run, however little can be read back about the chosen drive + [ "$(run_in_engine 'DEVICE=/dev/definitely-not-here settings_summary >/dev/null 2>&1; echo $?')" == 0 ] \ + && pass "an unreadable target drive does not break the summary" \ + || fail "settings_summary fails when the drive details cannot be read" + + #yad renders its text as pango markup, so an ISO filename containing markup characters would corrupt the window + markup_expected="- Windows source: <b>/tmp/a&lt;b&gt;&amp;c.iso</b>" + [ "$(run_in_engine 'SOURCE_FILE="/tmp/a<b>&c.iso"; settings_summary_markup | sed -n "/Windows source/p"')" == "$markup_expected" ] \ + && [ "$(run_in_engine 'settings_summary_markup | grep -c "^- Target drive: <b>/dev/does-not-exist</b>$"')" == 1 ] \ + && grep -qF 'window_text="$(settings_summary_markup)' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the Linux overview escapes pango markup in every value it shows" \ + || fail "a value containing markup characters would corrupt the Linux overview window" + + #the macOS confirmation screen and the CLI banner list the same settings from shared renderers + [ "$(run_in_engine 'settings_summary_plain | sed -n 2p')" == 'Target drive: /dev/does-not-exist' ] \ + && [ "$(run_in_engine 'settings_summary | sed -n 2p')" == $'Target drive\t/dev/does-not-exist' ] \ + && [ "$(run_in_engine 'settings_summary_plain " %-24s %s\n" | sed -n 2p')" == ' Target drive: /dev/does-not-exist' ] \ + && grep -qF 'rows="$(settings_summary)"' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'const rows = rawRows.split' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "settings_summary_plain ' %-24s %s" "$REPO_DIR/install-wor.sh" \ + && pass "the CLI banner and the macOS confirmation screen render one shared summary" \ + || fail "the CLI banner and the macOS confirmation screen do not share a renderer" + + #config.json loading and precedence + cfg_test_dir="$(mktemp -d)" + cat > "$cfg_test_dir/config.json" <<'JSON' +{ + "target": { + "rpiModel": 5, + "device": "/dev/sdz", + "canInstallOnSameDrive": true + }, + "media": { + "winLang": "de-de", + "bid": "22631.2861" + }, + "execution": { + "dryRun": true + } +} +JSON + cfg_test_out="$(run_in_engine "unset RPI_MODEL WIN_LANG BID DEVICE DRY_RUN; WOR_CONFIG_FILE='$cfg_test_dir/config.json' load_config_json; printf '%s|%s|%s|%s|%s\n' \"\$RPI_MODEL\" \"\$WIN_LANG\" \"\$BID\" \"\$DEVICE\" \"\$DRY_RUN\"")" + cfg_override_out="$(run_in_engine "unset BID DEVICE DRY_RUN; RPI_MODEL=4 WIN_LANG=en-us WOR_CONFIG_FILE='$cfg_test_dir/config.json' load_config_json; printf '%s|%s|%s|%s|%s\n' \"\$RPI_MODEL\" \"\$WIN_LANG\" \"\$BID\" \"\$DEVICE\" \"\$DRY_RUN\"")" + cfg_hook_out="$(cd "$REPO_DIR" && ./install-wor-hook.sh --config "$cfg_test_dir/config.json" summary | grep -E '^Target hardware|Operating system' | tr '\n' '|')" + rm -rf "$cfg_test_dir" + [ "$cfg_test_out" == "5|de-de|22631.2861|/dev/sdz|1" ] \ + && [ "$cfg_override_out" == "4|en-us|22631.2861|/dev/sdz|1" ] \ + && [ "$cfg_hook_out" == "Target hardware Raspberry Pi 5|Operating system Windows 11 (de-DE) arm64 build 22631.2861|" ] \ + && pass "config.json populates unset variables while preserving environment overrides" \ + || fail "config.json loading failed: got '$cfg_test_out' / '$cfg_override_out' / '$cfg_hook_out'" + + #a Pi 3 or Pi 5 has no 3 GB RAM limit, so offering the line at all would be misleading + ! run_in_engine 'RPI_MODEL=5 settings_summary' | grep -q 'Pi 4 RAM unlock' \ + && run_in_engine 'RPI_MODEL=4 settings_summary' | grep -q 'Pi 4 RAM unlock' \ + && pass "the Pi 4 RAM unlock line appears only for a Pi 4" \ + || fail "the Pi 4 RAM unlock line is shown for the wrong models" + + [ "$(run_in_engine 'for RPI_MODEL in 3 4 5 ;do uefi_pinned_version ;done | tr "\n" " "')" == "$(jq -r '.systemDefaults | [.uefiVerPi3, .uefiVerPi4, .uefiVerPi5] | join(" ") + " "' "$REPO_DIR/src/config/metadata.json")" ] \ + && pass "uefi_pinned_version returns the pinned firmware for every supported model" \ + || fail "uefi_pinned_version does not match the pinned UEFI versions" + + #the CLI used to write the firmware's own config.txt while the GUI wrote the shipped template + config_from_engine="$(run_in_engine 'set_default_config_txt; printf "%s" "$CONFIG_TXT"')" + [ -n "$config_from_engine" ] \ + && [ "$config_from_engine" == "$(printf '\n\n%s' "$(cat "$REPO_DIR/config-templates/pi4.config.txt")")" ] \ + && [ "$(run_in_engine 'CONFIG_TXT=mine; set_default_config_txt; printf "%s" "$CONFIG_TXT"')" == mine ] \ + && grep -qF 'set_default_config_txt' "$REPO_DIR/install-wor.sh" \ + && pass "a CLI run and a GUI run start from the same shipped config.txt" \ + || fail "the CLI and the GUI do not agree on the default config.txt" + + if [ "$(uname -s)" == Linux ];then + package_arguments="$(run_in_engine 'package_installed() { return 1; }; sudo() { printf "%s\n" "$@"; }; status() { :; }; install_packages alpha beta gamma')" + [ "$package_arguments" == $'apt\nupdate\napt\ninstall\n-yf\nalpha\nbeta\ngamma\n--no-install-recommends' ] \ + && pass "Linux dependency installation preserves individual package arguments" \ + || fail "Linux dependency installation combines package names before invoking apt" + else + skip "Linux dependency argument handling requires Linux" + fi + + #the two front-ends used to keep their own export lists, so one could silently drop a setting + exported_settings="$(run_in_engine 'SOURCE_FILE=/tmp/x.iso; set_default_config_txt; export_installer_settings + comm -23 <(printf "%s\n" "${WOR_INSTALLER_SETTINGS[@]}" | sort -u) <(compgen -e | sort -u)')" + [ -z "$exported_settings" ] \ + && [ "$(run_in_engine 'printf "%s\n" "${WOR_INSTALLER_SETTINGS[@]}" | sort | uniq -d | wc -l | tr -d " "')" == 0 ] \ + && pass "export_installer_settings hands the installer every collected setting exactly once" \ + || fail "these settings never reach the installer: $exported_settings" + + #shared behaviour belongs to install-wor.sh; a second copy in the GUI silently shadows it on sourcing + duplicate_functions="$(comm -12 \ + <(grep -aoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\) \{' "$REPO_DIR/install-wor.sh" | sort -u) \ + <(grep -aoE '^[a-zA-Z_][a-zA-Z0-9_]*\(\) \{' "$REPO_DIR/install-wor-gui.sh" | sort -u))" + [ -z "$duplicate_functions" ] \ + && pass "no function is defined in both install-wor.sh and install-wor-gui.sh" \ + || fail "these functions are defined twice and will drift apart: $duplicate_functions" + + #bootstrap repair is the sole pre-source helper because it restores missing shared libraries; + #every other GUI helper still has to come after the engine is loaded + gui_source_line="$(grep -an 'source "$cli_script" source' "$REPO_DIR/install-wor-gui.sh" | head -n1 | cut -d: -f1)" + gui_first_function_line="$(grep -anE '^[a-zA-Z_][a-zA-Z0-9_]*\(\) \{' "$REPO_DIR/install-wor-gui.sh" | head -n1 | cut -d: -f1)" + gui_second_function_line="$(grep -anE '^[a-zA-Z_][a-zA-Z0-9_]*\(\) \{' "$REPO_DIR/install-wor-gui.sh" | sed -n '2p' | cut -d: -f1)" + [ -n "$gui_source_line" ] && [ -n "$gui_first_function_line" ] && [ -n "$gui_second_function_line" ] \ + && [ "$gui_first_function_line" -lt "$gui_source_line" ] \ + && [ "$gui_source_line" -lt "$gui_second_function_line" ] \ + && grep -qF 'repair_missing_checkout_runtime() {' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -ac 'source "$cli_script" source' "$REPO_DIR/install-wor-gui.sh")" == 1 ] \ + && pass "bootstrap repair runs before the GUI sources its engine; all other helpers run after" \ + || fail "a non-bootstrap GUI helper is defined before shared engine functions are available" + + #every shared name the GUI relies on has to survive as a function, on both platforms + missing_shared="$(run_in_engine 'for fn in error warning status echo_red gui_error_dialog settings_summary \ + cache_mode_label install_mode_label uefi_pinned_version set_default_config_txt describe_device human_size \ + export_installer_settings read_config_template drive_capability validate_install_mode is_safe_target_device \ + list_bids list_bids_supported get_bid get_os_name list_langs default_win_lang list_windows_locale_options get_device_name get_size_raw \ + get_file_size setup ;do declare -F "$fn" >/dev/null || echo "$fn" ;done')" + [ -z "$missing_shared" ] \ + && pass "install-wor.sh exports every shared function the GUI depends on" \ + || fail "the GUI calls these functions, but install-wor.sh does not define them: $missing_shared" + + #the CHECK_FOR_UPDATES/NO_UPDATE pair still gates the check, and `warning` stays defined + [ "$(run_in_engine 'warning "update failed" 2>&1 | sed "s/\x1b\[[0-9;]*m//g"')" == 'update failed' ] \ + && [ "$(run_in_engine 'unset NO_UPDATE; CHECK_FOR_UPDATES=0 load_config_json; printf %s "$NO_UPDATE"')" == 1 ] \ + && [ "$(run_in_engine 'unset NO_UPDATE CHECK_FOR_UPDATES; load_config_json; printf %s "$NO_UPDATE"')" == 0 ] \ + && grep -qF '"checkForUpdates": true' "$REPO_DIR/config-templates/config.json" \ + && grep -qF 'set_check_updates_if_unset' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Checking for WoR-Flasher updates...' "$REPO_DIR/install-wor.sh" \ + && grep -qF -- '--check-release' "$REPO_DIR/install-wor.sh" \ + && pass "the release check is gated by CHECK_FOR_UPDATES and NO_UPDATE" \ + || fail "the release check is no longer gated by CHECK_FOR_UPDATES and NO_UPDATE" + + #ISO acceptance used to be written out three times, so the CLI and the GUI could disagree on what is usable + iso_dir="$(mktemp -d)" + : > "$iso_dir/small.iso" + : > "$iso_dir/notanimage.txt" + #sparse, so these cost nothing on disk. 2 GB sits just under the 3 GB floor, making it a boundary test + dd if=/dev/zero of="$iso_dir/22631.2861_ARM64_en-us.iso" bs=1 count=0 seek=$((4*1024*1024*1024)) >/dev/null 2>&1 + dd if=/dev/zero of="$iso_dir/truncated.iso" bs=1 count=0 seek=$((2*1024*1024*1024)) >/dev/null 2>&1 + valid_iso_result="$(run_in_engine 'validate_iso_file "'"$iso_dir"'/22631.2861_ARM64_en-us.iso"; echo "rc=$?"')" + missing_iso_result="$(run_in_engine 'validate_iso_file "'"$iso_dir"'/missing.iso" >/dev/null; echo "rc=$?"')" + extension_iso_result="$(run_in_engine 'validate_iso_file "'"$iso_dir"'/notanimage.txt" >/dev/null; echo "rc=$?"')" + truncated_iso_result="$(run_in_engine 'validate_iso_file "'"$iso_dir"'/truncated.iso" >/dev/null; echo "rc=$?"')" + truncated_iso_message="$(run_in_engine 'validate_iso_file "'"$iso_dir"'/truncated.iso" 2>&1')" + engine_iso_references="$(awk '{ count += gsub(/validate_iso_file/, "&") } END { print count + 0 }' "$REPO_DIR/install-wor.sh")" + gui_iso_references="$(awk '{ count += gsub(/validate_iso_file/, "&") } END { print count + 0 }' "$REPO_DIR/install-wor-gui.sh")" + [ "$valid_iso_result" == 'rc=0' ] \ + && [ "$missing_iso_result" == 'rc=1' ] \ + && [ "$extension_iso_result" == 'rc=1' ] \ + && [ "$truncated_iso_result" == 'rc=1' ] \ + && [ "$truncated_iso_message" == 'This file is smaller than 3GB and is probably incomplete.' ] \ + && [ "$engine_iso_references" == 3 ] \ + && [ "$gui_iso_references" == 1 ] \ + && pass "the CLI and the GUI accept and reject exactly the same ISO files" \ + || fail "ISO validation mismatch: valid=$valid_iso_result missing=$missing_iso_result extension=$extension_iso_result truncated=$truncated_iso_result message=$truncated_iso_message references=$engine_iso_references/$gui_iso_references" + + #the build number and language are read back out of the filename, in both front-ends + [ "$(run_in_engine 'bid_from_iso_name "'"$iso_dir"'/22631.2861_ARM64_en-us.iso"')" == '22631.2861' ] \ + && [ "$(run_in_engine 'lang_from_iso_name "'"$iso_dir"'/22631.2861_ARM64_EN-US.iso"')" == 'en-us' ] \ + && [ -z "$(run_in_engine 'bid_from_iso_name "'"$iso_dir"'/small.iso"')" ] \ + && ! grep -qF "tr '_ -' " "$REPO_DIR/install-wor-gui.sh" \ + && pass "ISO build number and language are inferred by one shared function" \ + || fail "ISO build number or language inference is duplicated or wrong" + + #the CLI listed only winfiles_from_iso_*, the GUI listed both, from two different pipelines + winfiles_dir="$(mktemp -d)" + mkdir -p "$winfiles_dir/winfiles_22631.2861_en-us" "$winfiles_dir/winfiles_from_iso_22000.1_de-de" \ + "$winfiles_dir/notwinfiles_9_9" "$winfiles_dir/winfiles_incomplete_xx" + touch "$winfiles_dir/winfiles_22631.2861_en-us/alldone" "$winfiles_dir/winfiles_from_iso_22000.1_de-de/alldone" \ + "$winfiles_dir/notwinfiles_9_9/alldone" + [ "$(run_in_engine 'list_cached_winfiles "'"$winfiles_dir"'" | tr "\n" " "')" == 'winfiles_from_iso_22000.1_de-de winfiles_22631.2861_en-us ' ] \ + && [ "$(run_in_engine 'bid_from_winfiles_dir winfiles_from_iso_22000.1_de-de')" == '22000.1' ] \ + && [ "$(run_in_engine 'lang_from_winfiles_dir winfiles_22631.2861_en-us')" == 'en-us' ] \ + && ! grep -qF "name 'alldone'" "$REPO_DIR/install-wor-gui.sh" \ + && pass "cached Windows files are discovered the same way by the CLI and the GUI" \ + || fail "cached Windows file discovery is duplicated or lists the wrong folders" + + #the GUI enumerated drives with its own lsblk call, so a filter added here would not apply there + ! grep -qF 'lsblk -I 8,179,259' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'for device in $(list_dev_paths) ;do' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'linux_no_device_message()' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'No external writable target drive was found.' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'WoR-Flasher hides loop/snap devices' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'device_buttons=(--button="<b>Refresh</b>' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF "device_buttons=(--button='<b>Cancel</b>':1 --button=\"<b>Refresh</b>" "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'list_dev_paths() {' "$REPO_DIR/install-wor.sh" \ + && [ "$(grep -cF 'lsblk -I 8,179,259' "$REPO_DIR/install-wor.sh")" == 1 ] \ + && pass "both front-ends enumerate candidate drives through one function" \ + || fail "drive enumeration is duplicated between the CLI and the GUI" + + #en-us has to come first so it is the preselected row, and both GUIs offer the same order + [ "$(run_in_engine 'list_langs_preferred | cut -d: -f1 | head -n1')" == 'en-us' ] \ + && [ "$(run_in_engine 'list_langs_preferred | wc -l | tr -d " "')" == "$(run_in_engine 'list_langs | wc -l | tr -d " "')" ] \ + && [ "$(grep -cF 'list_langs_preferred' "$REPO_DIR/install-wor-gui.sh")" == 2 ] \ + && run_in_engine 'is_known_win_lang en-us' \ + && ! run_in_engine 'is_known_win_lang en' \ + && pass "both GUIs offer the same language order and accept the same codes" \ + || fail "the language list differs between front-ends, or an invalid code is accepted" + + malformed_catalogue_out="$(run_in_engine 'RPI_MODEL=5; versions="<releases><version number=\"10\"><release build=\"22631.2861\"><date>2024-01-01</date>$(printf "\377")</release></version></releases>"; list_bids 10' 2>&1)" + [[ "$malformed_catalogue_out" == *22631.2861* ]] \ + && [[ "$malformed_catalogue_out" != *'illegal byte sequence'* ]] \ + && pass "Windows release catalogue parsing tolerates a malformed response byte on macOS" \ + || fail "a malformed release catalogue byte can close the macOS wizard" + + #wiminfo --xml emits UTF-16LE with a BOM and no line breaks. Passing that directly to BSD sed + #closes the macOS wizard with "illegal byte sequence" whenever cached Windows files are present. + wim_locale_dir="$(mktemp -d)" + mkdir -p "$wim_locale_dir/bin" + touch "$wim_locale_dir/install.wim" + printf '<WIM><IMAGE><WINDOWS><LANGUAGES><LANGUAGE>en-US</LANGUAGE><DEFAULT>en-US</DEFAULT></LANGUAGES></WINDOWS></IMAGE></WIM>' \ + | iconv -f UTF-8 -t UTF-16LE > "$wim_locale_dir/wim.xml" + { printf '#!/bin/sh\n'; printf 'cat "$WIM_XML_FIXTURE"\n'; } > "$wim_locale_dir/bin/wiminfo" + chmod +x "$wim_locale_dir/bin/wiminfo" + wim_locale_out="$(run_in_engine 'WIM_XML_FIXTURE="'"$wim_locale_dir"'/wim.xml"; export WIM_XML_FIXTURE; PATH="'"$wim_locale_dir"'/bin:$PATH"; list_wim_locale_codes "'"$wim_locale_dir"'/install.wim"' 2>&1)" + [ "$wim_locale_out" == en-us ] \ + && pass "cached WIM locale discovery decodes wiminfo UTF-16LE output before parsing" \ + || fail "cached WIM locale discovery cannot parse wiminfo XML: '$wim_locale_out'" + rm -rf "$wim_locale_dir" + + cached_locale_count="$(run_in_engine 'locale_test_dir="$(mktemp -d)"; DL_DIR="$locale_test_dir"; mkdir -p "$DL_DIR/winfiles_22631.2861_en-us"; : > "$DL_DIR/winfiles_22631.2861_en-us/install.wim"; list_wim_locale_codes() { printf "en-us\\n"; }; list_windows_locale_options | awk -F "\t" "\$1 == \"en-US\" { count++ } END { print count }"; rm -rf "$locale_test_dir"')" + [ "$(run_in_engine 'windows_locale_from_language_code sr-latn-rs')" == 'sr-Latn-RS' ] \ + && [ "$(run_in_engine 'HOST_OS=Darwin; defaults() { printf "en_GB@currency=GBP\\n"; }; default_windows_locale')" == 'en-GB' ] \ + && [ "$(run_in_engine 'HOST_OS=Darwin; defaults() { printf "xx_YY\\n"; }; default_windows_locale')" == 'en-US' ] \ + && [ "$cached_locale_count" == 1 ] \ + && grep -qF '[ -z "$WINDOWS_LOCALE_SETUP" ] && WINDOWS_LOCALE_SETUP=1' "$REPO_DIR/install-wor.sh" \ + && grep -qF '[ -z "$WINDOWS_LOCALE" ] && WINDOWS_LOCALE="$(default_windows_locale)"' "$REPO_DIR/install-wor.sh" \ + && [ "$(run_in_engine 'list_windows_locale_options | head -n1')" == $'en-US\tEnglish (United States) (en-US)' ] \ + && run_in_engine 'WINDOWS_LOCALE_SETUP=1 WINDOWS_LOCALE=sr-Latn-RS true' \ + && grep -qF 'localeCheckbox = $.NSButton.checkboxWithTitleTargetAction' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF 'localePopup.enabled = enabled' "$REPO_DIR/install-wor-gui.sh" \ + && pass "Windows regional settings default to the host locale, deduplicate cached entries, and accept multi-part locales" \ + || fail "Windows regional settings still depend on a freeform or incomplete locale field" + + rm -rf "$iso_dir" "$winfiles_dir" + + #one entry point, but never a guess: DISPLAY is also set over SSH and in CI, and this tool erases disks + name="$(run_in_engine 'printf %s "$WOR_FLASHER_NAME"')" + [ "$(cd "$REPO_DIR" && ./install-wor.sh --help | head -n1)" == "$name $(run_in_engine 'printf %s "$WOR_FLASHER_VERSION"')" ] \ + && grep -qF 'gui|--gui|-g)' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'exec "$DIRECTORY/install-wor-gui.sh" "$@"' "$REPO_DIR/install-wor.sh" \ + && ! grep -qE 'if .*-n .\$DISPLAY|command -v yad .*&&.*exec' "$REPO_DIR/install-wor.sh" \ + && [ "$(cd "$REPO_DIR" && NO_UPDATE=1 ./install-wor.sh --bogus 2>&1 | sed 's/\x1b\[[0-9;]*m//g'; echo "rc=${PIPESTATUS[0]}")" == "Unknown argument '--bogus'. Run 'install-wor.sh --help' for usage. +rc=1" ] \ + && pass "install-wor.sh gui/--gui hands over explicitly and never auto-detects a display" \ + || fail "the CLI entry point is missing, or it guesses whether to open a GUI" + + #a bug report is unactionable without knowing which version produced it + version="$(run_in_engine 'printf %s "$WOR_FLASHER_VERSION"')" + metadata_version="$(jq -r '.product.version' "$REPO_DIR/src/config/metadata.json")" + [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \ + && [ "$name" == 'WoR-Flasher' ] \ + && [ "$version" == "$metadata_version" ] \ + && grep -qF 'wor_metadata_required product version WOR_FLASHER_VERSION' "$REPO_DIR/src/lib/metadata.sh" \ + && grep -qF 'source "$WOR_METADATA_FILE"' "$REPO_DIR/install-wor.sh" \ + && [ "$(cd "$REPO_DIR" && ./install-wor.sh --version)" == "$name $version" ] \ + && [ "$(cd "$REPO_DIR" && ./install-wor.sh -V)" == "$name $version" ] \ + && [ "$(run_in_engine 'settings_summary | head -n1')" == "$(printf '%s version\t%s' "$name" "$version")" ] \ + && pass "the version is a single semantic value, reported by --version and on every run" \ + || fail "the version is missing, malformed, or not reported: '$version'" + + #the version history and the README must not describe different releases + grep -qE "^#$version - \S" "$REPO_DIR/install-wor.sh" \ + && grep -qE "^- \*\*$version\*\*" "$REPO_DIR/README.md" \ + && grep -qF "version-$version-" "$REPO_DIR/README.md" \ + && grep -A1 -F '<key>CFBundleShortVersionString</key>' "$REPO_DIR/src/macos-app/Contents/Info.plist" | grep -qF "<string>$version</string>" \ + && grep -A1 -F '<key>CFBundleVersion</key>' "$REPO_DIR/src/macos-app/Contents/Info.plist" | grep -qF "<string>$version</string>" \ + && pass "the version history, README and app bundle metadata all agree" \ + || fail "release metadata is out of step with WOR_FLASHER_VERSION" + + #GitHub only surfaces these community files if they are named exactly right and are tracked + missing_community='' + for community_file in LICENSE NOTICE README.md CONTRIBUTING.md CODE_OF_CONDUCT.md SECURITY.md \ + .github/FUNDING.yml .github/PULL_REQUEST_TEMPLATE.md .github/ISSUE_TEMPLATE/config.yml ;do + [ -s "$REPO_DIR/$community_file" ] || missing_community="$missing_community $community_file" + done + [ -z "$missing_community" ] \ + && grep -qF 'GNU GENERAL PUBLIC LICENSE' "$REPO_DIR/LICENSE" \ + && grep -qF 'Version 3, 29 June 2007' "$REPO_DIR/LICENSE" \ + && pass "every community health file is present and the license is GPL-3.0" \ + || fail "these community health files are missing or empty:$missing_community" + + grep -qF '"$HOME/pi-apps/manage" install '\''More RAM'\''' "$REPO_DIR/install-wor-gui.sh" \ + && grep -qF '"$(dirname "$(cat /usr/local/bin/pi-apps | sed -n 2p)")/manage" install '\''More RAM'\''' "$REPO_DIR/install-wor-gui.sh" \ + && pass "the RAM-download option installs More RAM through Pi-Apps at default and custom locations" \ + || fail "the RAM-download option does not install More RAM through every supported Pi-Apps location" + + wsl_hook_out="$(cd "$REPO_DIR" && WSL_DISTRO_NAME=wor-test ./install-wor-hook.sh list-devices 2>&1; echo "rc=$?")" + hook_progress_file="$(mktemp "${TMPDIR:-/tmp}/wor-hook-progress.XXXXXX")" + rm -f "$hook_progress_file" + hook_progress_out="$(cd "$REPO_DIR" && ./install-wor-hook.sh --progress-file "$hook_progress_file" run --version 2>&1)" + hook_bad_progress_out="$(cd "$REPO_DIR" && ./install-wor-hook.sh --progress-file 2>&1; echo "rc=$?")" + hook_set_summary="$(cd "$REPO_DIR" && ./install-wor-hook.sh --set RPI_MODEL=5 --set BID=26200.6899 --set WIN_LANG=de-de --set DEVICE=/dev/does-not-exist --set CAN_INSTALL_ON_SAME_DRIVE=1 summary | grep -E '^Target hardware|Operating system' | tr '\n' '|')" + hook_set_expected=$'Target hardware Raspberry Pi 5|Operating system Windows 11 (de-DE) arm64 build 26200.6899|' + hook_bad_set_out="$(cd "$REPO_DIR" && ./install-wor-hook.sh --set NOT-VALID=1 summary 2>&1; echo "rc=$?")" + hook_bootstrap_dir="$(mktemp -d)" + mkdir -p "$hook_bootstrap_dir/source/config-templates" "$hook_bootstrap_dir/source/src/lib" "$hook_bootstrap_dir/source/src/config" + cp "$REPO_DIR/install-wor.sh" "$hook_bootstrap_dir/source/" + cp "$REPO_DIR/src/lib/metadata.sh" "$REPO_DIR/src/lib/dependencies.sh" "$REPO_DIR/src/lib/paths.sh" "$REPO_DIR/src/lib/cleanup.sh" "$REPO_DIR/src/lib/gui.sh" "$hook_bootstrap_dir/source/src/lib/" + cp "$REPO_DIR/src/config/metadata.json" "$REPO_DIR/src/config/metadata.schema.json" "$hook_bootstrap_dir/source/src/config/" + cp -R "$REPO_DIR/config-templates/." "$hook_bootstrap_dir/source/config-templates/" + git -C "$hook_bootstrap_dir/source" init -q + git -C "$hook_bootstrap_dir/source" add . + git -C "$hook_bootstrap_dir/source" -c user.name=WoR-Test -c user.email=wor-test@example.invalid commit -qm fixture + git -C "$hook_bootstrap_dir/source" branch -M fixture + cp "$REPO_DIR/install-wor-hook.sh" "$hook_bootstrap_dir/hook.sh" + hook_bootstrap_out="$(WOR_HOOK_REPOSITORY="$hook_bootstrap_dir/source" WOR_HOOK_REF=fixture WOR_HOOK_INSTALL_DIR="$hook_bootstrap_dir/installed" "$hook_bootstrap_dir/hook.sh" run --version 2>&1)" + rm "$hook_bootstrap_dir/source/config-templates/prefinalize.cmd" + git -C "$hook_bootstrap_dir/source" add -u + git -C "$hook_bootstrap_dir/source" -c user.name=WoR-Test -c user.email=wor-test@example.invalid commit -qm incomplete-fixture + hook_incomplete_out="$(WOR_HOOK_REPOSITORY="$hook_bootstrap_dir/source" WOR_HOOK_REF=fixture WOR_HOOK_INSTALL_DIR="$hook_bootstrap_dir/incomplete" "$hook_bootstrap_dir/hook.sh" run --version 2>&1; echo "rc=$?")" + [ -x "$REPO_DIR/install-wor-hook.sh" ] \ + && grep -qF 'list-devices)' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF 'describe-device)' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF 'summary)' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF 'exec "$ENGINE"' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF ': "${WOR_HOOK_REF:=main}"' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF 'WOR_GUI_PROGRESS_FILE="${1#*=}"' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF 'export WOR_GUI_PROGRESS_FILE' "$REPO_DIR/install-wor-hook.sh" \ + && grep -qF 'Use --progress-file FILE with run' "$REPO_DIR/install-wor-hook.sh" \ + && grep -A2 -F 'list-devices)' "$REPO_DIR/install-wor-hook.sh" | grep -qF 'require_linux_host' \ + && ! grep -qE '(exec|source|bash)[[:space:]]+.*install-wor-hook\.sh' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(cd "$REPO_DIR" && ./install-wor-hook.sh run --version)" == "WoR-Flasher $version" ] \ + && [ "$hook_progress_out" == "WoR-Flasher $version" ] \ + && [ ! -e "$hook_progress_file" ] \ + && grep -qF 'Usage:' <<< "$hook_bad_progress_out" \ + && [ "$(tail -n1 <<< "$hook_bad_progress_out")" == 'rc=2' ] \ + && [ "$hook_set_summary" == "$hook_set_expected" ] \ + && grep -qF 'Invalid --set name:' <<< "$hook_bad_set_out" \ + && [ "$(tail -n1 <<< "$hook_bad_set_out")" == 'rc=2' ] \ + && grep -qF 'TASK<TAB>percent<TAB>label' "$REPO_DIR/README.md" \ + && [ "$(cd "$REPO_DIR" && WOR_RUN_ID=hook-test DL_DIR=/tmp/wor-test-dl RPI_MODEL=4 BID=22631.2861 WIN_LANG=en-us DEVICE=/dev/does-not-exist CAN_INSTALL_ON_SAME_DRIVE=1 ./install-wor-hook.sh summary)" == "$(run_in_engine 'WOR_RUN_ID=hook-test settings_summary')" ] \ + && [ "$(cd "$REPO_DIR" && ./install-wor-hook.sh describe-device 2>/dev/null; echo $?)" == 2 ] \ + && grep -qF 'WoR-Flasher does not support WSL.' <<< "$wsl_hook_out" \ + && [ "$(tail -n1 <<< "$wsl_hook_out")" == 'rc=1' ] \ + && grep -qF 'Obtaining WoR-Flasher' <<< "$hook_bootstrap_out" \ + && grep -qE 'WoR-Flasher [0-9]+\.[0-9]+\.[0-9]+$' <<< "$hook_bootstrap_out" \ + && [ -f "$hook_bootstrap_dir/installed/install-wor.sh" ] \ + && [ -d "$hook_bootstrap_dir/installed/config-templates" ] \ + && grep -qF 'The obtained WoR-Flasher checkout is incomplete.' <<< "$hook_incomplete_out" \ + && [ "$(tail -n1 <<< "$hook_incomplete_out")" == 'rc=1' ] \ + && [ ! -e "$hook_bootstrap_dir/incomplete" ] \ + && pass "integration adapter exposes the shared installer engine" \ + || fail "integration adapter is missing or does not use the shared engine" + rm -f "$hook_progress_file" + rm -rf "$hook_bootstrap_dir" + + #upstream ships no license, so the fork must say so rather than implying a grant it cannot make + grep -qF 'ships no LICENSE file' "$REPO_DIR/NOTICE" \ + && grep -qF 'https://github.com/Botspot/wor-flasher' "$REPO_DIR/NOTICE" \ + && grep -qF 'does not distribute' "$REPO_DIR/NOTICE" 2>/dev/null || grep -qF 'downloads, but does not redistribute' "$REPO_DIR/NOTICE" \ + && grep -qF '[NOTICE](NOTICE)' "$REPO_DIR/README.md" \ + && pass "the upstream licensing caveat is recorded in NOTICE and linked from the README" \ + || fail "the upstream licensing caveat is missing" + + #attribution is the one thing a fork must never get wrong + missing_attribution='' + for attribution in 'https://github.com/Botspot' 'https://blackoutsecure.app' \ + 'https://linktr.ee/billmcilhargey' 'https://discord.gg/RXSTvaUvuu' 'https://discord.gg/jQCpfVK' \ + 'https://github.com/sponsors/Botspot' 'https://uupdump.net' ;do + grep -qF "$attribution" "$REPO_DIR/README.md" || missing_attribution="$missing_attribution $attribution" + done + [ -z "$missing_attribution" ] \ + && grep -qF 'github: [Botspot, blackoutsecure]' "$REPO_DIR/.github/FUNDING.yml" \ + && grep -qF 'https://github.com/sponsors/blackoutsecure' "$REPO_DIR/README.md" \ + && pass "the README credits the original author, this fork's maintainer and every upstream project" \ + || fail "the README is missing this attribution:$missing_attribution" + + #WoR-PE applies install.wim with DISM rather than running Windows Setup's media flow, so nothing + #performs the implicit answer-file search and Autounattend.xml on the media root is never read. + #Windows then stops at "Let's connect you to a network". The documented prefinalize.cmd hook is + #the only point that can put the answer file where the installed OS looks for it. + pe_hook_dir="$(mktemp -d)" + mkdir -p "$pe_hook_dir/peinstaller/winpe/2" + printf 'stub\n' > "$pe_hook_dir/peinstaller/winpe/2/setup.exe" + pe_hook_out="$(cd "$pe_hook_dir" && env -u CONFIG_TXT NO_UPDATE=1 DIRECTORY="$REPO_DIR" bash -c ' + source "$DIRECTORY/install-wor.sh" source >/dev/null 2>&1 + RPI_MODEL=4; OOBE_NETWORK_BYPASS=1; PI4_AUTO_DISABLE_3GB=0 + mark_cache "$PWD/peinstaller" token-v1 + configure_pe_prefinalize + #the answer file has to reach the installed OS, and the hook must not invalidate the cache + grep -qF "<HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>" peinstaller/winpe/2/scripts/unattend.xml && echo answer-staged + grep -qF "Windows\\Panther" peinstaller/winpe/2/scripts/prefinalize.cmd && echo targets-panther + grep -q "exit /b 0" peinstaller/winpe/2/scripts/prefinalize.cmd && echo always-exits-zero + grep -qU $'"'"'\r'"'"' peinstaller/winpe/2/scripts/prefinalize.cmd && echo crlf + cache_is_current "$PWD/peinstaller" token-v1 && echo cache-intact + #turning both customizations off must not leave a stale hook behind in the cache + OOBE_NETWORK_BYPASS=0; WINDOWS_LOCALE_SETUP=0; RPI_MODEL=5 + configure_pe_prefinalize + [ -e peinstaller/winpe/2/scripts ] || echo stale-hook-removed + ')" + rm -rf "$pe_hook_dir" + for pe_hook_expected in answer-staged targets-panther always-exits-zero crlf cache-intact stale-hook-removed ;do + printf '%s\n' "$pe_hook_out" | grep -qx "$pe_hook_expected" || missing_pe_hook="$missing_pe_hook $pe_hook_expected" + done + [ -z "$missing_pe_hook" ] \ + && [ "$(grep -cF 'configure_pe_prefinalize' "$REPO_DIR/install-wor.sh")" == 3 ] \ + && pass "the offline-OOBE answer file is delivered through WoR-PE's prefinalize hook" \ + || fail "the answer file would never be read, so Windows stops at the network screen:$missing_pe_hook" + + #both the media copies and the hook copy have to come from one builder, or they can disagree + [ "$(run_in_engine 'OOBE_NETWORK_BYPASS=1; RPI_MODEL=5; unattend_xml | grep -c "HideWirelessSetupInOOBE"')" == 1 ] \ + && [ "$(run_in_engine 'OOBE_NETWORK_BYPASS=0; PI4_AUTO_DISABLE_3GB=0; WINDOWS_LOCALE_SETUP=0; RPI_MODEL=5; unattend_xml >/dev/null 2>&1; echo $?')" == 1 ] \ + && grep -qF 'unattend_xml | sudo tee "$destination"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'unattend_xml > "$scripts_dir/unattend.xml"' "$REPO_DIR/install-wor.sh" \ + && pass "the media copies and the prefinalize copy of the answer file share one builder" \ + || fail "the answer file is built in more than one place" + + #one variable decides where the log goes, and it is resolved on use: the Linux GUI can still + #change DL_DIR after this script has been sourced + [[ "$(run_in_engine 'wor_log_file')" == /tmp/wor-test-dl/logs/wor-flasher-*.log ]] \ + && [ "$(run_in_engine 'wor_last_log_file')" == '/tmp/wor-test-dl/last-run.log' ] \ + && [ "$(run_in_engine 'WOR_LOG_FILE=/tmp/elsewhere.log; wor_log_file')" == '/tmp/elsewhere.log' ] \ + && [[ "$(run_in_engine 'DL_DIR=/tmp/moved-later; wor_log_file')" == /tmp/moved-later/logs/wor-flasher-*.log ]] \ + && grep -qF 'WOR_RUN_ID' "$REPO_DIR/install-wor.sh" \ + && [ "$(run_in_engine 'WOR_RUN_ID=shared-log-id; settings_summary | sed -n "s/^Log file\t//p"')" == '/tmp/wor-test-dl/logs/wor-flasher-shared-log-id.log' ] \ + && grep -qF 'cp "$saved_log" "$last_log"' "$REPO_DIR/install-wor-gui.sh" \ + && [ "$(grep -cF 'last-run.log' "$REPO_DIR/install-wor-gui.sh")" == 0 ] \ + && pass "one variable decides where the timestamped run log goes, with last-run kept for support" \ + || fail "the log path is hardcoded, does not follow DL_DIR/WOR_LOG_FILE, or loses last-run" + + #logs and caches are generated beside the scripts; none of it may ever be committed + if command -v git >/dev/null && git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 ;then + tracked_junk="$(git -C "$REPO_DIR" ls-files | grep -E '(^|/)(cache/|wget-log|.*\.log$)' || true)" + unignored='' + for junk_path in cache/x wget-log some.log .test-workspace/x ;do + #-q takes a single pathname only, so ask about them one at a time + (cd "$REPO_DIR" && git check-ignore -q "$junk_path") || unignored="$unignored $junk_path" + done + [ -z "$tracked_junk" ] && [ -z "$unignored" ] \ + && pass "generated logs and caches are ignored and none are tracked" \ + || fail "generated files are tracked ($tracked_junk) or not ignored ($unignored)" + else + skip "git is unavailable; cannot check that generated files are ignored" + fi + + #prefinalize.cmd is stored with LF and gains CRLF when written, so a CRLF copy here would give CRCRLF + ! grep -qU $'\r' "$REPO_DIR/config-templates/prefinalize.cmd" \ + && grep -qF '*.cmd text eol=lf' "$REPO_DIR/.gitattributes" \ + && ! grep -qF 'config_txt_tips' "$REPO_DIR/.gitattributes" \ + && pass "the batch template is stored with LF, so the CR is added exactly once" \ + || fail "the batch template has CRLF in the repo, or .gitattributes does not pin it" + + #the specialize action runs on the installed OS, where the media may no longer be lettered, so the + #script it invokes has to be copied into Windows too - the answer file alone is not enough + ram_hook_dir="$(mktemp -d)" + mkdir -p "$ram_hook_dir/peinstaller/winpe/2" + printf 'stub\n' > "$ram_hook_dir/peinstaller/winpe/2/setup.exe" + ram_hook_out="$(cd "$ram_hook_dir" && env -u CONFIG_TXT NO_UPDATE=1 DIRECTORY="$REPO_DIR" bash -c ' + source "$DIRECTORY/install-wor.sh" source >/dev/null 2>&1 + RPI_MODEL=4; PI4_AUTO_DISABLE_3GB=1; OOBE_NETWORK_BYPASS=0 + configure_pe_prefinalize + [ -s peinstaller/winpe/2/scripts/Pi4Disable3GB.ps1 ] && echo script-staged + grep -qF "Setup\\Scripts" peinstaller/winpe/2/scripts/prefinalize.cmd && echo hook-copies-it + #a model without the 3 GB limit must not carry the action at all + RPI_MODEL=5 + configure_pe_prefinalize + [ -e peinstaller/winpe/2/scripts/Pi4Disable3GB.ps1 ] || echo not-staged-for-pi5 + ')" + rm -rf "$ram_hook_dir" + for ram_hook_expected in script-staged hook-copies-it not-staged-for-pi5 ;do + printf '%s\n' "$ram_hook_out" | grep -qx "$ram_hook_expected" || missing_ram_hook="$missing_ram_hook $ram_hook_expected" + done + #Path is capped at 259 characters by the Windows unattend schema, and a non-zero script result + #fails Windows Setup outright, so invoke the staged file and let it swallow and log its own errors. + ram_command="$(sed -n 's/.*<Path>\(.*\)<\/Path>.*/\1/p' "$REPO_DIR/config-templates/pi4-ram-unlock-specialize.xml")" + [ -z "$missing_ram_hook" ] \ + && grep -qF 'Setup\Scripts\Pi4Disable3GB.ps1' "$REPO_DIR/config-templates/pi4-ram-unlock-specialize.xml" \ + && [ "${#ram_command}" -le 259 ] \ + && grep -qF '} catch {' "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" \ + && grep -qF "Set-Content \$log ('Pi 4 RAM unlock failed: ' + \$_.Exception.Message)" "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" \ + && grep -qF 'runtime variable was changed successfully' "$REPO_DIR/README.md" \ + && grep -qF 'exit 0' "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" \ + && ! grep -qF 'exit 1' "$REPO_DIR/config-templates/pi4-ram-unlock.ps1" \ + && pass "the Pi 4 RAM unlock reaches the installed OS and cannot fail Windows setup" \ + || fail "the RAM unlock is not delivered to the installed OS:$missing_ram_hook" + + #the answer file is concatenated from fragments, and Windows silently ignores one that is not + #well-formed - so a bad escape or a missing newline between fragments would fail invisibly + if command -v python3 >/dev/null ;then + answer_passes="$(run_in_engine 'RPI_MODEL=4; PI4_AUTO_DISABLE_3GB=1; OOBE_NETWORK_BYPASS=1; WINDOWS_LOCALE_SETUP=0; unattend_xml' \ + | python3 -c 'import sys,xml.dom.minidom +d = xml.dom.minidom.parseString(sys.stdin.read()) +print(",".join(s.getAttribute("pass") for s in d.getElementsByTagName("settings")))' 2>/dev/null)" + [ "$answer_passes" == 'specialize,oobeSystem' ] \ + && pass "the generated answer file is well-formed XML with both passes" \ + || fail "the generated answer file is malformed or missing a pass: '$answer_passes'" + else + skip "python3 is unavailable; cannot check that the answer file is well-formed" + fi + + #a table of contents that points at a heading which no longer exists is worse than none + broken_anchors='' + while read -r anchor ;do + [ -z "$anchor" ] && continue + #GitHub slugs: lowercase, punctuation dropped, spaces to hyphens + grep -qE "^#{2,4} " "$REPO_DIR/README.md" || break + grep -E "^#{2,4} " "$REPO_DIR/README.md" \ + | sed 's/^#* //; s/!\[[^]]*\]([^)]*)//g; s/`//g' \ + | tr 'A-Z' 'a-z' | sed 's/[^a-z0-9 -]//g; s/^ *//; s/ *$//; s/ /-/g' \ + | grep -qx "$anchor" || broken_anchors="$broken_anchors #$anchor" + done < <(grep -oE '^\s*- \[[^]]+\]\(#[a-z0-9-]+\)' "$REPO_DIR/README.md" | grep -oE '#[a-z0-9-]+' | tr -d '#') + [ -z "$broken_anchors" ] \ + && pass "every table-of-contents entry points at a heading that exists" \ + || fail "the table of contents links to headings that do not exist:$broken_anchors" } make_disk() { #Input: size, name. Output: loop device path @@ -118,7 +2281,7 @@ make_disk() { #Input: size, name. Output: loop device path [ -z "$dev" ] && die "No free loop device is available." #containers ship a fixed set of /dev/loop* nodes, so the next free name may not exist yet [ ! -e "$dev" ] && sudo mknod "$dev" b 7 "${dev##*loop}" 2>/dev/null - sudo losetup "$dev" "$img" || die "Failed to attach $img to $dev" + sudo losetup --partscan "$dev" "$img" || die "Failed to attach $img to $dev" LOOP_DEVICES+=("$dev") echo "$dev" } @@ -126,6 +2289,7 @@ make_disk() { #Input: size, name. Output: loop device path require_tools() { sudo -n true 2>/dev/null || die "This harness needs passwordless sudo to create loopback devices." command -v losetup >/dev/null || die "losetup is not installed." + command -v pv >/dev/null || die "pv is required before the progress-helper tests; install the test prerequisites first." [ -e /dev/loop-control ] || die "No /dev/loop-control, so loopback devices cannot be created here." } @@ -142,6 +2306,25 @@ stub_kernel_modules() { #containers have no /lib/modules, which install-wor.sh treats as a pending reboot local moddir="/lib/modules/$(uname -r)" [ ! -d "$moddir" ] && sudo mkdir -p "$moddir" + + #minimal containers have no device manager to create loop partition nodes + local partprobe_wrapper + partprobe_wrapper="$(mktemp)" + cat > "$partprobe_wrapper" <<'PARTPROBE' +#!/bin/bash +/usr/sbin/partprobe "$@" || exit $? +for device in "$@" ;do + device_name="${device##*/}" + for partition in /sys/class/block/"$device_name"/"$device_name"p* ;do + [ -e "$partition/dev" ] || continue + node="/dev/${partition##*/}" + IFS=: read -r major minor < "$partition/dev" + [ -b "$node" ] || mknod "$node" b "$major" "$minor" + done +done +PARTPROBE + sudo install -m 0755 "$partprobe_wrapper" /usr/local/bin/partprobe + rm -f "$partprobe_wrapper" return 0 } @@ -151,6 +2334,23 @@ seed_winfiles() { #Input: build id. Makes install-wor.sh skip the multi-gigabyte touch "$TEST_DL_DIR/winfiles_${1}_${TEST_WIN_LANG}/alldone" } +seed_bootable_winfiles() { #Input: build id. Creates a small, valid WinPE fixture for a real loop-device flash. + local bid="$1" winfiles fixture_one fixture_two + winfiles="$TEST_DL_DIR/winfiles_${bid}_${TEST_WIN_LANG}" + fixture_one="$TEST_DIR/boot-wim-index-one" + fixture_two="$TEST_DIR/boot-wim-index-two" + rm -rf "$winfiles" "$fixture_one" "$fixture_two" + mkdir -p "$winfiles/bootpart/sources" "$winfiles/bootpart/efi/boot" "$winfiles/bootpart/boot" "$fixture_one" "$fixture_two" "$TEST_DL_DIR/peinstaller/winpe/2" + printf 'first boot image\n' > "$fixture_one/fixture.txt" + printf 'second boot image\n' > "$fixture_two/fixture.txt" + printf 'PE installer fixture\n' > "$TEST_DL_DIR/peinstaller/winpe/2/fixture.txt" + printf 'fallback bootloader fixture\n' > "$winfiles/bootpart/efi/boot/bootaa64.efi" + wimcapture "$fixture_one" "$winfiles/bootpart/sources/boot.wim" boot-one >/dev/null + wimappend "$fixture_two" "$winfiles/bootpart/sources/boot.wim" boot-two >/dev/null + wimcapture "$fixture_one" "$winfiles/install.wim" install >/dev/null + touch "$winfiles/alldone" +} + run_with_timeout() { if command -v timeout >/dev/null ;then timeout "$TEST_COMMAND_TIMEOUT" "$@" @@ -159,13 +2359,34 @@ run_with_timeout() { fi } -run_flasher() { #Input: VAR=VALUE pairs. Sets LAST_OUT and LAST_CODE. +run_flasher_with_dry_run() { #Input: dry-run flag, then VAR=VALUE pairs. Sets LAST_OUT and LAST_CODE. + local dry_run="$1" + shift local output_file progress "running install-wor.sh with $(printf '%s ' "$@")" progress "live installer output follows (timeout: ${TEST_COMMAND_TIMEOUT}s)" output_file="$TEST_DIR/flasher-output.log" #stdin is closed so an unexpected prompt (e.g. the root-user confirmation) fails fast instead of hanging - (cd "$TEST_DIR" && run_with_timeout env ROOT_DEV=/dev/__wor_flasher_test_root__ DL_DIR="$TEST_DL_DIR" WIN_LANG="$TEST_WIN_LANG" RUN_MODE=cli DRY_RUN=1 SKIP_PACKAGE_INSTALL="${SKIP_PACKAGE_INSTALL:-0}" "$@" "$REPO_DIR/install-wor.sh" </dev/null) 2>&1 | tee "$output_file" 1>&2 + (cd "$TEST_DIR" && run_with_timeout env ROOT_DEV=/dev/__wor_flasher_test_root__ DL_DIR="$TEST_DL_DIR" WIN_LANG="$TEST_WIN_LANG" RUN_MODE=cli DRY_RUN="$dry_run" SKIP_PACKAGE_INSTALL="${SKIP_PACKAGE_INSTALL:-0}" "$@" "$REPO_DIR/install-wor.sh" </dev/null) 2>&1 | tee "$output_file" 1>&2 + LAST_CODE="${PIPESTATUS[0]}" + LAST_OUT="$(<"$output_file")" + progress "install-wor.sh finished with exit $LAST_CODE" +} + +run_flasher() { #Input: VAR=VALUE pairs. Sets LAST_OUT and LAST_CODE. + run_flasher_with_dry_run 1 "$@" +} + +run_flasher_real() { #Input: VAR=VALUE pairs. Sets LAST_OUT and LAST_CODE. + run_flasher_with_dry_run 0 "$@" +} + +run_flasher_interrupted() { #Input: seconds to wait before SIGINT, then VAR=VALUE pairs. Sets LAST_OUT and LAST_CODE. + local delay="$1" output_file + shift + progress "running install-wor.sh with $(printf '%s ' "$@"), sending SIGINT after ${delay}s" + output_file="$TEST_DIR/flasher-output.log" + (cd "$TEST_DIR" && timeout --signal=INT --kill-after=10 "$delay" env ROOT_DEV=/dev/__wor_flasher_test_root__ DL_DIR="$TEST_DL_DIR" WIN_LANG="$TEST_WIN_LANG" RUN_MODE=cli DRY_RUN=0 SKIP_PACKAGE_INSTALL="${SKIP_PACKAGE_INSTALL:-0}" "$@" "$REPO_DIR/install-wor.sh" </dev/null) 2>&1 | tee "$output_file" 1>&2 LAST_CODE="${PIPESTATUS[0]}" LAST_OUT="$(<"$output_file")" progress "install-wor.sh finished with exit $LAST_CODE" @@ -228,16 +2449,22 @@ expect_no_output() { for arg in "$@" ;do case "$arg" in + --macos-auth) MODE=macos-auth ;; --walkthrough) MODE=walkthrough ;; --gui) MODE=gui ;; --clean) MODE=clean ;; --full) SKIP_ESD=0 ;; --keep) KEEP=1 ;; - -h|--help) sed -n '3,12p' "$0" | sed 's/^#//' ; exit 0 ;; + -h|--help) sed -n '3,13p' "$0" | sed 's/^#//' ; exit 0 ;; *) die "Unknown option: $arg" ;; esac done +if [ "$MODE" == macos-auth ];then + macos_auth_checks + summary +fi + if [ "$MODE" == clean ];then detach_all rm -rf "$TEST_DIR" "$REPO_DIR/cache" @@ -253,13 +2480,26 @@ static_checks info "== WoR-Flasher test suite ==" +if [ "$(uname -s)" == Darwin ] && [ "$MODE" == gui ];then + command -v osascript >/dev/null || die "The macOS GUI walkthrough needs osascript." + info "DRY_RUN is set, so the selected removable drive will not be modified." + DL_DIR="$TEST_DL_DIR" DRY_RUN=1 USE_CACHE=0 "$REPO_DIR/install-wor-gui.sh" + exit $? +fi + if [ "$(uname -s)" != Linux ];then if [ "$MODE" == suite ] && [ -z "${WOR_FLASHER_CONTAINER_TEST:-}" ] && [ -x "$REPO_DIR/tests/run-linux-integration.sh" ];then if command -v docker >/dev/null && docker info >/dev/null 2>&1 ;then info "== Linux integration via Docker ==" progress "starting Docker integration; dependency setup can take several minutes" - "$REPO_DIR/tests/run-linux-integration.sh" "$@" - exit $? + #fold the container result into this run's tally: exiting on it alone would discard + #every host-side failure recorded above, reporting a red run as green + if "$REPO_DIR/tests/run-linux-integration.sh" "$@" ;then + pass "Linux integration suite completed" + else + fail "Linux integration suite failed" + fi + summary fi skip "integration tests require Linux loop devices; Docker is unavailable" else @@ -306,6 +2546,41 @@ DIRECTORY="$REPO_DIR" #shellcheck disable=SC1090 source "$REPO_DIR/install-wor.sh" source >/dev/null 2>&1 +#configure_pe_settings_ini edits cached payload, so it has to re-record the manifest or every run re-downloads the PE installer +pe_cache_dir="$(mktemp -d)" +mkdir -p "$pe_cache_dir/peinstaller/winpe/2" +printf '[WoR Configuration File]\n' > "$pe_cache_dir/peinstaller/winpe/2/settings.ini" +printf 'payload\n' > "$pe_cache_dir/peinstaller/winpe/2/other.bin" +( + cd "$pe_cache_dir" || exit 1 + #both are read by the sourced install-wor.sh helpers below + #shellcheck disable=SC2034 + USE_CACHE=1 + #shellcheck disable=SC2034 + HIDE_EMPTY_DRIVES=1 + mark_cache "$pe_cache_dir/peinstaller" PE_TOKEN >/dev/null 2>&1 || exit 1 + configure_pe_settings_ini || exit 1 + grep -q '^HideEmptyDrives=1$' "$pe_cache_dir/peinstaller/winpe/2/settings.ini" || exit 1 + cache_is_current "$pe_cache_dir/peinstaller" PE_TOKEN || exit 1 + #a genuinely altered payload must still be rejected + printf 'tampered\n' > "$pe_cache_dir/peinstaller/winpe/2/other.bin" + cache_is_current "$pe_cache_dir/peinstaller" PE_TOKEN && exit 1 + exit 0 +) && pass "editing the WoR-PE settings keeps the cache valid while tampering is still detected" \ + || fail "configure_pe_settings_ini invalidates the PE installer cache, forcing a re-download every run" +rm -rf "$pe_cache_dir" + +progress_test_dir="$(mktemp -d)" +printf 'progress helper fixture\n' > "$progress_test_dir/source" +original_sudo_function="$(declare -f sudo)" +sudo() { "$@"; } +copy_file_with_progress test-copy "$progress_test_dir/source" "$progress_test_dir/destination" 2>/dev/null \ + && [ "$(sha256_file "$progress_test_dir/source")" == "$(sha256_file_with_progress test-hash "$progress_test_dir/destination" 2>/dev/null)" ] \ + && pass "copy and checksum progress helpers preserve file contents" \ + || fail "copy or checksum progress helper corrupted its output" +eval "$original_sudo_function" +rm -rf "$progress_test_dir" + for pair in '19045.1234:Windows 10' '22631.2861:Windows 11' ;do bid="${pair%%:*}"; want="${pair#*:}" [[ "$(get_os_name "$bid")" == "$want"* ]] && pass "get_os_name $bid -> $want" || fail "get_os_name $bid" @@ -315,6 +2590,119 @@ done [ "$(drive_capability "$DEV_RECOVERY")" == recovery ] && pass "drive_capability $SIZE_RECOVERY -> recovery" || fail "drive_capability $SIZE_RECOVERY" [ "$(drive_capability "$DEV_SMALL")" == too-small ] && pass "drive_capability $SIZE_TOO_SMALL -> too-small" || fail "drive_capability $SIZE_TOO_SMALL" +if command -v jq >/dev/null ;then + original_darwin_plist_json="$(declare -f darwin_plist_json)" + test_host_os="$HOST_OS" + test_root_dev="$ROOT_DEV" + HOST_OS=Darwin + ROOT_DEV=/dev/disk0 + DARWIN_DEVICE_INFO='{"WholeDisk":true,"Internal":false,"VirtualOrPhysical":"Physical","ReadOnlyMedia":false,"DiskSize":64000000000,"MediaName":"USB Drive"}' + darwin_plist_json() { + if [ "$2" == list ] && [ "$4" == /dev/disk2 ];then + printf '%s\n' '{"AllDisksAndPartitions":[{"Partitions":[{"VolumeName":"WOR_BOOT","DeviceIdentifier":"disk2s1"},{"VolumeName":"WOR_INSTALL","DeviceIdentifier":"disk2s2"}]}]}' + elif [ "$2" == list ];then + printf '%s\n' '{"AllDisks":["disk2"]}' + else + printf '%s\n' "$DARWIN_DEVICE_INFO" + fi + } + is_safe_target_device /dev/disk2 && pass "Darwin accepts an external physical writable disk" || fail "Darwin rejected an external physical writable disk" + is_safe_target_device /dev/disk0 && fail "Darwin accepted the startup disk" || pass "Darwin rejects the startup disk" + [ "$(darwin_list_device_paths)" == /dev/disk2 ] && pass "Darwin GUI lists safe external disks" || fail "Darwin GUI listed an unexpected disk" + [ "$(darwin_list_device_choices)" == $'/dev/disk2\t59.6 GB USB Drive Labels: WOR_BOOT, WOR_INSTALL Volumes: WOR_BOOT, WOR_INSTALL' ] \ + && pass "Darwin GUI lists detected volumes" || fail "Darwin GUI did not show detected volume details" + [ "$(darwin_partition_by_volume_name /dev/disk2 WOR_BOOT)" == /dev/disk2s1 ] \ + && [ "$(darwin_partition_by_volume_name /dev/disk2 WOR_INSTALL)" == /dev/disk2s2 ] \ + && pass "Darwin resolves formatted partitions by volume name" \ + || fail "Darwin used fixed partition numbers instead of volume names" + grep -qF 'sgdisk_bin="$(command -v sgdisk)"' "$REPO_DIR/install-wor.sh" \ + && grep -qF -- '-n "1:0:+${boot_size_mb}M" -t 1:ef00 -c 1:WOR_BOOT' "$REPO_DIR/install-wor.sh" \ + && grep -qF '[ "$CAN_INSTALL_ON_SAME_DRIVE" == 1 ] && install_size_mb=18000 || install_size_mb=6000' "$REPO_DIR/install-wor.sh" \ + && grep -qF -- '-n "2:0:+${install_size_mb}M" -t 2:0700 -c 2:WOR_INSTALL' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF -- '-n 2:0:0' "$REPO_DIR/install-wor.sh" \ + && grep -qF -- '-A 1:set:63 -A 2:set:63' "$REPO_DIR/install-wor.sh" \ + && grep -qF -- '-A 1:clear:63 -A 2:clear:63' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'boot_size_mb=$((boot_payload_kb / 1024 + 512))' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'diskutil partitionDisk' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_prepare_disk_or_die "$DEVICE" "$sgdisk_bin" "$boot_size_mb" "$install_size_mb" "$PART1" "$PART2"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_prepare_disk_or_die "$DEVICE" "$sgdisk_bin" "$boot_size_mb" "$install_size_mb" "$PART1" "$PART2"'$'\n'' gui_start_sudo_keepalive' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_mount_partition_or_die "$PART1"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_mount_partition_or_die "$PART2"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_verify_final_partition_types_or_die "$PART1" "$PART2"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sudo -n bash -c "$finalizer_script" wor-partition-finalizer "$device" "$sgdisk_bin" "$raw_device" "$DARWIN_FINALIZE_GO"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if ! kill -0 "$parent_pid" 2>/dev/null;then' "$REPO_DIR/install-wor.sh" \ + && grep -qF '[ "$boot_content" == EFI ] || [ "$boot_content" == "Microsoft Basic Data" ]' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'Final media verification failed: partition 1 is' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'for attempt in $(seq 1 30) ;do' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sudo bash -s -- "$device" "$sgdisk_bin" "$boot_size_mb" "$install_size_mb" "$part1" "$part2"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'if sudo bash -s -- "$device" "$sgdisk_bin" "$boot_size_mb" "$install_size_mb" "$part1" "$part2" > "$output_file" 2>&1' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'if sudo bash -s -- "$device" "$sgdisk_bin" "$raw_device" > "$output_file" 2>&1' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'if output="$(sudo bash -s -- "$device" "$sgdisk_bin"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_report_copy_failure "$boot_mount"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'sudo -n touch "$probe"' "$REPO_DIR/install-wor.sh" \ + && grep -qF '/usr/sbin/diskutil eraseVolume MS-DOS WOR_BOOT "$1"' "$REPO_DIR/install-wor.sh" \ + && grep -qF '/usr/sbin/diskutil eraseVolume ExFAT WOR_INSTALL "$2"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'command_output="$(sudo bash -s -- "$boot_partition" "$install_partition"' "$REPO_DIR/install-wor.sh" \ + && ! grep -qF 'command_output="$(command sudo diskutil eraseVolume' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'less than 1 GiB remains unallocated for the Windows target partition' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'verify_written_image "$DEVICE" "$PART1" "$PART2" "$boot_mount" "$win_mount"' "$REPO_DIR/install-wor.sh" \ + && pass "Darwin creates WOR_BOOT as a real EFI System Partition" \ + || fail "Darwin does not type WOR_BOOT as an EFI System Partition" + #a raw-disk-write denial reads as a bare "Operation not permitted"; the helper must + #recognize that specific text and point the user at Full Disk Access instead of guessing + fda_message="$(run_in_engine 'fake_cmd() { echo "newfs_msdos: /dev/rdisk9s1: Operation not permitted" >&2; return 1; }; open() { :; }; darwin_format_or_die "format the boot partition on /dev/disk9s1" fake_cmd' 2>&1)" + echo "$fda_message" | grep -qF 'Full Disk Access' \ + && echo "$fda_message" | grep -qF 'click the + button' \ + && pass "darwin_format_or_die recognizes an Operation not permitted denial and explains adding the app via +" \ + || fail "darwin_format_or_die gave no Full Disk Access guidance for a permission denial" + fda_message_native="$(run_in_engine 'export WOR_NATIVE_APP=1; fake_cmd() { echo "newfs_msdos: /dev/rdisk9s1: Operation not permitted" >&2; return 1; }; open() { :; }; darwin_format_or_die "format the boot partition on /dev/disk9s1" fake_cmd' 2>&1)" + echo "$fda_message_native" | grep -qF 'find WoR-Flasher.app' \ + && pass "darwin_format_or_die names WoR-Flasher.app when launched as the native app" \ + || fail "darwin_format_or_die did not name WoR-Flasher.app under WOR_NATIVE_APP=1" + fda_message_native_path="$(run_in_engine 'export WOR_NATIVE_APP=1 WOR_APP_BUNDLE_PATH=/Applications/WoR-Flasher.app; fake_cmd() { echo "newfs_msdos: /dev/rdisk9s1: Operation not permitted" >&2; return 1; }; open() { :; }; darwin_format_or_die "format the boot partition on /dev/disk9s1" fake_cmd' 2>&1)" + echo "$fda_message_native_path" | grep -qF 'WoR-Flasher.app at /Applications/WoR-Flasher.app' \ + && pass "darwin_format_or_die includes the exact native app path when available" \ + || fail "darwin_format_or_die did not include WOR_APP_BUNDLE_PATH in the Full Disk Access message" + fda_message_vscode="$(run_in_engine 'export WOR_NATIVE_APP=1 WOR_APP_BUNDLE_PATH=/Applications/WoR-Flasher.app TERM_PROGRAM=vscode; fake_cmd() { echo "newfs_msdos: /dev/rdisk9s1: Operation not permitted" >&2; return 1; }; open() { :; }; darwin_format_or_die "format the boot partition on /dev/disk9s1" fake_cmd' 2>&1)" + echo "$fda_message_vscode" | grep -qF 'also enable Visual Studio Code.app' \ + && pass "darwin_format_or_die names the launcher app when macOS may attribute FDA to it" \ + || fail "darwin_format_or_die did not include a launcher fallback in the Full Disk Access message" + removable_message="$(run_in_engine 'HOST_OS=Darwin; open() { :; }; touch() { echo "touch: /Volumes/WOR_BOOT/.wor-flasher-write-probe: Operation not permitted" >&2; return 1; }; darwin_require_mounted_volume_access /Volumes/WOR_BOOT "write to /Volumes/WOR_BOOT"' 2>&1)" + echo "$removable_message" | grep -qF 'Removable Volumes' \ + && echo "$removable_message" | grep -qF 'Full Disk Access' \ + && echo "$removable_message" | grep -qF 'Shift-Command-G' \ + && echo "$removable_message" | grep -qF '/bin/bash' \ + && grep -qF 'Privacy_FilesAndFolders' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_require_mounted_volume_access "$boot_mount" "write to $boot_mount"' "$REPO_DIR/install-wor.sh" \ + && pass "macOS preflights removable-volume privacy before copying mounted media" \ + || fail "macOS mounted-media TCC guidance or preflight is missing" + generic_message="$(run_in_engine 'fake_cmd() { echo "some other disk error" >&2; return 1; }; open() { :; }; darwin_format_or_die "format the boot partition on /dev/disk9s1" fake_cmd' 2>&1)" + echo "$generic_message" | grep -qF 'Full Disk Access' \ + && fail "darwin_format_or_die wrongly blames Full Disk Access for an unrelated failure" \ + || pass "darwin_format_or_die does not misattribute an unrelated failure to Full Disk Access" + grep -qF 'open "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"' "$REPO_DIR/install-wor.sh" \ + && grep -qF 'darwin_diskutil_format_pair_or_die()' "$REPO_DIR/install-wor.sh" \ + && pass "macOS formats partitions through diskutil instead of raw partition writers" \ + || fail "macOS still formats partitions through the raw disk writer path" + DARWIN_DEVICE_INFO='{"WholeDisk":true,"Internal":false,"VirtualOrPhysical":"Physical","WritableMedia":true,"TotalSize":64000000000,"MediaName":"USB Drive"}' + is_safe_target_device /dev/disk2 && pass "Darwin accepts current writable-media metadata" || fail "Darwin rejected current writable-media metadata" + for safety_case in \ + 'internal:{"WholeDisk":true,"Internal":true,"VirtualOrPhysical":"Physical","ReadOnlyMedia":false}' \ + 'virtual:{"WholeDisk":true,"Internal":false,"VirtualOrPhysical":"Virtual","ReadOnlyMedia":false}' \ + 'read-only:{"WholeDisk":true,"Internal":false,"VirtualOrPhysical":"Physical","ReadOnlyMedia":true}' \ + 'partition:{"WholeDisk":false,"Internal":false,"VirtualOrPhysical":"Physical","ReadOnlyMedia":false}'; do + case_name="${safety_case%%:*}" + DARWIN_DEVICE_INFO="${safety_case#*:}" + is_safe_target_device /dev/disk2 && fail "Darwin accepted a $case_name disk" || pass "Darwin rejects a $case_name disk" + done + eval "$original_darwin_plist_json" + unset DARWIN_DEVICE_INFO + HOST_OS="$test_host_os" + ROOT_DEV="$test_root_dev" +else + skip "Darwin disk safety test needs jq" +fi + info "== Detecting builds from the catalog ==" #the newest build an ARMv8.0 Pi can boot, and the newest build overall GOOD_BID="$(RPI_MODEL=4 get_bid 11)" @@ -326,9 +2714,17 @@ NEWEST_BID="$(RPI_MODEL=5 get_bid 11)" RPI_MODEL=4 cpu_supports_bid "$GOOD_BID" && pass "cpu_supports_bid allows $GOOD_BID on a Pi 4" || fail "cpu_supports_bid rejected $GOOD_BID on a Pi 4" unset RPI_MODEL -######## Every model gets a fresh, uncached run +catalog_fixture=$'<LanguageCode>en-gb</LanguageCode>\n<FilePath>https://example.com/en-gb.esd</FilePath>\n<Size>1</Size>\n</File>\n<LanguageCode>en-us</LanguageCode>\n<FilePath>https://example.com/en-us.esd</FilePath>\n<Size>2</Size>\n</File>\n<Languages>' +catalog_entry="$(get_esd_catalog_entry "$catalog_fixture" en-us)" +grep -qF 'https://example.com/en-us.esd' <<<"$catalog_entry" \ + && ! grep -qF 'https://example.com/en-gb.esd' <<<"$catalog_entry" \ + && pass "ESD catalog parser selects the requested language" \ + || fail "ESD catalog parser selected the wrong language" + +######## Every model gets a fresh model-specific run; the common PE installer is reused #sourcing install-wor.sh sets IFS to a newline, so split the model list explicitly +pe_installer_populated=0 for model in $(tr ' ' '\n' <<<"$TEST_MODELS") ;do info "== Raspberry Pi $model, fresh run ==" bid="$(RPI_MODEL=$model get_bid 11)" @@ -339,9 +2735,14 @@ for model in $(tr ' ' '\n' <<<"$TEST_MODELS") ;do echo " using build $bid" seed_winfiles "$bid" - run_flasher TERM=unknown BID="$bid" RPI_MODEL="$model" DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=0 + run_flasher TERM=unknown BID="$bid" RPI_MODEL="$model" DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=1 expect_ok "Pi $model dry run completes" - expect_output "Pi $model downloads the PE installer" "Downloading WoR PE-based installer" + if [ "$pe_installer_populated" == 0 ];then + expect_output "Pi $model downloads the PE installer" "Downloading WoR PE-based installer" + pe_installer_populated=1 + else + expect_output "Pi $model reuses the shared PE installer" "cached copy is up to date" + fi expect_output "Pi $model downloads UEFI firmware" "UEFI firmware" expect_output "Pi $model stops before flashing" "DRY_RUN" expect_no_output "Pi $model suppresses unknown terminal warnings" "unknown terminal" @@ -354,21 +2755,84 @@ for model in $(tr ' ' '\n' <<<"$TEST_MODELS") ;do for folder in peinstaller "pi${model}-uefipackage" ;do [ -f "$TEST_DL_DIR/$folder/.wor-flasher-version" ] && pass "Pi $model stamped $folder" || fail "Pi $model left $folder unstamped" + [ -s "$TEST_DL_DIR/$folder/.wor-flasher-sha256" ] && pass "Pi $model checksummed $folder" || fail "Pi $model left $folder without an integrity manifest" done done ######## Cache modes, which need a populated cache to test against info "== Cache modes ==" -run_flasher BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=0 run_flasher BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=1 expect_output "USE_CACHE=1 reuses a current cache" "cached copy is up to date" +printf 'tampered\n' >> "$TEST_DL_DIR/pi4-uefipackage/RPI_EFI.fd" +run_flasher BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=1 +expect_output "USE_CACHE=1 refreshes modified cached content" "Downloading Pi4 UEFI firmware" +info "== Linux boot partition layout ==" +seed_bootable_winfiles "$GOOD_BID" +run_flasher_real BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 +expect_ok "Pi 4 real loop-device flash completes" +expect_output "Pi 4 verifies the written image" "Written image verified successfully" + +boot_partition="$(get_partition "$DEV_INSTALL" 1)" +partition_layout="$(sudo parted -ms "$DEV_INSTALL" unit s print)" || die "Could not inspect the test loop-device partition layout." +partition_count="$(awk -F: '$1 ~ /^[0-9]+$/ { count++ } END { print count + 0 }' <<<"$partition_layout")" +boot_filesystem="$(awk -F: '$1 == 1 { print $5 }' <<<"$partition_layout")" +boot_mount="$(mktemp -d)" +if sudo mount "$boot_partition" "$boot_mount"; then + [ "$partition_count" == 2 ] \ + && pass "Pi 4 flash creates exactly two partitions" \ + || fail "Pi 4 flash creates an unexpected partition layout" + [ "$boot_filesystem" == fat32 ] \ + && pass "Pi 4 boot partition is FAT32" \ + || fail "Pi 4 first partition is not FAT32" + sudo test -f "$boot_mount/EFI/BOOT/BOOTAA64.EFI" \ + && pass "Pi 4 boot loader is on the first FAT partition" \ + || fail "Pi 4 first partition lacks the UEFI fallback boot loader" + sudo umount "$boot_mount" +else + fail "Pi 4 boot partition could not be mounted for verification" +fi +rmdir "$boot_mount" + +info "== Interruption and authorization failures ==" + +fake_sudo_dir="$(mktemp -d)" +cat > "$fake_sudo_dir/sudo" <<'FAKESUDO' +#!/bin/bash +echo "sudo: a password is required" 1>&2 +exit 1 +FAKESUDO +chmod +x "$fake_sudo_dir/sudo" + +run_flasher_real PATH="$fake_sudo_dir:$PATH" BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 +expect_fail "flashing stops immediately when sudo/authorization fails" +expect_output "the failure names the affected step" "Failed to make GPT partition table" +expect_no_output "nothing continues past an authorization failure" "Generating partitions" +expect_no_output "nothing continues past an authorization failure" "Copying files" +expect_no_output "nothing continues past an authorization failure" "script has completed" +rm -rf "$fake_sudo_dir" + +hanging_sudo_dir="$(mktemp -d)" +cat > "$hanging_sudo_dir/sudo" <<'HANGINGSUDO' +#!/bin/bash +#simulates being stuck at an interactive password prompt, like the real sudo would be +echo "Password:" +sleep 30 +HANGINGSUDO +chmod +x "$hanging_sudo_dir/sudo" + +run_flasher_interrupted 3 PATH="$hanging_sudo_dir:$PATH" BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 +expect_fail "Ctrl+C while stuck at the password prompt stops the flasher" +expect_output "Ctrl+C shows a clear interrupted message" "Interrupted" +expect_no_output "Ctrl+C does not let the flash continue in the background" "script has completed" +rm -rf "$hanging_sudo_dir" + expect_no_output "USE_CACHE=1 downloads nothing" "Downloading ARM64 drivers" echo 'https://example.com/stale.zip' > "$TEST_DL_DIR/driverpackage/.wor-flasher-version" run_flasher BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=1 expect_output "USE_CACHE=1 refreshes a stale component" "Downloading ARM64 drivers" -expect_output "USE_CACHE=1 keeps the current ones" "peinstaller - cached copy is up to date" +expect_output "USE_CACHE=1 keeps the current ones" "pi4-uefipackage - cached copy is up to date" run_flasher BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 expect_output "USE_CACHE=2 skips update checks" "without checking for updates" @@ -384,6 +2848,7 @@ else expect_fail "$NEWEST_BID is refused on a Pi 4" expect_output "the refusal explains why" "ARMv8.1" + seed_winfiles "$NEWEST_BID" run_flasher BID="$NEWEST_BID" RPI_MODEL=5 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 expect_ok "$NEWEST_BID is allowed on a Pi 5" fi @@ -396,12 +2861,12 @@ expect_fail "self-install is refused on a recovery-sized drive" run_flasher_with_input '' BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_RECOVERY" USE_CACHE=2 expect_ok "recovery-sized drive automatically uses recovery mode" -expect_output "automatic recovery mode is recorded" "CAN_INSTALL_ON_SAME_DRIVE: 0" +expect_output "automatic recovery mode is recorded" "Installation mode: Recovery drive for another >16 GB drive" expect_no_output "recovery-sized drive does not ask for install mode" "Choose the installation mode" run_flasher_with_input '2\n' BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" USE_CACHE=2 expect_ok "large drive can be used as a recovery drive" -expect_output "large-drive recovery choice is recorded" "CAN_INSTALL_ON_SAME_DRIVE: 0" +expect_output "large-drive recovery choice is recorded" "Installation mode: Recovery drive for another >16 GB drive" run_flasher BID="$GOOD_BID" RPI_MODEL=9 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 expect_fail "an unknown RPI_MODEL is rejected" @@ -409,95 +2874,113 @@ expect_fail "an unknown RPI_MODEL is rejected" info "== GUI handoff ==" #Regression test: lxterminal and gnome-terminal reuse an existing process, so the launched #terminal does not inherit exported variables. env -i reproduces that. -handoff_out="$( - CONFIG_TXT='arm_64bit=1 -# a "quoted" line with $(touch '"$TEST_DIR"'/INJECTED) and `touch '"$TEST_DIR"'/INJECTED2` -armstub=RPI_EFI.fd' - cli_script="$REPO_DIR/install-wor.sh" - export CONFIG_TXT cli_script - env_file="$(mktemp)" - # declare -p emits shell-escaped declarations; sourcing that generated file is the - # behavior under test, so SC2090's warning about indirect command expansion is a false positive. - #shellcheck disable=SC2090 - declare -p CONFIG_TXT cli_script > "$env_file" - env -i /bin/bash -c "source '$env_file'; printf '%s\n' \"\$cli_script\"; echo \"\$CONFIG_TXT\" | wc -l" - rm -f "$env_file" -)" +injection_marker="$TEST_DIR/CONFIG_TXT_INJECTED" +CONFIG_TXT="arm_64bit=1 +# a \"quoted\" line with \$(touch \"$injection_marker\") +armstub=RPI_EFI.fd" +cli_script="$REPO_DIR/install-wor.sh" +env_file="$(mktemp)" +runner_file="$(mktemp)" +# declare -p emits shell-escaped declarations; sourcing that generated file is the +# behavior under test, so SC2090's warning about indirect command expansion is a false positive. +#shellcheck disable=SC2090 +declare -p CONFIG_TXT cli_script > "$env_file" +printf 'source ' > "$runner_file" +printf '%q' "$env_file" >> "$runner_file" +printf '\n' >> "$runner_file" +printf '%s\n' 'printf "%s\n" "$cli_script"' >> "$runner_file" +printf '%s\n' 'printf "%s\n" "$CONFIG_TXT" | wc -l' >> "$runner_file" +handoff_out="$(env -i /bin/bash "$runner_file")" +rm -f "$env_file" "$runner_file" +unset CONFIG_TXT cli_script injection_marker [ "$(head -n1 <<<"$handoff_out")" == "$REPO_DIR/install-wor.sh" ] \ && pass "values survive a terminal that does not inherit the environment" \ || fail "values were lost in a terminal that does not inherit the environment" [ "$(tail -n1 <<<"$handoff_out")" == 3 ] && pass "a multi-line CONFIG_TXT stays intact" || fail "CONFIG_TXT was mangled" -if [ -e "$TEST_DIR/INJECTED" ] || [ -e "$TEST_DIR/INJECTED2" ];then +if [ -e "$TEST_DIR/CONFIG_TXT_INJECTED" ];then fail "CONFIG_TXT was executed as code" else pass "CONFIG_TXT is not executed as code" fi -info "== Self-updater ==" -if ! command -v git >/dev/null || ! git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 ;then - skip "self-updater tests need a git checkout" +info "== Update check ==" +#The tool must never rewrite its own installation. A half-updated disk flasher is far more +#dangerous than an out-of-date one, so the release check is read-only by construction. +if grep -qE 'git (fetch|merge|pull|reset|clean|restore)' "$REPO_DIR/install-wor.sh" ;then + fail "install-wor.sh still runs a mutating git command; the engine must not update itself" +else + pass "install-wor.sh runs no mutating git command" +fi + +#repair_missing_files legitimately restores missing tracked files from the LOCAL revision, +#so the launcher may still call git restore. What it must not do is fetch or merge a remote. +if grep -qE 'git (-C "\$REPO_DIR" )?(fetch|merge|pull|ls-remote)' "$REPO_DIR/src/macos-app/Contents/MacOS/WoR-Flasher" ;then + fail "the macOS launcher still fetches or merges from a git remote" +else + pass "the macOS launcher never fetches or merges from a git remote" +fi + +grep -qF -- '--check-git' "$REPO_DIR/src/updater.mjs" "$REPO_DIR/package.json" \ + && fail "the retired --check-git updater mode is still referenced" \ + || pass "the retired --check-git updater mode is gone" + +grep -qF 'execSync' "$REPO_DIR/src/lib/node-runtime.mjs" \ + && fail "the Node runtime library still shells out" \ + || pass "the Node runtime library shells out to nothing" + +if ! command -v node >/dev/null ;then + skip "the release-check CLI needs node" + skip "the release check needs node to prove it leaves a checkout untouched" else - branch="$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD)" - if [ "$branch" == HEAD ];then - skip "self-updater tests need a checked-out branch, not a detached HEAD" + #an unroutable slug makes the request fail without touching the network fixture + update_out="$(node "$REPO_DIR/src/updater.mjs" --check-release "--repo-dir=$REPO_DIR" --repo-slug='../evil' 2>/dev/null)" + [ "$update_out" == UNKNOWN ] \ + && pass "the release check refuses an unsafe repository slug" \ + || fail "the release check did not reject an unsafe repository slug (got '$update_out')" + + if ! command -v git >/dev/null || ! git -C "$REPO_DIR" rev-parse --git-dir >/dev/null 2>&1 ;then + skip "proving the checkout is left untouched needs a git checkout" else - #a disposable clone of the branch actually being worked on - never runs against $REPO_DIR itself - clone_dir="$TEST_DIR/self-update-clone" - rm -rf "$clone_dir" - if ! git clone -q --branch "$branch" "$REPO_DIR" "$clone_dir" 2>/dev/null ;then - fail "could not clone $REPO_DIR (branch $branch) to test the self-updater" + branch="$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD)" + if [ "$branch" == HEAD ];then + skip "proving the checkout is left untouched needs a branch, not a detached HEAD" else - #point the clone's self-updater at this same repo/branch, so it dynamically pulls whatever is actually being worked on - update_env=(UPDATE_REPO_URL="$REPO_DIR" UPDATE_REF="$branch" NO_UPDATE=0 ROOT_DEV=/dev/__wor_flasher_test_root__ DL_DIR="$TEST_DL_DIR" WIN_LANG="$TEST_WIN_LANG" RUN_MODE=cli DRY_RUN=1 BID="$GOOD_BID" RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2) - - progress "self-updater: checking clean clone already on $branch" - LAST_OUT="$(run_with_timeout env "${update_env[@]}" "$clone_dir/install-wor.sh" 2>&1)" - LAST_CODE=$? - progress "self-updater clean-clone check finished with exit $LAST_CODE" - expect_ok "already level with $branch: flasher still runs" - expect_no_output "already level with $branch: self-updater stays quiet" "Auto-updating wor-flasher" - - #fall one commit behind $REPO_DIR's $branch, so the clone actually has something to pull - if git -C "$clone_dir" reset -q --hard HEAD~1 2>/dev/null ;then + #a disposable clone, deliberately left one commit behind - never runs against $REPO_DIR itself + clone_dir="$TEST_DIR/update-check-clone" + rm -rf "$clone_dir" + if ! git clone -q --branch "$branch" "$REPO_DIR" "$clone_dir" 2>/dev/null ;then + fail "could not clone $REPO_DIR (branch $branch) to test the update check" + elif ! parent_commit="$(git -C "$clone_dir" rev-parse --verify HEAD~1 2>/dev/null)" ;then + skip "$branch has no earlier commit to fall behind" + elif ! git -C "$clone_dir" checkout -q -b "wor-test-behind-$$" "$parent_commit" \ + || ! git -C "$clone_dir" branch -q --set-upstream-to="origin/$branch" ;then + fail "could not prepare a non-destructive behind-remote test branch" + else + behind_commit="$(git -C "$clone_dir" rev-parse HEAD)" printf '\n' >> "$clone_dir/README.md" - progress "self-updater: checking dirty clone protection" - LAST_OUT="$(run_with_timeout env "${update_env[@]}" "$clone_dir/install-wor.sh" 2>&1)" - LAST_CODE=$? - progress "self-updater dirty-clone check finished with exit $LAST_CODE" - expect_ok "dirty behind $branch: flasher still runs" - expect_output "dirty behind $branch: self-updater preserves local changes" "Skipping automatic update because this checkout has uncommitted changes" - git -C "$clone_dir" diff --quiet \ - && fail "dirty behind $branch: self-updater discarded local changes" \ - || pass "dirty behind $branch: local changes were preserved" - git -C "$clone_dir" reset -q --hard HEAD - - progress "self-updater: checking behind clone fast-forward and reload" - LAST_OUT="$(run_with_timeout env "${update_env[@]}" "$clone_dir/install-wor.sh" 2>&1)" + LAST_OUT="$(run_with_timeout env NO_UPDATE=0 ROOT_DEV=/dev/__wor_flasher_test_root__ \ + DL_DIR="$TEST_DL_DIR" WIN_LANG="$TEST_WIN_LANG" RUN_MODE=cli DRY_RUN=1 BID="$GOOD_BID" \ + RPI_MODEL=4 DEVICE="$DEV_INSTALL" CAN_INSTALL_ON_SAME_DRIVE=1 USE_CACHE=2 \ + "$clone_dir/install-wor.sh" 2>&1)" LAST_CODE=$? - progress "self-updater behind-clone check finished with exit $LAST_CODE" - expect_ok "behind $branch: flasher still runs after update" - expect_output "behind $branch: self-updater detects the update" "Auto-updating wor-flasher" - expect_output "behind $branch: self-updater pulls and reloads" "Reloading script" - [ "$(git -C "$clone_dir" rev-parse HEAD)" == "$(git -C "$REPO_DIR" rev-parse "$branch")" ] \ - && pass "self-updater fast-forwarded the clone to $branch" \ - || fail "self-updater did not fast-forward the clone to $branch" - else - skip "$branch has no earlier commit to test dirty update protection" - skip "$branch has no earlier commit to fall behind" + expect_ok "a checkout behind its remote still runs" + expect_no_output "the engine never announces a self-update" "Auto-updating wor-flasher" + expect_no_output "the engine never reloads itself after updating" "Reloading script" + [ "$(git -C "$clone_dir" rev-parse HEAD)" == "$behind_commit" ] \ + && pass "the update check left the checkout on its original commit" \ + || fail "the update check moved the checkout to another commit" + git -C "$clone_dir" diff --quiet -- README.md \ + && fail "the update check discarded local changes" \ + || pass "the update check preserved local changes" fi fi fi fi -info "== terminal-run ==" -if [ -z "$DISPLAY" ] && [ -z "$WAYLAND_DISPLAY" ];then - skip "terminal-run needs a display" -elif ! command -v lxterminal >/dev/null && ! command -v xfce4-terminal >/dev/null && ! command -v mate-terminal >/dev/null && ! command -v xterm >/dev/null && ! command -v konsole >/dev/null && ! command -v terminator >/dev/null && ! command -v gnome-terminal >/dev/null && ! command -v qterminal >/dev/null && ! command -v x-terminal-emulator >/dev/null ;then - skip "terminal-run needs a terminal emulator" -else - DEBUG=1 timeout 30 "$REPO_DIR/terminal-run" 'true' 'wor-flasher test' >/dev/null 2>&1 \ - && pass "terminal-run launches a terminal" || fail "terminal-run could not launch a terminal" -fi +info "== no spawned terminal emulator ==" +#the GUI runs the engine in-process, so no terminal launcher may return to the tree +[ ! -e "$REPO_DIR/terminal-run" ] \ + && pass "terminal-run is not present" || fail "terminal-run reappeared; the GUI must not spawn a terminal" ######## Summary