diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index d7af6b536..096116e05 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -2,9 +2,9 @@ name: Build MacOS on: push: - branches: [ "qml" ] + branches: ["main"] pull_request: - branches: [ "qml" ] + branches: ["main"] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) @@ -19,16 +19,65 @@ jobs: working-directory: src steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v3 - - name: Install Qt - uses: jurplel/install-qt-action@v3 + # Use Cmake < 4.0 + - name: Setup CMake 3.31 + uses: jwlawson/actions-setup-cmake@v2 + with: + cmake-version: "3.31.x" - - name: Configure CMake - # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. - # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + # Use Qt 5, not Qt 6 + - name: Install Qt 5 + run: | + brew update + brew install qt@5 - - name: Build - # Build your program with the given configuration - run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + - name: Configure CMake + run: cmake -S . -B ../build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DCMAKE_PREFIX_PATH="$(brew --prefix qt@5)" + + - name: Build + run: cmake --build ../build --config ${{ env.BUILD_TYPE }} + + # 5) Ad-hoc code sign (for CI purposes only) + - name: Ad-hoc Code Sign .app + run: | + CAPP="../build/unraid-usb-creator.app" + codesign --remove-signature "$CAPP" + codesign --force --deep --sign - "$CAPP" + + # 6) Remove quarantine attribute + - name: Remove quarantine attribute + run: sudo xattr -rc ../build/unraid-usb-creator.app + + # 7) Install Homebrew create-dmg (shell‐script version) + - name: Install create-dmg + run: | + brew update + brew install create-dmg + + # 8) Package the signed .app into a DMG that has "drag to Applications" + - name: Create DMG with drag-to-Applications UI + run: | + # Make sure there's a Releases/ folder in the repo root + mkdir -p "${{ github.workspace }}/Releases" + + # Create a “drag-to-Applications” DMG: + create-dmg \ + --volname "Unraid Installer" \ + --volicon "${{ github.workspace }}/src/icons/unraid.icns" \ + --background "${{ github.workspace }}/src/icons/UN-logotype-gradient.png" \ + --window-pos 200 120 \ + --window-size 800 400 \ + --icon "unraid-usb-creator.app" 200 190 \ + --hide-extension "unraid-usb-creator.app" \ + --app-drop-link 600 185 \ + "${{ github.workspace }}/Releases/unraid-usb-creator.dmg" \ + "${{ github.workspace }}/build/unraid-usb-creator.app" + + - name: Upload Mac DMG + uses: actions/upload-artifact@v4 + with: + name: UnraidInstallerMacOS + path: Releases/unraid-usb-creator.dmg +# Might need to use `codesign` to sign the app and everything related to that diff --git a/.github/workflows/build-ubuntu-deb.yml b/.github/workflows/build-ubuntu-deb.yml index cde42e01b..145fb4572 100644 --- a/.github/workflows/build-ubuntu-deb.yml +++ b/.github/workflows/build-ubuntu-deb.yml @@ -2,21 +2,32 @@ name: Build Ubuntu .deb on: push: - branches: [ "qml" ] + branches: [ "main" ] pull_request: - branches: [ "qml" ] + branches: [ "main" ] jobs: build: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - name: Install dependencies - run: sudo apt install -y --no-install-recommends build-essential devscripts debhelper cmake git libarchive-dev libcurl4-gnutls-dev - qtbase5-dev qtbase5-dev-tools qtdeclarative5-dev libqt5svg5-dev qttools5-dev libgnutls28-dev - qml-module-qtquick2 qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qtquick-templates2 qml-module-qtquick-window2 qml-module-qtgraphicaleffects + run: | + sudo apt update + sudo apt install -y --no-install-recommends build-essential devscripts debhelper cmake git libarchive-dev libcurl4-gnutls-dev + sudo apt install -y --no-install-recommends qtbase5-dev qtbase5-dev-tools qtdeclarative5-dev libqt5svg5-dev qttools5-dev libgnutls28-dev + sudo apt install -y --no-install-recommends qml-module-qtquick2 qml-module-qtquick-controls2 qml-module-qtquick-layouts qml-module-qtquick-templates2 qml-module-qtquick-window2 qml-module-qtgraphicaleffects - name: Build run: debuild -uc -us + + - name: Workaround actions/upload-artifact#176 + run: echo "artifacts_path=$(realpath ..)" >> $GITHUB_ENV + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: Deb + path: ${{ env.artifacts_path }}/*.deb diff --git a/.github/workflows/build-ubuntu-flatpak.yml b/.github/workflows/build-ubuntu-flatpak.yml new file mode 100644 index 000000000..1846034fb --- /dev/null +++ b/.github/workflows/build-ubuntu-flatpak.yml @@ -0,0 +1,63 @@ +name: Build Flatpak + +on: + push: + branches: ["main", "flatpak"] + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + - name: Install Flatpak & Builder + run: | + sudo apt update + sudo apt install -y flatpak flatpak-builder + + - name: Add Flathub Remote + run: | + flatpak --user remote-add --if-not-exists flathub \ + https://dl.flathub.org/repo/flathub.flatpakrepo + + # Build Flatpak repo into local "repo" directory + - name: Build Flatpak Repository + run: | + # force‐clean ensures a fresh build each time + flatpak-builder --user --force-clean \ + --install-deps-from=flathub \ + --repo=repo \ + build-dir \ + flatpak/com.limetech.unraid-usb-creator.yml + + # Expose that local repo so we can install from it + - name: Add Local Flatpak Repo + run: | + flatpak --user remote-add --no-gpg-verify local-repo repo + + + - name: Install from Local Repo + run: | + flatpak --user install --assumeyes local-repo \ + com.limetech.unraid-usb-creator + + # Create a single‐file .flatpak bundle from that local repo + - name: Create Flatpak Bundle + run: | + flatpak build-bundle \ + repo \ + com.limetech.unraid-usb-creator.flatpak \ + com.limetech.unraid-usb-creator \ + --arch=x86_64 \ + --runtime-repo=https://dl.flathub.org/repo/flathub.flatpakrepo + + - name: Upload Flatpak Artifact + uses: actions/upload-artifact@v4 + with: + name: Flatpak + path: com.limetech.unraid-usb-creator.flatpak + +# Need to add signing & distributing \ No newline at end of file diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml new file mode 100644 index 000000000..e09abb1c6 --- /dev/null +++ b/.github/workflows/build-windows.yml @@ -0,0 +1,38 @@ +name: Build Windows + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +env: + BUILD_TYPE: Release + +jobs: + + build: + runs-on: ubuntu-latest + container: ghcr.io/unraid/usb-creator-next/usb-creator-mxe:latest + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Configure CMake + run: | + cmake src -B build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DQt5_DIR=/opt/mxe/usr/i686-w64-mingw32.shared/qt5/lib/cmake/Qt5 + + - name: Build + run: | + cmake --build build --config ${{env.BUILD_TYPE}} + + - name: Run NSIS + run: | + makensis build/unraid-usb-creator.nsi + + - name: Upload Artifact + uses: actions/upload-artifact@v4 + with: + name: WindowsInstaller + path: build/unraid-usb-creator-*.exe diff --git a/.github/workflows/mxe-docker.yml b/.github/workflows/mxe-docker.yml new file mode 100644 index 000000000..bf1cf45c8 --- /dev/null +++ b/.github/workflows/mxe-docker.yml @@ -0,0 +1,29 @@ +name: Build and Push Docker Image + +on: + workflow_dispatch: # Enables manual triggering of the workflow via a button push + +jobs: + build-and-push-docker: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push + uses: docker/build-push-action@v3 + with: + push: true + file: src/windows/Dockerfile.win32dyn + tags: ghcr.io/${{ github.repository }}/usb-creator-mxe:latest diff --git a/.gitignore b/.gitignore index 961ad1a24..e7dbca48a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,22 @@ -build** +build/** + .vscode obj-** debian/rpi-imager/** debian/.debhelper** debian/files debian/*.substvars -debian/debhelper** \ No newline at end of file +debian/debhelper**.DS_Store + + +# For macOS .dmg creation in workflow +Releases +*.dmg + +*.DS_Store + +# For flatpak builds +*.flatpak-builder +repo +*.flatpak +build-dir diff --git a/README.md b/README.md index ae5d4bfc6..31def67d2 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,16 @@ -# rpi-imager +# Unraid USB Creator -Raspberry Pi Imaging Utility +Unraid USB Creation Utility -- Download the latest version for Windows, macOS and Ubuntu from the [Raspberry Pi downloads page](https://www.raspberrypi.com/software/). -- To install on Raspberry Pi OS, use `sudo apt update && sudo apt install rpi-imager`. +Download the latest version for Windows, macOS and Ubuntu from the [USB Creator Download Page](https://unraid.net/getting-started/). + +![main page](./screenshots/main_page.png) -# How to use Raspberry Pi Imager +Based on the work by [Raspberry Pi](https://github.com/raspberrypi/rpi-imager) -Please see our [official documentation](https://www.raspberrypi.com/documentation/computers/getting-started.html#raspberry-pi-imager). +## How to use the Unraid USB Creator + +Please see our [official documentation](https://docs.unraid.net/unraid-os/getting-started/quick-install-guide/). ## License @@ -32,21 +35,13 @@ sudo apt install --no-install-recommends build-essential devscripts debhelper cm #### Get the source ``` -git clone --depth 1 https://github.com/raspberrypi/rpi-imager -``` - -#### Building on the Pi - -If building on a device with limited memory (e.g. 1 GB Pi), disable parallel build or it may run out of memory: - -``` -export DEB_BUILD_OPTIONS="parallel=1" +git clone --depth 1 https://github.com/unraid/usb-creator-next ``` #### Build the Debian package ``` -cd rpi-imager +cd usb-creator-next debuild -uc -us ``` @@ -55,7 +50,7 @@ Can install it with apt: ``` cd .. -sudo apt install ./rpi-imager*.deb +sudo apt install ./unraid-usb-creator*.deb ``` It should create an icon in the start menu under "Utilities" or "Accessories". @@ -75,13 +70,13 @@ sudo yum install git gcc gcc-c++ make cmake libarchive-devel libcurl-devel lzma- #### Get the source ``` -git clone --depth 1 https://github.com/raspberrypi/rpi-imager +git clone --depth 1 https://github.com/unraid/usb-creator-next ``` #### Build and install the software ``` -cd rpi-imager +cd usb-creator-next mkdir -p build cd build cmake ../src @@ -91,76 +86,78 @@ sudo make install ### Windows -#### Get dependencies +#### Cross-compile for Windows with Docker + MXE -- Get the Qt online installer from: https://www.qt.io/download-open-source -During installation, choose a Qt 5.x with Mingw32 32-bit toolchain and CMake. +Prerequisite: [Docker](https://www.docker.com/) -- If using the official Qt distribution that does NOT have schannel (Windows native SSL library) support, compile OpenSSL libraries ( https://wiki.qt.io/Compiling_OpenSSL_with_MinGW ) and copy the libssl/crypto DLLs to C:\qt\5.x\mingw73_32\bin the include files to C:\qt\5.x\mingw73_32\include and the import library files to C:\qt\5.x\mingw73_32\lib +Dockerfile: src/windows/Dockerfile.win32dyn -- For building installer get Nullsoft scriptable install system: https://nsis.sourceforge.io/Download +To build the image (might take a few hours) and start a new container in interactive mode: +``` +docker build --force-rm -f Dockerfile.win32dyn -t unraid/usb-creator:win32dyn . +docker run --rm -it unraid/usb-creator:win32dyn +``` -- It is assumed you already have a proper code signing certificate, and signtool.exe from the Windows SDK installed. -If NOT and are you only compiling for your own personal use, comment out all lines mentioning signtool from CMakelists.txt and the .nsi installer script. +Or, to pull the image from GitHub and start a new container in interactive mode: +``` +docker pull ghcr.io/unraid/usb-creator-next/usb-creator-mxe:latest +docker run --rm -it ghcr.io/unraid/usb-creator-next/usb-creator-mxe:latest +``` -#### Building +Then, inside the container: +``` +git clone https://github.com/unraid/usb-creator-next +cd usb-creator-next +mkdir build +cd build +cmake ../src -DQt5_DIR=/opt/mxe/usr/i686-w64-mingw32.shared/qt5/lib/cmake/Qt5 -DCMAKE_BUILD_TYPE=Release +cmake --build . --config Release +makensis unraid-usb-creator.nsi -Building can be done manually using the command-line, using "cmake", "make", etc., but if you are not that familar with setting up a proper Windows build environment (setting paths, etc.), it is easiest to use the Qt creator GUI instead. +``` -- Download source .zip from github and extract it to a folder on disk -- Open src/CMakeLists.txt in Qt creator. -- For builds you distribute to others, make sure you choose "Release" in the toolchain settings and not the debug flavour. -- Menu "Build" -> "Build all" -- Result will be in build_rpi-imager_someversion -- Go to the BUILD folder, right click on the .nsi script "Compile NSIS script", to create installer. +#### Signing -Note: the CMake integration in Qt Creator is a bit flaky at times. If you made any custom changes to the CMakeLists.txt file and it subsequently gets in an endless loop where it never finishes the "configures" stage while re-processing the file, delete "build_rpi-imager_someversion" directory and try again. +To sign the .exe installer on Windows, [follow these instructions](https://github.com/unraid/digicert-keylockertools) (private repo) ### macOS -#### Get dependencies +#### Get macOS dependencies - Get the Qt online installer from: https://www.qt.io/download-open-source During installation, choose a Qt 5.x edition and CMake. - For creating a .DMG for distribution you can use an utility like: https://github.com/sindresorhus/create-dmg - It is assumed you have an Apple developer subscription, and already have a "Developer ID" code signing certificate for distribution outside the Mac Store. (Privileged apps are not allowed in the Mac store) -#### Building +#### Building on Mac - Download source .zip from github and extract it to a folder on disk - Start Qt Creator (may need to start "finder" navigate to home folder using the "Go" menu, and find Qt folder to start it manually as it may not have created icon in Applications), and open src/CMakeLists.txt - Menu "Build" -> "Build all" -- Result will be in build_rpi-imager_someversion -- For distribution to others: code sign the .app, create a DMG, code sign the DMG, submit it for notarization to Apple and staple the notarization ticket to the DMG. +- Result will be in build_unraid-usb-creator_someversion +- Install create-dmg for signing `brew install create-dmg` -E.g.: +#### Signing on Mac -``` -cd build-rpi-imager-Desktop_Qt_5_14_1_clang_64bit-Release/ -codesign --deep --force --verify --verbose --sign "YOUR KEYID" --options runtime rpi-imager.app -mv rpi-imager.app "Raspberry Pi Imager.app" -create-dmg Raspberry\ Pi\ Imager.app -mv Raspberry\ Pi\ Imager\ .dmg imager.dmg -xcrun altool --notarize-app -t osx -f imager.dmg --primary-bundle-id="org.raspberrypi.imagingutility" -u YOUR-EMAIL-ADDRESS -p YOUR-APP-SPECIFIC-APPLE-PASSWORD -itc_provider TEAM-ID-IF-APPLICABLE -xcrun stapler staple imager.dmg -``` +For distribution to others: -### Linux embedded (netboot) build +1. code sign the .app +2. create a DMG +3. submit the DMG for notarization to Apple +4. staple the notarization ticket to the DMG. -The embedded build runs under a minimalistic Linux distribution compiled by buildroot. -To build: - -- You must be running a Linux system, and have the buildroot dependencies installed as listed in the buildroot manual: https://buildroot.org/downloads/manual/manual.html#requirement -- Run: +E.g.: +```sh +cd build-usb-creator-next-Desktop_Qt_5_14_1_clang_64bit-Release/ +codesign --deep --force --verify --verbose --sign "YOUR KEYID" --options runtime unraid-usb-creator.app +mv unraid-usb-creator.app "Unraid USB Creator.app" +create-dmg "Unraid USB Creator.dmg" "Unraid USB Creator.app" +mv Unraid\ USB\ Creator.dmg imager.dmg +xcrun notarytool submit "Unraid USB Creator.dmg" --apple-id YOUR-EMAIL-ADDRESS --password YOUR-APP-SPECIFIC-APPLE-PASSWORD --team-id TEAM-ID-FOUND-ON-https://developer.apple.com/account +xcrun notarytool wait SUBMISSION_ID --apple-id YOUR-EMAIL-ADDRESS --password YOUR-APP-SPECIFIC-APPLE-PASSWORD --team-id TEAM-ID-FOUND-ON-https://developer.apple.com/account +xcrun stapler staple "Unraid USB Creator.dmg" ``` -cd rpi-imager/embedded -./build.sh -``` - -The result will be in the "output" directory. -The files can be copied to a FAT32 formatted SD card, and inserted in a Pi for testing. -If you would like to build a (signed) netboot image there are tools for that at: https://github.com/raspberrypi/usbboot/tree/master/tools ## Other notes @@ -176,33 +173,36 @@ So can simply create another 'start menu shortcut' to the application with that ### Telemetry -In order to understand usage of the application (e.g. uptake of Raspberry Pi Imager versions and which images and operating systems are most popular) when using the default image repository, the URL, operating system name and category (if present) of a selected image are sent along with the running version of Raspberry Pi Imager, your operating system, CPU architecture, locale and Raspberry Pi revision (if applicable) to https://rpi-imager-stats.raspberrypi.com by downloadstatstelemetry.cpp. +Telemetry is currently disabled on the Unraid USB Creator -This web service is hosted by [Heroku](https://www.heroku.com) and only stores an incrementing counter using a [Redis Sorted Set](https://redis.io/topics/data-types#sorted-sets) for each URL, operating system name and category per day in the `eu-west-1` region and does not associate any personal data with those counts. This allows us to query the number of downloads over time and nothing else. +## Images -The last 1,500 requests to the service are logged for one week before expiring as this is the [minimum log retention period for Heroku](https://devcenter.heroku.com/articles/logging#log-history-limits). +### Homepage -On Windows, you can opt out of telemetry by disabling it in the Registry: +![main page](./screenshots/main_page.png) -``` -reg add "HKCU\Software\Raspberry Pi\Imager" /v telemetry /t REG_DWORD /d 0 -``` +### OS Selection -On Linux, run `rpi-imager --disable-telemetry` or add the following to `~/.config/Raspberry Pi/Imager.conf`: +![os selection](./screenshots/os_selection.png) -```ini -[General] -telemetry=false -``` +### Drive Selection -On macOS, disable it by editing the property list for the application: +#### Blacklisted -``` -defaults write org.raspberrypi.Imager.plist telemetry -bool NO -``` +![blacklisted](./screenshots/blacklisted_drive.png) + +#### Good Drive + +![good](./screenshots/good_drive.png) + +### Options Selection + +![options](./screenshots/options_selection.png) + +### Writing in Progress -### OS Customization +![writing progress](./screenshots/writing_in_progress.png) -When using the app, press CTRL + SHIFT + X to reveal the **OS Customization** dialog. +### Success -In here, you can specify several things you would otherwise set in the boot configuration files. For example, you can enable SSH, set the Wi-Fi login, and specify your locale settings for the system image. +![success message](./screenshots/success.png) diff --git a/debian/changelog b/debian/changelog index c31bbe907..9f17b8895 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,250 +1,5 @@ -rpi-imager (1.8.5) unstable; urgency=medium - - * Retry downloads on HTTP2 errors. - * Rename .desktop file to match DBus name, as some GNOME - desktop environments expect it that way. - * Disable Link Time Optimization as precaution against hard to - trace bugs. - - -- Floris Bos Sat, 20 Jan 2024 16:36:11 +0100 - -rpi-imager (1.8.4) unstable; urgency=medium - - * OS customisation: Fixed #531, which caused password entry - to drop the first character - * OS customisation: Impose character limits on usernames & - hostnames - * i18n: Added Polish translation - * i18n: Updated Catalan, Japanese translations - * cli: Fixed local file passing via --repo - - -- Floris Bos Fri, 22 Dec 2023 16:00:00 +0200 - -rpi-imager (1.8.3) unstable; urgency=medium - - * Home: Fixed "No filtering" option - * OS customisation: Fixed no-customisation flow - - -- Floris Bos Thu, 16 Nov 2023 14:00:00 +0200 - -rpi-imager (1.8.2) unstable; urgency=medium - - * Home: Device filtering reworked to download OS list eagerly - * OS List: Fixed 'Recommended' tag annotation - * i18n: Added zh-TW Traditional Chinese translation - * i18n: Updated Korean, German, Ukranian, translations - * build: Linux: Fix libdrm dependency requirement on non-embedded - build. - * Commmon: Disable QML caching entirely - * OS customisation: Allow use if only cloudinit payload available - * OS customisation: Allow empty PSK for 'Open' WiFi networks. - * OS List: Use ImageList v4 URL - - -- Floris Bos Wed, 15 Nov 2023 20:16:16 +0200 - -rpi-imager (1.8.1) unstable; urgency=medium - - * Hide empty categories when filtering - * i8n: Updates German translation - - -- Floris Bos Thu, 19 Oct 2023 20:16:16 +0200 - -rpi-imager (1.8.0) unstable; urgency=medium - - * Advanced options: Rename to OS customization - * OS customization: change to tabbed UI - * OS customization: make SSH public key field a textarea - * OS customization: add convenience button to run ssh-keygen - * OS customization: Enforce choice as part of write flow - * OS customization: Fixes for state consistency across UI - * Home: Add mechanism for selecting Raspberry Pi device - * Shared: Multiple keyboard focus fixes - * i18n: Adds Spanish/Ukrainian translations - * i18n: Updates French, Catalan, Italian, German, Spanish, Russian, - Japanese translations - * i18n: Use en_US as refernce string, and make en_GB a translation - * Workaround for ArchLinux's lsblk labeling internal SD card readers - (mmcblk0) as non-removable storage. - * Allow drag-dropping image files to Imager. - * Local .xz files: parse uncompressed size for better progress reports. - - -- Floris Bos Wed, 18 Oct 2023 18:00:00 +0200 - -rpi-imager (1.7.5) unstable; urgency=medium - - * Bump version number to match bug fix release for Windows. - (No actual changes from 1.7.4.1 for Linux) - - -- Floris Bos Mon, 22 May 2023 18:53:22 +0200 - -rpi-imager (1.7.4.1) unstable; urgency=medium - - * Advanced settings: fix creating files on FAT partition that are - an exact multiple of cluster size. - * Do you want to apply saved settings window: offer 'no' option, - without clearing settings. - * Eject storage properly on Linux. - - -- Floris Bos Thu, 18 May 2023 15:05:28 +0200 - -rpi-imager (1.7.4) unstable; urgency=medium - - * Advanced settings: fix escaping single quotes - * Advanced settings: default to using username of logged-in user - * Now uses a different method to edit files on the FAT partition - to apply advanced settings. Imager now understands the FAT16/FAT32 - file system format and can edit files by itself using the raw - disk device, without having to rely on the operating system - to mount the partition first. - - -- Floris Bos Sun, 20 Nov 2022 17:30:20 +0100 - -rpi-imager (1.7.3) unstable; urgency=medium - - * Linux: use GnuTLS instead of OpenSSL for computing SHA256 - * Fix persistenting public key authentication setting - * Linux: prefill wlan PSK if Linux distro uses NetworkManager - * Add digital signage icon - * Fix ""Cannot send events to objects owned by a different thread" warning - * Update Slovan/Korean language files - * Allow selecting file names without extension - * Add possibility to outsource handling of custom settings to script at - /usr/lib/raspberrypi-sys-mods/imager_custom - * Advanced settings: disallow invalid characters in hostname field - - -- Floris Bos Thu, 18 Aug 2022 20:04:37 +0200 - -rpi-imager (1.7.2) unstable; urgency=medium - - * Remove overscan/piwiz supression advanced options - * gz/xz/zstd custom images: pad if image size is not dividable by 512 byte - * Store saved wifi password hashed - * Make buttons blue on keyboard navigation - * Add Japan, Korean translations - - -- Floris Bos Thu, 24 Mar 2022 17:58:52 +0100 - -rpi-imager (1.7.1) unstable; urgency=medium - - * Fix advanced settings being broken if there are saved wifi - settings and wifiSSIDHidden is not present - - -- Floris Bos Thu, 03 Feb 2022 18:38:15 +0100 - -rpi-imager (1.7.0) unstable; urgency=medium - - * Only apply advanced settings if the operating system indicates it - supports it by "init_format": "cloudinit" | "systemd" in the - repository. Some heuristics are used with custom images from disk. - * Advanced settings: add support for cloudinit format - * Advanced settings: add support for specifying username - * Advanced settings: allow setting username and password - * Advanced settings: allow hidden wifi SSID - * Advanced settings: allow multi-line authorized_keys - * Retry on GnuTLS Recv errors - * Some fixes to deal better with Linux distributions auto-mounting - drives - * Add Slovenija translation - * Adds support for zstd - * Allow nested subitems entries - * Add word-wrapping to OS list (contributed by mzanetti) - * Update icons - * Telemetry: phone back home when image from repository is written: - - name of image written, parent category. - - about the computer running Imager: OS, version, architecture, - locale, Imager version, Pi revision. - - -- Floris Bos Wed, 02 Feb 2022 19:47:17 +0100 - -rpi-imager (1.6.2) unstable; urgency=medium - - * Add website link support - * Add CLI support - - -- Floris Bos Fri, 07 May 2021 13:19:19 +0200 - -rpi-imager (1.6.1) unstable; urgency=medium - - * When saving advanced options delay modifying files - on FAT partition until config.txt is visible. - * Update translations - * Linux: revert disable high dpi scaling on incorrect EDID" - due to it causing the custom file selection dialog not to - function properly on Fedora - - -- Floris Bos Sat, 27 Mar 2021 11:06:56 +0100 - -rpi-imager (1.6) unstable; urgency=medium - - * Wayland: fix "Client tried to set invalid geometry" error - * Add advanced users option screen available under SHIFT-CTRL-X - * Disable high DPI scaling on incorrect EDID - * Fix handling of .iso files - * Update translations - * GUI: change "SD card" -> "Storage" - * Allow NVMe drives as destination drive (Linux only) - - -- Floris Bos Fri, 26 Feb 2021 14:32:21 +0100 - -rpi-imager (1.5) unstable; urgency=medium - - * More verbose progress/error reporting - * Reconnect if download stalls for more than one minute - * Remember last selected custom image path - * Add German/French/Italian/Slovak/Turkish translations - * Fix multi-level subitems_url - * Add update notification support - * Allow translators to specify external .qm file for testing - * Remove dependency on qml-module-qt-labs-settings - * Enables telemetry collecting information about which images from - repository are most popular - - -- Floris Bos Tue, 24 Nov 2020 10:38:21 +0100 - -rpi-imager (1.4) unstable; urgency=medium - - * Add basic accessibility support - * Add keyboard navigation support - * Linux: do not let progress exceed 100% if - device reports incorrect write counters - * Show new SD card size if SD card is replaced - * Linux: do not use direct IO on verifying but purge cache with - posix_fadvise() - - -- Floris Bos Tue, 30 Jun 2020 00:29:37 +0200 - -rpi-imager (1.3) unstable; urgency=medium - - * Remove zero sized drives from list - * Fix height of drive list - * Show scrollbars permanently - * Add localisation support - * Use accelerated sha256 code provided by OpenSSL - * Custom user agent - * Ask for confirmation on quit - * Use direct IO on verifying - * Improve progress indication - - -- Floris Bos Mon, 25 May 2020 00:45:50 +0200 - -rpi-imager (1.2) unstable; urgency=medium - - * Mention version number in title bar. - * Performance improvements - - -- Floris Bos Tue, 10 Mar 2020 17:08:11 +0100 - -rpi-imager (1.1) unstable; urgency=medium - - * Fix verification on images that are uncompressed or have - a size that is not dividable by 128 KB. - * Round instead of floor size of SD card up and show 1 decimal - * Executable name changed from imagingutility to rpi-imager - - -- Floris Bos Mon, 09 Mar 2020 12:25:24 +0100 - -rpi-imager (1.0) unstable; urgency=medium +unraid-usb-creator (1.0) unstable; urgency=medium * Initial Release. - -- Floris Bos Thu, 20 Feb 2020 11:54:04 +0100 + -- Unraid Team Thu, 20 Feb 2020 11:54:04 +0100 diff --git a/debian/control b/debian/control index 79de610a6..854feb19b 100644 --- a/debian/control +++ b/debian/control @@ -1,4 +1,4 @@ -Source: rpi-imager +Source: unraid-usb-creator Section: admin Priority: optional Maintainer: Floris Bos @@ -6,13 +6,13 @@ Build-Depends: debhelper (>= 10), cmake, libarchive-dev, liblzma-dev, libcurl4-g qtbase5-dev, qtbase5-dev-tools, qtdeclarative5-dev, libqt5svg5-dev, qttools5-dev, libgnutls28-dev, qml-module-qtquick2, qml-module-qtquick-controls2, qml-module-qtquick-layouts, qml-module-qtquick-templates2, qml-module-qtquick-window2, qml-module-qtgraphicaleffects Standards-Version: 4.1.2 -Homepage: https://www.raspberrypi.org/ +Homepage: https://github.com/unraid/usb-creator-next -Package: rpi-imager +Package: unraid-usb-creator Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, qml-module-qtquick2, qml-module-qtquick-controls2, qml-module-qtquick-layouts, qml-module-qtquick-templates2, qml-module-qtquick-window2, qml-module-qtgraphicaleffects, dosfstools, fdisk Recommends: udisks2 -Description: Raspberry Pi imaging utility +Description: Unraid imaging utility Graphical user-interface to write disk images and format SD cards. diff --git a/debian/copyright b/debian/copyright index 2d16a3e97..2d4ca513f 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,5 +1,5 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: rpi-imager +Upstream-Name: unraid-usb-creator Source: Files: * diff --git a/debian/rpi-imager.manpages b/debian/rpi-imager.manpages deleted file mode 100644 index 8b912b866..000000000 --- a/debian/rpi-imager.manpages +++ /dev/null @@ -1 +0,0 @@ -doc/man/rpi-imager.1 diff --git a/debian/unraid-usb-creator.manpages b/debian/unraid-usb-creator.manpages new file mode 100644 index 000000000..73f24ec10 --- /dev/null +++ b/debian/unraid-usb-creator.manpages @@ -0,0 +1 @@ +doc/man/unraid-usb-creator.1 diff --git a/debian/unraid-usb-creator/DEBIAN/control b/debian/unraid-usb-creator/DEBIAN/control new file mode 100644 index 000000000..0c3325ab6 --- /dev/null +++ b/debian/unraid-usb-creator/DEBIAN/control @@ -0,0 +1,12 @@ +Package: unraid-usb-creator +Version: 1.0 +Architecture: amd64 +Maintainer: Unraid Developers contact@unraid.net +Installed-Size: 2970 +Depends: libarchive13 (>= 3.0.4), libc6 (>= 2.34), libcurl3-gnutls (>= 7.16.2), libgcc-s1 (>= 3.3.1), libgnutls30 (>= 3.7.0), liblzma5 (>= 5.1.1alpha+20110809), libqt5core5a (>= 5.15.1), libqt5dbus5 (>= 5.14.1), libqt5gui5 (>= 5.10.0) | libqt5gui5-gles (>= 5.10.0), libqt5network5 (>= 5.12.2), libqt5qml5 (>= 5.10.0), libqt5widgets5 (>= 5.0.2), libstdc++6 (>= 12), qml-module-qtquick2, qml-module-qtquick-controls2, qml-module-qtquick-layouts, qml-module-qtquick-templates2, qml-module-qtquick-window2, qml-module-qtgraphicaleffects, dosfstools, fdisk +Recommends: udisks2 +Section: admin +Priority: optional +Homepage: https://github.com/unraid/usb-creator-next +Description: Unraid imaging utility + Graphical user-interface to write disk images and format SD cards. diff --git a/debian/unraid-usb-creator/DEBIAN/md5sums b/debian/unraid-usb-creator/DEBIAN/md5sums new file mode 100644 index 000000000..2b7b44137 --- /dev/null +++ b/debian/unraid-usb-creator/DEBIAN/md5sums @@ -0,0 +1,7 @@ +d2af054930493926471278d11433cf84 usr/bin/unraid-usb-creator +56dbceb5198ff7545c315f9a812e3484 usr/share/applications/com.limetech.unraid-usb-creator.desktop +cf8cee94e0a222f252e30701d87be77b usr/share/doc/unraid-usb-creator/changelog.gz +9cc7fd7ca7041b8d8b8335041ad0fe75 usr/share/doc/unraid-usb-creator/copyright +123e8a8371979ae319bfce1e351066d4 usr/share/icons/hicolor/128x128/apps/unraid128.png +3aabc017165ad2720ab6bef324d5e901 usr/share/man/man1/unraid-usb-creator.1.gz +1c114514f76627c98334c800569951c2 usr/share/metainfo/unraid-usb-creator.metainfo.xml diff --git a/debian/unraid-usb-creator/usr/bin/unraid-usb-creator b/debian/unraid-usb-creator/usr/bin/unraid-usb-creator new file mode 100755 index 000000000..ddcee8987 Binary files /dev/null and b/debian/unraid-usb-creator/usr/bin/unraid-usb-creator differ diff --git a/debian/unraid-usb-creator/usr/share/applications/com.limetech.unraid-usb-creator.desktop b/debian/unraid-usb-creator/usr/share/applications/com.limetech.unraid-usb-creator.desktop new file mode 100644 index 000000000..801891487 --- /dev/null +++ b/debian/unraid-usb-creator/usr/share/applications/com.limetech.unraid-usb-creator.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=Unraid USB Creator +Comment=Unraid USB Creator +Icon=unraid128 +Exec=unraid-usb-creator %F +Categories=Utility +StartupNotify=false diff --git a/debian/unraid-usb-creator/usr/share/doc/unraid-usb-creator/changelog.gz b/debian/unraid-usb-creator/usr/share/doc/unraid-usb-creator/changelog.gz new file mode 100644 index 000000000..9a33fdad0 Binary files /dev/null and b/debian/unraid-usb-creator/usr/share/doc/unraid-usb-creator/changelog.gz differ diff --git a/debian/unraid-usb-creator/usr/share/doc/unraid-usb-creator/copyright b/debian/unraid-usb-creator/usr/share/doc/unraid-usb-creator/copyright new file mode 100644 index 000000000..2d4ca513f --- /dev/null +++ b/debian/unraid-usb-creator/usr/share/doc/unraid-usb-creator/copyright @@ -0,0 +1,39 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: unraid-usb-creator +Source: + +Files: * +Copyright: 2020 Raspberry Pi Ltd +License: Apache-2.0 + +Files: src/dependencies/mountutils/* src/dependencies/drivelist/* +Copyright: 2017 Balena.io +License: Apache-2.0 + +Files: src/dependencies/sha256crypt/* +Copyright: Released into the Public Domain by Ulrich Drepper +License: public-domain + +Files-Excluded: src/dependencies/cmcurl/* src/dependencies/cmliblzma/* src/dependencies/fat32format/* src/dependencies/libarchive-3.4.1/* src/dependencies/zlib-1.2.11/* +Comment: Not used in Linux build (depending on the official packages instead) + +License: Apache-2.0 + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + . + https://www.apache.org/licenses/LICENSE-2.0 + . + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + . + On Debian systems, the complete text of the Apache version 2.0 license + can be found in "/usr/share/common-licenses/Apache-2.0". + +License: public-domain + SHA256-based Unix crypt implementation. + Released into the Public Domain by Ulrich Drepper + diff --git a/debian/unraid-usb-creator/usr/share/icons/hicolor/128x128/apps/unraid128.png b/debian/unraid-usb-creator/usr/share/icons/hicolor/128x128/apps/unraid128.png new file mode 100644 index 000000000..ad920ba9f Binary files /dev/null and b/debian/unraid-usb-creator/usr/share/icons/hicolor/128x128/apps/unraid128.png differ diff --git a/debian/unraid-usb-creator/usr/share/man/man1/unraid-usb-creator.1.gz b/debian/unraid-usb-creator/usr/share/man/man1/unraid-usb-creator.1.gz new file mode 100644 index 000000000..1b4c51069 Binary files /dev/null and b/debian/unraid-usb-creator/usr/share/man/man1/unraid-usb-creator.1.gz differ diff --git a/debian/unraid-usb-creator/usr/share/metainfo/unraid-usb-creator.metainfo.xml b/debian/unraid-usb-creator/usr/share/metainfo/unraid-usb-creator.metainfo.xml new file mode 100644 index 000000000..19bb806e6 --- /dev/null +++ b/debian/unraid-usb-creator/usr/share/metainfo/unraid-usb-creator.metainfo.xml @@ -0,0 +1,44 @@ + + + com.limetech.unraid-usb-creator + CC0-1.0 + Apache-2.0 + Unraid USB Creator + Unraid USB Creator + +

+ Unraid USB Creator downloads a .JSON file from the Unraid OS + endpoint with a list of all current download options, ensuring you are + always installing the most up-to-date version. +

+

+ Once you’ve selected an operating system from the available options, + the utility reads the relevant file directly from the Unraid + website and writes it straight to the USB. The utility will then run + an OS-specific script to make the USB bootable using syslinux. +

+

+ During this process, Unraid USB Creator also caches the downloaded + operating system image – that is to say, it saves a local copy on your + computer, so you can create additional USBs without having to + download the file again. +

+
+ com.limetech.unraid-usb-creator.desktop + + + https://github.com/unraid/usb-creator-next/assets/47037553/c4fbe528-3505-40ba-87b4-727b6b333cf3 + Main window + + + https://github.com/unraid/usb-creator-next + + unraid-usb-creator + + + + + + moderate + +
diff --git a/doc/man/rpi-imager.1 b/doc/man/unraid-usb-creator.1 similarity index 88% rename from doc/man/rpi-imager.1 rename to doc/man/unraid-usb-creator.1 index 077e9b52e..ddb111eea 100644 --- a/doc/man/rpi-imager.1 +++ b/doc/man/unraid-usb-creator.1 @@ -1,11 +1,11 @@ -.TH RPI\-IMAGER 1 +.TH UNRAID\-USB\-CREATOR 1 . .SH NAME -rpi\-imager \- Flash disk images to removable storage +unraid\-usb\-creator \- Flash disk images to removable storage . . .SH SYNOPSIS -.SY rpi\-imager +.SY unraid\-usb\-creator .OP \-\-debug .OP \-\-repo url .OP \-\-qm translations @@ -13,7 +13,7 @@ rpi\-imager \- Flash disk images to removable storage .OP image-uri .YS . -.SY rpi\-imager +.SY unraid\-usb\-creator \-\-cli .OP \-\-debug .OP \-\-quiet @@ -23,17 +23,17 @@ image-uri destination-device .YS . -.SY rpi\-imager +.SY unraid\-usb\-creator \-\-version .YS . -.SY rpi\-imager +.SY unraid\-usb\-creator \-\-help .YS . . .SH DESCRIPTION -.B rpi\-imager +.B unraid\-usb\-creator is a utility for writing a disk image file, which may optionally be compressed, to a removable destination disk, such as a micro-SD card or a USB-attached SSD drive. @@ -76,8 +76,7 @@ Output extra debugging information on the console. . .TP .B \-\-disable\-telemetry -Do not report OS writes to -.I http://rpi-imager-stats.raspberrypi.com/ +Do not report OS writes . .TP .B \-\-disable\-verify @@ -137,15 +136,15 @@ Only valid when run with . .SH EXAMPLES .TP -.B rpi\-imager +.B unraid\-usb\-creator Launch the graphical interface. . .TP -.B rpi\-imager \-\-repo http://localhost:8080/os_list_utility.json +.B unraid\-usb\-creator \-\-repo http://localhost:8080/os_list_utility.json Test a locally hosted version of the OS list with the graphical interface. . .TP -.B rpi\-imager \-\-cli \-\-disable\-verify test.img /dev/mmcblk0 +.B unraid\-usb\-creator \-\-cli \-\-disable\-verify test.img /dev/mmcblk0 Write .I test.img to diff --git a/flatpak/com.limetech.unraid-usb-creator.yml b/flatpak/com.limetech.unraid-usb-creator.yml new file mode 100644 index 000000000..4d80ea003 --- /dev/null +++ b/flatpak/com.limetech.unraid-usb-creator.yml @@ -0,0 +1,27 @@ +app-id: com.limetech.unraid-usb-creator +runtime: org.kde.Platform +runtime-version: '5.15-22.08' +sdk: org.kde.Sdk +command: unraid-usb-creator + +#Flapak is sandboxed. Therefore, needs very specific permissions to access os +# https://docs.flatpak.org/en/latest/sandbox-permissions.html +finish-args: + - --share=ipc + - --socket=x11 + - --socket=wayland + - --share=network + - --device=dri + +modules: + - name: UnraidUsbCreator + buildsystem: cmake + + config-opts: + - -DCMAKE_BUILD_TYPE=Release + sources: + - type: dir + path: "../src" + + + # also have to look at the config-options/finish-args and how that works and if it'll do what we want. \ No newline at end of file diff --git a/license.txt b/license.txt index 5ad674f87..e7d35af9b 100644 --- a/license.txt +++ b/license.txt @@ -1,8 +1,8 @@ == -Raspberry Pi Imaging Utility main code license terms +Unraid USB Creator (next) main code license terms == -Copyright (C) 2020 Raspberry Pi Ltd +Copyright (C) 2024 Lime Technology, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ This software depends on the following third-party components covered by open so - libcurl (libcurl license) - fat32format.exe (GPL license) - zstd (BSD license) +- Raspberry Pi Imager (Apache License) fat32format is only used on the Windows platform (to get around 32 GB limit of the official Windows format command) @@ -1786,3 +1787,21 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. == + +== +Raspberry Pi Imager License +== + +Copyright (C) 2020 Raspberry Pi Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/screenshots/blacklisted_drive.png b/screenshots/blacklisted_drive.png new file mode 100644 index 000000000..b58203f4a Binary files /dev/null and b/screenshots/blacklisted_drive.png differ diff --git a/screenshots/good_drive.png b/screenshots/good_drive.png new file mode 100644 index 000000000..a6db18255 Binary files /dev/null and b/screenshots/good_drive.png differ diff --git a/screenshots/main_page.png b/screenshots/main_page.png new file mode 100644 index 000000000..4df849ade Binary files /dev/null and b/screenshots/main_page.png differ diff --git a/screenshots/options_selection.png b/screenshots/options_selection.png new file mode 100644 index 000000000..16b2371b6 Binary files /dev/null and b/screenshots/options_selection.png differ diff --git a/screenshots/os_selection.png b/screenshots/os_selection.png new file mode 100644 index 000000000..c10ab6a16 Binary files /dev/null and b/screenshots/os_selection.png differ diff --git a/screenshots/success.png b/screenshots/success.png new file mode 100644 index 000000000..11ab84b84 Binary files /dev/null and b/screenshots/success.png differ diff --git a/screenshots/writing_in_progress.png b/screenshots/writing_in_progress.png new file mode 100644 index 000000000..61983140d Binary files /dev/null and b/screenshots/writing_in_progress.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9b8e28d36..c2e21c1dc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -3,14 +3,15 @@ cmake_minimum_required(VERSION 3.9.4) OPTION (ENABLE_CHECK_VERSION "Check for version updates" ON) -OPTION (ENABLE_TELEMETRY "Enable sending telemetry" ON) +OPTION (ENABLE_TELEMETRY "Enable sending telemetry" OFF) OPTION (DRIVELIST_FILTER_SYSTEM_DRIVES "Filter System drives from displayed drives" ON) -project(rpi-imager LANGUAGES CXX C) +project(unraid-usb-creator LANGUAGES CXX C) set(IMAGER_VERSION_MAJOR 1) -set(IMAGER_VERSION_MINOR 8) -set(IMAGER_VERSION_STR "${IMAGER_VERSION_MAJOR}.${IMAGER_VERSION_MINOR}.5") -set(IMAGER_VERSION_CSV "${IMAGER_VERSION_MAJOR},${IMAGER_VERSION_MINOR},5,0") +set(IMAGER_VERSION_MINOR 1) +set(IMAGER_VERSION_PATCH 0) +set(IMAGER_VERSION_STR "${IMAGER_VERSION_MAJOR}.${IMAGER_VERSION_MINOR}.${IMAGER_VERSION_PATCH}") +set(IMAGER_VERSION_CSV "${IMAGER_VERSION_MAJOR},${IMAGER_VERSION_MINOR},${IMAGER_VERSION_PATCH},0") add_definitions(-DIMAGER_VERSION_STR="${IMAGER_VERSION_STR}") add_definitions(-DIMAGER_VERSION_CSV=${IMAGER_VERSION_CSV}) set(CMAKE_INCLUDE_CURRENT_DIR ON) @@ -24,10 +25,10 @@ set(HEADERS config.h imagewriter.h networkaccessmanagerfactory.h nan.h drivelist # Add dependencies if (APPLE) - set_source_files_properties("icons/rpi-imager.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") + set_source_files_properties("icons/unraid.icns" PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") set(DEPENDENCIES acceleratedcryptographichash.cpp mac/macfile.cpp mac/macfile.h dependencies/mountutils/src/darwin/functions.cpp mac/macwlancredentials.h mac/macwlancredentials.cpp - dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/rpi-imager.icns) + dependencies/drivelist/src/darwin/list.mm dependencies/drivelist/src/darwin/REDiskList.m icons/unraid.icns) enable_language(OBJC C) elseif (UNIX) set(DEPENDENCIES dependencies/mountutils/src/linux/functions.cpp linux/linuxdrivelist.cpp linux/networkmanagerapi.h linux/networkmanagerapi.cpp linux/stpanalyzer.h linux/stpanalyzer.cpp) @@ -50,8 +51,8 @@ elseif (UNIX) endif() elseif (WIN32) set(DEPENDENCIES acceleratedcryptographichash.cpp dependencies/mountutils/src/windows/functions.cpp dependencies/drivelist/src/windows/list.cpp - windows/winfile.cpp windows/winfile.h windows/winwlancredentials.h windows/winwlancredentials.cpp - windows/rpi-imager.rc wlanapi_delayed.lib) + windows/winfile.cpp windows/winfile.h #windows/winwlancredentials.h windows/winwlancredentials.cpp + windows/unraid-usb-creator.rc wlanapi_delayed.lib) set(EXTRALIBS setupapi ${CMAKE_CURRENT_BINARY_DIR}/wlanapi_delayed.lib) add_custom_command( OUTPUT wlanapi_delayed.lib @@ -92,7 +93,9 @@ endif( IS_BIG_ENDIAN ) set(SOURCES "main.cpp" "imagewriter.cpp" "networkaccessmanagerfactory.cpp" "drivelistitem.cpp" "drivelistmodel.cpp" "drivelistmodelpollthread.cpp" "downloadthread.cpp" "downloadextractthread.cpp" "devicewrapper.cpp" "devicewrapperblockcacheentry.cpp" "devicewrapperpartition.cpp" "devicewrapperfatpartition.cpp" - "driveformatthread.cpp" "localfileextractthread.cpp" "powersaveblocker.cpp" "downloadstatstelemetry.cpp" "qml.qrc" "dependencies/sha256crypt/sha256crypt.c" "cli.cpp") + "driveformatthread.cpp" "localfileextractthread.cpp" "powersaveblocker.cpp" "downloadstatstelemetry.cpp" "qml.qrc" "dependencies/sha256crypt/sha256crypt.c" "cli.cpp" + "unraidguidvalidator.h" "unraidguidvalidator.cpp" +) find_package(Qt5 5.14 QUIET COMPONENTS Core Quick LinguistTools Svg OPTIONAL_COMPONENTS Widgets DBus WinExtras) if (Qt5_FOUND) @@ -129,11 +132,7 @@ if(${QT}WinExtras_FOUND) set(EXTRALIBS ${EXTRALIBS} ${QT}::WinExtras) endif() -set(TRANSLATIONS i18n/rpi-imager_en.ts i18n/rpi-imager_nl.ts i18n/rpi-imager_zh.ts i18n/rpi-imager_tr.ts - i18n/rpi-imager_fr.ts i18n/rpi-imager_de.ts i18n/rpi-imager_sk.ts i18n/rpi-imager_it.ts - i18n/rpi-imager_ca.ts i18n/rpi-imager_sl.ts i18n/rpi-imager_ko.ts i18n/rpi-imager_ja.ts - i18n/rpi-imager_ru.ts i18n/rpi-imager_es.ts i18n/rpi-imager_uk.ts i18n/rpi-imager_zh-TW.ts - i18n/rpi-imager_pl.ts) +set(TRANSLATIONS i18n/unraid-usb-creator_en.ts i18n/unraid-usb-creator_es.ts i18n/unraid-usb-creator_de.ts i18n/unraid-usb-creator_fr.ts i18n/unraid-usb-creator_zh.ts) #qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TRANSLATIONS}) if (Qt5_FOUND) qt5_add_translation(QM_FILES ${TRANSLATIONS}) @@ -179,16 +178,9 @@ if (WIN32) find_package(OpenSSL REQUIRED) - # Bundled zlib - add_subdirectory(dependencies/zlib-1.2.13) - set(ZLIB_LIBRARY zlibstatic) - set(ZLIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zlib-1.2.13 CACHE PATH "zlib include dir") + find_package(ZLIB REQUIRED) - # Bundled libcurl - set(CMAKE_CURL_INCLUDES) - set(CURL_LIBRARIES cmcurl) - add_subdirectory(dependencies/cmcurl) - set(CURL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/cmcurl/include) + find_package(CURL REQUIRED) # Bundled liblzma add_subdirectory(dependencies/cmliblzma) @@ -198,12 +190,7 @@ if (WIN32) set(LIBLZMA_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/cmliblzma/liblzma/api) set(LIBLZMA_LIBRARY cmliblzma) - # Bundled zstd - set(ZSTD_BUILD_PROGRAMS OFF) - set(ZSTD_BUILD_SHARED OFF) - add_subdirectory(dependencies/zstd-1.5.4/build/cmake) - set(ZSTD_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/dependencies/zstd-1.5.4/lib CACHE PATH "zstd include dir") - set(ZSTD_LIBRARY libzstd_static) + find_package(zstd REQUIRED) # Bundled libarchive set(ENABLE_TEST OFF CACHE BOOL "") @@ -225,52 +212,28 @@ if (WIN32) POST_BUILD COMMAND ${CMAKE_STRIP} "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe") - # Code signing - find_program(SIGNTOOL "signtool.exe" PATHS - "c:/Program Files (x86)/Microsoft SDKs/ClickOnce/SignTool" - "c:/Program Files (x86)/Windows Kits/10/bin/10.0.22621.0/x64") - if (NOT SIGNTOOL) - message(FATAL_ERROR "Unable to locate signtool.exe used for code signing") - endif() - add_definitions(-DSIGNTOOL="${SIGNTOOL}") - - add_custom_command(TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND "${SIGNTOOL}" sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe") - - add_custom_command(TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND "${SIGNTOOL}" sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a "${CMAKE_BINARY_DIR}/dependencies/fat32format/fat32format.exe") - # Windeploy - find_program(WINDEPLOYQT "windeployqt.exe" PATHS "${${QT}_DIR}/../../../bin") - if (NOT WINDEPLOYQT) - message(FATAL_ERROR "Unable to locate windeployqt.exe") + find_program(MXEDEPLOYQT "mxedeployqt") + if (NOT MXEDEPLOYQT) + message(FATAL_ERROR "Unable to locate mxedeployqt") endif() - + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/deploy") add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.exe" "${CMAKE_BINARY_DIR}/dependencies/fat32format/fat32format.exe" - "${CMAKE_SOURCE_DIR}/../license.txt" "${CMAKE_SOURCE_DIR}/windows/rpi-imager-cli.cmd" - "${CMAKE_BINARY_DIR}/deploy") - - add_custom_command(TARGET ${PROJECT_NAME} - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy - "${${QT}_DIR}/../../../bin/libssl-1_1.dll" "${${QT}_DIR}/../../../bin/libcrypto-1_1.dll" + "${CMAKE_SOURCE_DIR}/../license.txt" "${CMAKE_SOURCE_DIR}/windows/unraid-usb-creator-cli.cmd" "${CMAKE_BINARY_DIR}/deploy") configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/windows/rpi-imager.nsi.in" - "${CMAKE_CURRENT_BINARY_DIR}/rpi-imager.nsi" + "${CMAKE_CURRENT_SOURCE_DIR}/windows/unraid-usb-creator.nsi.in" + "${CMAKE_CURRENT_BINARY_DIR}/unraid-usb-creator.nsi" @ONLY) - add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD - COMMAND "${WINDEPLOYQT}" --no-translations --no-webkit2 --no-opengl-sw --angle --qmldir "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/deploy/rpi-imager.exe") + COMMAND "${MXEDEPLOYQT}" --qmlrootpath "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_BINARY_DIR}/deploy") # Remove excess files add_custom_command(TARGET ${PROJECT_NAME} @@ -344,14 +307,14 @@ else() endif() configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/linux/rpi-imager.metainfo.xml.in" - "${CMAKE_CURRENT_BINARY_DIR}/rpi-imager.metainfo.xml" + "${CMAKE_CURRENT_SOURCE_DIR}/linux/unraid-usb-creator.metainfo.xml.in" + "${CMAKE_CURRENT_BINARY_DIR}/unraid-usb-creator.metainfo.xml" @ONLY) - install(TARGETS rpi-imager DESTINATION bin) - install(FILES icons/rpi-imager.png DESTINATION share/icons/hicolor/128x128/apps) - install(FILES linux/org.raspberrypi.rpi-imager.desktop DESTINATION share/applications) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/rpi-imager.metainfo.xml" DESTINATION share/metainfo) + install(TARGETS unraid-usb-creator DESTINATION bin) + install(FILES icons/unraid128.png DESTINATION share/icons/hicolor/128x128/apps) + install(FILES linux/com.limetech.unraid-usb-creator.desktop DESTINATION share/applications) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/unraid-usb-creator.metainfo.xml" DESTINATION share/metainfo) endif() include_directories(${CURL_INCLUDE_DIR} ${LibArchive_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} ${LIBLZMA_INCLUDE_DIR} ${LIBDRM_INCLUDE_DIRS}) diff --git a/src/MsgPopup.qml b/src/MsgPopup.qml index b754f97ea..f09286a4d 100644 --- a/src/MsgPopup.qml +++ b/src/MsgPopup.qml @@ -20,16 +20,24 @@ Popup { property alias title: msgpopupheader.text property alias text: msgpopupbody.text + property alias body: msgpopupbody property bool continueButton: true property bool quitButton: false property bool yesButton: false property bool noButton: false + property bool installGuideButton: false signal yes() signal no() + signal installGuide() + + background: Rectangle { + color: UnColors.darkGray + border.color: UnColors.mediumGray + } // background of title Rectangle { - color: "#f5f5f5" + color: UnColors.mediumGray anchors.right: parent.right anchors.top: parent.top height: 35 @@ -37,7 +45,7 @@ Popup { } // line under title Rectangle { - color: "#afafaf" + color: UnColors.mediumGray width: parent.width y: 35 implicitHeight: 1 @@ -46,6 +54,7 @@ Popup { Text { id: msgx text: "X" + color: "white" anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 25 @@ -74,6 +83,7 @@ Popup { Layout.topMargin: 10 font.family: roboto.name font.bold: true + color: "white" } Text { @@ -87,6 +97,7 @@ Popup { Layout.leftMargin: 25 Layout.topMargin: 25 Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "") + color: "white" } RowLayout { @@ -94,7 +105,7 @@ Popup { Layout.bottomMargin: 10 spacing: 20 - ImButtonRed { + ImButton { text: qsTr("NO") onClicked: { msgpopup.close() @@ -103,7 +114,7 @@ Popup { visible: msgpopup.noButton } - ImButtonRed { + ImButton { text: qsTr("YES") onClicked: { msgpopup.close() @@ -112,7 +123,7 @@ Popup { visible: msgpopup.yesButton } - ImButtonRed { + ImButton { text: qsTr("CONTINUE") onClicked: { msgpopup.close() @@ -120,7 +131,7 @@ Popup { visible: msgpopup.continueButton } - ImButtonRed { + ImButton { text: qsTr("QUIT") onClicked: { Qt.quit() @@ -129,6 +140,15 @@ Popup { visible: msgpopup.quitButton } + ImButton { + text: qsTr("QUICK INSTALL GUIDE") + onClicked: { + msgpopup.installGuide() + } + font.family: roboto.name + visible: msgpopup.installGuideButton + } + Text { text: " " } } } diff --git a/src/OptionsPopup.qml b/src/OptionsPopup.qml index 3b2a995f2..91bab17cc 100644 --- a/src/OptionsPopup.qml +++ b/src/OptionsPopup.qml @@ -18,6 +18,12 @@ Window { minimumHeight: 125 height: Math.min(750, cl.implicitHeight) title: qsTr("OS Customization") + modality: Qt.WindowModal + + color: UnColors.darkGray + Material.theme: Material.Dark + Material.background: UnColors.darkGray + Material.accent: UnColors.orange property bool initialized: false property bool hasSavedSettings: false @@ -30,6 +36,7 @@ Window { property string cloudinitnetwork signal saveSettingsSignal(var settings) + signal continueSignal() ColumnLayout { id: cl @@ -57,7 +64,6 @@ Window { TabBar { id: bar Layout.fillWidth: true - TabButton { text: qsTr("General") onClicked: { @@ -103,7 +109,8 @@ Window { } Text { text : ".local" - color: chkHostname.checked ? "black" : "grey" + opacity: chkHostname.checked ? 1.0 : 0.3 + color: "white" } } @@ -132,7 +139,8 @@ Window { Text { text: qsTr("Username:") - color: parent.enabled ? (fieldUserName.indicateError ? "red" : "black") : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: fieldUserName.indicateError ? "red" : "white" } TextField { id: fieldUserName @@ -150,7 +158,8 @@ Window { Text { text: qsTr("Password:") - color: parent.enabled ? (fieldUserPassword.indicateError ? "red" : "black") : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: fieldUserPassword.indicateError ? "red" : "white" } TextField { id: fieldUserPassword @@ -205,7 +214,8 @@ Window { Text { text: qsTr("SSID:") - color: parent.enabled ? (fieldWifiSSID.indicateError ? "red" : "black") : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: fieldWifiSSID.indicateError ? "red" : "white" } TextField { id: fieldWifiSSID @@ -219,7 +229,8 @@ Window { Text { text: qsTr("Password:") - color: parent.enabled ? (fieldWifiPassword.indicateError ? "red" : "black") : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: fieldWifiPassword.indicateError ? "red" : "white" } TextField { id: fieldWifiPassword @@ -250,7 +261,8 @@ Window { Text { text: qsTr("Wireless LAN country:") - color: parent.enabled ? "black" : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: "white" } ComboBox { id: fieldWifiCountry @@ -271,7 +283,8 @@ Window { Text { text: qsTr("Time zone:") - color: parent.enabled ? "black" : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: "white" } ComboBox { id: fieldTimezone @@ -281,7 +294,8 @@ Window { Text { text: qsTr("Keyboard layout:") - color: parent.enabled ? "black" : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: "white" } ComboBox { id: fieldKeyboardLayout @@ -345,7 +359,8 @@ Window { Text { text: qsTr("Set authorized_keys for '%1':").arg(fieldUserName.text) - color: parent.enabled ? "black" : "grey" + opacity: parent.enabled ? 1.0 : 0.3 + color: "white" textFormat: Text.PlainText } TextArea { @@ -380,10 +395,12 @@ Window { id: chkEject text: qsTr("Eject media when finished") } + /* ImCheckBox { id: chkTelemtry text: qsTr("Enable telemetry") } + */ } } } @@ -395,7 +412,7 @@ Window { Layout.bottomMargin: 10 spacing: 20 - ImButtonRed { + ImButton { text: qsTr("SAVE") onClicked: { if (chkSetUser.checked && fieldUserPassword.text.length == 0) @@ -440,6 +457,7 @@ Window { applySettings() saveSettings() popup.close() + continueSignal() } } @@ -449,7 +467,7 @@ Window { function initialize() { chkBeep.checked = imageWriter.getBoolSetting("beep") - chkTelemtry.checked = imageWriter.getBoolSetting("telemetry") + // chkTelemtry.checked = imageWriter.getBoolSetting("telemetry") chkEject.checked = imageWriter.getBoolSetting("eject") var settings = imageWriter.getSavedCustomizationSettings() fieldTimezone.model = imageWriter.getTimezoneList() @@ -849,7 +867,7 @@ Window { imageWriter.setSetting("beep", chkBeep.checked) imageWriter.setSetting("eject", chkEject.checked) - imageWriter.setSetting("telemetry", chkTelemtry.checked) + imageWriter.setSetting("telemetry", false) // chkTelemtry.checked) if (chkHostname.checked || chkSetUser.checked || chkSSH.checked || chkWifi.checked || chkLocale.checked) { /* OS customization to be applied. */ diff --git a/src/UnraidOptionsPopup.qml b/src/UnraidOptionsPopup.qml new file mode 100644 index 000000000..f2ac49c2c --- /dev/null +++ b/src/UnraidOptionsPopup.qml @@ -0,0 +1,310 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright (C) 2020 Raspberry Pi Ltd + */ + +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Material 2.2 +import "qmlcomponents" + +Popup { + id: popup + x: 50 + y: 25 + width: parent.width-100 + height: parent.height-50 + padding: 0 + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + Material.theme: Material.Dark + Material.background: UnColors.darkGray + Material.accent: UnColors.orange + + property bool initialized: false + property bool hasSavedSettings: false + property string config + property string cmdline + property string firstrun + property string cloudinit + property string cloudinitrun + property string cloudinitwrite + property string cloudinitnetwork + property bool validInputs: false + + signal saveSettingsSignal(var settings) + signal continueSignal() + + background: Rectangle { + color: UnColors.darkGray + border.color: UnColors.mediumGray + } + + // background of title + Rectangle { + color: UnColors.mediumGray + anchors.right: parent.right + anchors.top: parent.top + height: 35 + width: parent.width + } + // line under title + Rectangle { + color: UnColors.mediumGray + width: parent.width + y: 35 + implicitHeight: 1 + } + + Text { + color: "white" + text: "X" + anchors.right: parent.right + anchors.top: parent.top + anchors.rightMargin: 25 + anchors.topMargin: 10 + font.family: roboto.name + font.bold: true + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + popup.close() + } + } + } + + ColumnLayout { + spacing: 10 + width: parent.width + Text { + color: "white" + text: qsTr("Settings") + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true + Layout.topMargin: 10 + font.family: roboto.name + font.bold: true + } + GroupBox { + + Layout.fillWidth: true + Layout.fillHeight: true + Layout.leftMargin: 25 + Layout.rightMargin: 25 + Layout.topMargin: 10 + ColumnLayout { + // General tab + spacing: -10 + + RowLayout { + Text { + text : "Server Name:" + color: !fieldServername.acceptableInput ? "red" : "white" + } + Loader { + // try to account for RegExpValidator renaming in Qt6 + source: "qmlcomponents/regex_validator_qt5.qml" + property bool valid: item !== null + id: regexValidatorQt5 + } + Loader { + // try to account for RegExpValidator renaming in Qt6 + source: "qmlcomponents/regex_validator_qt6.qml" + property bool valid: item !== null + id: regexValidatorQt6 + } + TextField { + id: fieldServername + text: "Tower" + horizontalAlignment: TextInput.AlignHCenter + selectByMouse: true + maximumLength: 15 + validator: regexValidatorQt5.valid ? regexValidatorQt5.item : regexValidatorQt6.item + } + } + + RowLayout { + Text { + text : "Network Mode:" + color: "white" + } + ImRadioButton { + id: radioDhcp + text: qsTr("DHCP") + checked: true + onCheckedChanged: { + } + } + ImRadioButton { + id: radioStaticIp + text: qsTr("Static IP") + onCheckedChanged: { + } + } + } + + RowLayout { + enabled: radioStaticIp.checked + IpTextField { + id: ipAddressField + label : "IP Address:" + } + Text { + text : "Netmask:" + color: "white" + opacity: parent.enabled ? 1.0 : 0.3 + } + ComboBox { + id: fieldNetmask + font.family: roboto.name + model: ["255.255.0.0", + "255.255.128.0", + "255.255.192.0", + "255.255.224.0", + "255.255.240.0", + "255.255.248.0", + "255.255.252.0", + "255.255.254.0", + "255.255.255.0", + "255.255.255.128", + "255.255.255.192", + "255.255.255.224", + "255.255.255.240", + "255.255.255.248", + "255.255.255.252"] + Layout.preferredWidth: 200 + currentIndex: -1 + Component.onCompleted: { + currentIndex = find("255.255.255.0") + } + Layout.topMargin: 10 + Layout.bottomMargin: 10 + popup: Popup { + y: fieldNetmask.height - 1 + width: fieldNetmask.width + implicitHeight: contentItem.implicitHeight + padding: 1 + Material.theme: Material.Dark + Material.accent: UnColors.orange + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: fieldNetmask.delegateModel + currentIndex: fieldNetmask.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: UnColors.darkGray + radius: 2 + } + } + } + } + RowLayout { + enabled: radioStaticIp.checked + IpTextField { + id: gatewayField + label : "Gateway:" + } + IpTextField { + id: dnsField + label : "DNS Server:" + } + } + } + + } + RowLayout { + Layout.alignment: Qt.AlignCenter | Qt.AlignBottom + Layout.bottomMargin: 10 + spacing: 20 + + ImButton { + text: qsTr("CONTINUE") + onClicked: { + checkInputs() + if(validInputs) { + applySettings() + popup.close() + continueSignal() + } + } + } + + Text { text: " " } + } + + } + + + function openPopup() { + if (!initialized) { + initialize() + if (imageWriter.hasSavedCustomizationSettings()) + { + applySettings() + } + } + + open() + } + + function initialize() { + initialized = true + } + + function checkInputs() { + validInputs = false + if (!fieldServername.acceptableInput) { + fieldServername.forceActiveFocus() + return + } + if (!ipAddressField.acceptableInput) { + ipAddressField.forceActiveFocus() + return + } + if (!gatewayField.acceptableInput) { + gatewayField.forceActiveFocus() + return + } + if (!dnsField.acceptableInput) { + dnsField.forceActiveFocus() + return + } + validInputs = true + } + + function applySettings() { + var settings = { }; + settings.dhcp = radioDhcp.checked + settings.static = radioStaticIp.checked + settings.ipaddr = ipAddressField.fullAddress + settings.gateway = gatewayField.fullAddress + settings.dns = dnsField.fullAddress + settings.netmask = fieldNetmask.currentText + settings.servername = fieldServername.text + imageWriter.setSavedCustomizationSettings(settings) + + + // default these to off for now + imageWriter.setSetting("beep", false) + imageWriter.setSetting("eject", false) + imageWriter.setSetting("telemetry", false) // chkTelemtry.checked) + + cmdline = "" + config = "" + firstrun = "" + cloudinit = "" + cloudinitrun = "" + cloudinitwrite = "" + cloudinitnetwork = "" + imageWriter.setImageCustomization(config, cmdline, firstrun, cloudinit, cloudinitnetwork) + } +} diff --git a/src/UseSavedSettingsPopup.qml b/src/UseSavedSettingsPopup.qml index 145c26396..e4431ab66 100644 --- a/src/UseSavedSettingsPopup.qml +++ b/src/UseSavedSettingsPopup.qml @@ -18,6 +18,11 @@ Popup { padding: 0 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside modal: true + + background: Rectangle { + color: UnColors.darkGray + border.color: UnColors.mediumGray + } property bool hasSavedSettings: false @@ -29,7 +34,7 @@ Popup { // background of title Rectangle { - color: "#f5f5f5" + color: UnColors.mediumGray anchors.right: parent.right anchors.top: parent.top height: 35 @@ -37,7 +42,7 @@ Popup { } // line under title Rectangle { - color: "#afafaf" + color: UnColors.mediumGray width: parent.width y: 35 implicitHeight: 1 @@ -46,6 +51,7 @@ Popup { Text { id: msgx text: "X" + color: "white" anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 25 @@ -75,6 +81,7 @@ Popup { font.family: roboto.name font.bold: true text: qsTr("Use OS customization?") + color: "white" } Text { @@ -90,6 +97,7 @@ Popup { Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter Accessible.name: text.replace(/<\/?[^>]+(>|$)/g, "") text: qsTr("Would you like to apply OS customization settings?") + color: "white" } RowLayout { @@ -98,7 +106,7 @@ Popup { spacing: 20 id: buttons - ImButtonRed { + ImButton { text: qsTr("EDIT SETTINGS") onClicked: { // Don't close this dialog when "edit settings" is @@ -110,7 +118,7 @@ Popup { } } - ImButtonRed { + ImButton { id: noAndClearButton text: qsTr("NO, CLEAR SETTINGS") onClicked: { @@ -120,7 +128,7 @@ Popup { enabled: hasSavedSettings } - ImButtonRed { + ImButton { id: yesButton text: qsTr("YES") onClicked: { @@ -130,7 +138,7 @@ Popup { enabled: hasSavedSettings } - ImButtonRed { + ImButton { text: qsTr("NO") onClicked: { msgpopup.close() diff --git a/src/cli.cpp b/src/cli.cpp index 8eacb294c..b3c4c8a38 100644 --- a/src/cli.cpp +++ b/src/cli.cpp @@ -29,9 +29,9 @@ Cli::Cli(int &argc, char *argv[]) : QObject(nullptr) } #endif _app = new QCoreApplication(argc, argv); - _app->setOrganizationName("Raspberry Pi"); - _app->setOrganizationDomain("raspberrypi.org"); - _app->setApplicationName("Imager"); + _app->setOrganizationName("Lime Technology, Inc"); + _app->setOrganizationDomain("unraid.net"); + _app->setApplicationName("Unraid USB Creator"); _imageWriter = new ImageWriter; connect(_imageWriter, &ImageWriter::success, this, &Cli::onSuccess); connect(_imageWriter, &ImageWriter::error, this, &Cli::onError); @@ -211,7 +211,7 @@ int Cli::main() _imageWriter->setImageCustomization("", "", firstRunScript, "", ""); } - _imageWriter->setDst(args[1]); + _imageWriter->setDst(args[1], false); _imageWriter->setVerifyEnabled(!parser.isSet("disable-verify")); _imageWriter->setSetting("eject", !parser.isSet("disable-eject")); diff --git a/src/config.h b/src/config.h index 82afb7ad7..4a70d0a7a 100644 --- a/src/config.h +++ b/src/config.h @@ -8,13 +8,13 @@ /* Repository URL */ -#define OSLIST_URL "https://downloads.raspberrypi.org/os_list_imagingutility_v4.json" +#define OSLIST_URL "https://releases.unraid.net/usb-creator" /* Time synchronization URL (only used on eglfs QPA platform, URL must be HTTP) */ #define TIME_URL "http://downloads.raspberrypi.org/os_list_imagingutility_v4.json?time_synchronization" /* Phone home the name of images downloaded for image popularity ranking */ -#define TELEMETRY_URL "https://rpi-imager-stats.raspberrypi.com/downloads" +#define TELEMETRY_URL "" /* Hash algorithm for verifying (uncompressed image) checksum */ #define OSLIST_HASH_ALGORITHM QCryptographicHash::Sha256 @@ -37,4 +37,6 @@ /* Do not cache if it would bring free disk space under 5 GB */ #define IMAGEWRITER_MINIMAL_SPACE_FOR_CACHING 5*1024*1024*1024ll +#define UNRAID_GUID_URL "https://keys.lime-technology.com/validate/guid" + #endif // CONFIG_H diff --git a/src/dependencies/drivelist/src/darwin/list.mm b/src/dependencies/drivelist/src/darwin/list.mm index 4509df54b..249023359 100644 --- a/src/dependencies/drivelist/src/darwin/list.mm +++ b/src/dependencies/drivelist/src/darwin/list.mm @@ -24,6 +24,7 @@ #import #import #import +#import namespace Drivelist { bool IsDiskPartition(NSString *disk) { @@ -106,6 +107,68 @@ bool IsCard(CFDictionaryRef diskDescription) { return result; } +// BEGIN: FUNCTION ADDED FOR UNRAID USB CREATOR +void GetDeviceVidPidSerialNumber(const std::string& diskBsdName, DeviceDescriptor* device_descriptor) { + CFMutableDictionaryRef matchingDict = IOServiceMatching("IOUSBHostDevice"); + if (matchingDict == NULL) { + return; + } + io_iterator_t iter; + kern_return_t ret = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter); + if (ret != KERN_SUCCESS) { + return; + } + io_service_t device; + while ((device = IOIteratorNext(iter))) { + std::string bsdName{""}; + CFTypeRef bsdNameAsCFTypeRef = IORegistryEntrySearchCFProperty(device, kIOServicePlane, CFSTR(kIOBSDNameKey), kCFAllocatorDefault, kIORegistryIterateRecursively); + if (bsdNameAsCFTypeRef) { + if (CFGetTypeID(bsdNameAsCFTypeRef) == CFStringGetTypeID()) { + NSString* s = (NSString *) bsdNameAsCFTypeRef; + bsdName = std::string([s UTF8String]); + // match usb device to disk device using bsdname + if(bsdName == diskBsdName) { + CFTypeRef serialNumberAsCFTypeRef = IORegistryEntrySearchCFProperty(device, kIOServicePlane, CFSTR(kUSBSerialNumberString), kCFAllocatorDefault, kIORegistryIterateRecursively); + CFTypeRef vendorIdAsCFTypeRef = IORegistryEntrySearchCFProperty(device, kIOServicePlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, kIORegistryIterateRecursively); + CFTypeRef productIdAsCFTypeRef = IORegistryEntrySearchCFProperty(device, kIOServicePlane, CFSTR(kUSBProductID), kCFAllocatorDefault, kIORegistryIterateRecursively); + + bool allNotNull = serialNumberAsCFTypeRef && vendorIdAsCFTypeRef && productIdAsCFTypeRef; + bool allCorrectType = (CFGetTypeID(serialNumberAsCFTypeRef) == CFStringGetTypeID()) && (CFGetTypeID(vendorIdAsCFTypeRef) == CFNumberGetTypeID()) && (CFGetTypeID(productIdAsCFTypeRef) == CFNumberGetTypeID()); + if(allNotNull && allCorrectType) { + s = (NSString *) serialNumberAsCFTypeRef; + device_descriptor->serialNumber = std::string([s UTF8String]); + NSNumber * n = (NSNumber *) vendorIdAsCFTypeRef; + s = [NSString stringWithFormat:@"%X", [n intValue]]; + while ([s length] < 4) { + s = [@"0" stringByAppendingString:s]; + } + device_descriptor->vid = std::string([s UTF8String]); + n = (NSNumber *) productIdAsCFTypeRef; + s = [NSString stringWithFormat:@"%X", [n intValue]]; + while ([s length] < 4) { + s = [@"0" stringByAppendingString:s]; + } + device_descriptor->pid = std::string([s UTF8String]); + } + if(serialNumberAsCFTypeRef) CFRelease(serialNumberAsCFTypeRef); + if(vendorIdAsCFTypeRef) CFRelease(vendorIdAsCFTypeRef); + if(productIdAsCFTypeRef) CFRelease(productIdAsCFTypeRef); + } + } + CFRelease(bsdNameAsCFTypeRef); + } + IOObjectRelease(device); + if(!device_descriptor->serialNumber.empty() && !device_descriptor->vid.empty() && !device_descriptor->pid.empty()) { + IOObjectRelease(iter); + return; + } + } + IOObjectRelease(iter); + +} + +// END: FUNCTION ADDED FOR UNRAID USB CREATOR + DeviceDescriptor CreateDeviceDescriptorFromDiskDescription(std::string diskBsdName, CFDictionaryRef diskDescription) { NSString *deviceProtocol = (NSString*)CFDictionaryGetValue(diskDescription, kDADiskDescriptionDeviceProtocolKey); NSNumber *blockSize = DictionaryGetNumber(diskDescription, kDADiskDescriptionMediaBlockSizeKey); @@ -148,6 +211,10 @@ DeviceDescriptor CreateDeviceDescriptorFromDiskDescription(std::string diskBsdNa device.isUAS = false; device.isUASNull = true; +// BEGIN: FUNCTION ADDED FOR UNRAID USB CREATOR + GetDeviceVidPidSerialNumber(diskBsdName, &device); +// END: FUNCTION ADDED FOR UNRAID USB CREATOR + return device; } diff --git a/src/dependencies/drivelist/src/drivelist.hpp b/src/dependencies/drivelist/src/drivelist.hpp index 42d4e17a0..b8e877d90 100644 --- a/src/dependencies/drivelist/src/drivelist.hpp +++ b/src/dependencies/drivelist/src/drivelist.hpp @@ -55,6 +55,12 @@ struct DeviceDescriptor { bool isUSB; // Connected via Universal Serial Bus (USB) bool isUAS; // Connected via the USB Attached SCSI (UAS) bool isUASNull; + + // BEGIN: VARIABLES ADDED FOR UNRAID USB CREATOR + std::string vid; + std::string pid; + std::string serialNumber; + // END: VARIABLES ADDED FOR UNRAID USB CREATOR }; std::vector ListStorageDevices(); diff --git a/src/dependencies/drivelist/src/windows/list.cpp b/src/dependencies/drivelist/src/windows/list.cpp index c7f65a554..3768b5459 100644 --- a/src/dependencies/drivelist/src/windows/list.cpp +++ b/src/dependencies/drivelist/src/windows/list.cpp @@ -37,6 +37,8 @@ #include "../drivelist.hpp" #include "list.hpp" +#include + #include // Maxnet edit @@ -613,6 +615,81 @@ bool GetDetailData(DeviceDescriptor* device, return result; } +// BEGIN: FUNCTION ADDED FOR UNRAID USB CREATOR +bool GetDeviceVidPidSerialNumber(HDEVINFO hDeviceInfo, PSP_DEVINFO_DATA deviceInfoData, DeviceDescriptor* deviceDescriptor) { + CHAR wbuffer[MAX_PATH]; + ZeroMemory(&wbuffer, sizeof(wbuffer)); + + // we can get the serial number from an HDEVINFO opened with GUID_DEVICE_INTERFACE_DISK, + // but not VID or PID - so, extract SN first, then loop through devices with GUID_DEVICE_INTERFACE_USB_DEVICE + // to correlate all three + BOOL hasDeviceInstanceId = SetupDiGetDeviceInstanceId(hDeviceInfo, deviceInfoData, wbuffer, sizeof(wbuffer), NULL); + std::string deviceId = hasDeviceInstanceId ? std::string(wbuffer) : std::string(""); + + if(deviceId.empty()) { + return false; + } + + ZeroMemory(&wbuffer, sizeof(wbuffer)); + + HDEVINFO hInfo = NULL; + hInfo = SetupDiGetClassDevs(&GUID_DEVICE_INTERFACE_USB_DEVICE, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE); + if (hInfo == INVALID_HANDLE_VALUE) { + return false; + } + SP_DEVINFO_DATA DeviceInfoData; + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + std::string VID{""}, PID{""}, SN{""}; + size_t matchCount{0}; + for (DWORD i=0; SetupDiEnumDeviceInfo(hInfo, i, &DeviceInfoData); i++) + { + DWORD nSize = 0; + + if (!SetupDiGetDeviceInstanceId(hInfo, &DeviceInfoData, wbuffer, sizeof(wbuffer), &nSize)) { + continue; + } + + std::string deviceId_usb{wbuffer}; + std::smatch vid_pid_sn_match; + std::regex vid_pid_sn_regex("USB\\\\VID_([0-9A-Za-z]{4})(?:.*)&PID_([0-9A-Za-z]{4})(?:.*)\\\\([0-9A-Za-z]+)"); + if (std::regex_search(deviceId_usb, vid_pid_sn_match, vid_pid_sn_regex) && vid_pid_sn_match.size() == 4) + { + VID = vid_pid_sn_match[1]; + PID = vid_pid_sn_match[2]; + SN = vid_pid_sn_match[3]; + } + if(VID.empty() || PID.empty() || SN.empty()) { + continue; + } + + std::regex sn_regex("\\\\" + SN); + std::smatch sn_match; + if(std::regex_search(deviceId, sn_match, sn_regex)) { + deviceDescriptor->vid = VID; + deviceDescriptor->pid = PID; + deviceDescriptor->serialNumber = SN; + matchCount++; + } + } + + + if (hInfo) { + SetupDiDestroyDeviceInfoList(hInfo); + } + + if(matchCount != 1) { + // we either had multiple ambiguous matches or no matches + deviceDescriptor->vid = ""; + deviceDescriptor->pid = ""; + deviceDescriptor->serialNumber = ""; + return false; + } + + + return true; +} +// END: FUNCTION ADDED FOR UNRAID USB CREATOR + std::vector ListStorageDevices() { HDEVINFO hDeviceInfo = NULL; SP_DEVINFO_DATA deviceInfoData; @@ -658,6 +735,10 @@ std::vector ListStorageDevices() { !device.isVirtual && !device.isCard; device.devicePathNull = true; +// BEGIN: FUNCTION ADDED FOR UNRAID USB CREATOR + GetDeviceVidPidSerialNumber(hDeviceInfo, &deviceInfoData, &device); +// END: FUNCTION ADDED FOR UNRAID USB CREATOR + if (GetDetailData(&device, hDeviceInfo, deviceInfoData)) { device.isSystem = device.isSystem || IsSystemDevice(hDeviceInfo, &device); device.isCard = device.busType == "SD" || device.busType == "MMC"; @@ -674,7 +755,10 @@ std::vector ListStorageDevices() { SetupDiDestroyDeviceInfoList(hDeviceInfo); + + return deviceList; } + } // namespace Drivelist diff --git a/src/dependencies/zlib-1.2.13/zconf.h b/src/dependencies/zlib-1.2.13/zconf.h.included similarity index 100% rename from src/dependencies/zlib-1.2.13/zconf.h rename to src/dependencies/zlib-1.2.13/zconf.h.included diff --git a/src/downloadextractthread.cpp b/src/downloadextractthread.cpp index ac61df67a..a968ddecc 100644 --- a/src/downloadextractthread.cpp +++ b/src/downloadextractthread.cpp @@ -354,6 +354,85 @@ void DownloadExtractThread::extractMultiFileRun() emit cacheFileUpdated(computedHash); } + if(_initFormat == "UNRAID") { + if(_allNetworkSettingsPresent() && _imgWriterSettings["static"].toBool()) + { + QFile fileNetwork(folder + "/config/network.cfg"); + if (fileNetwork.exists()) + { + fileNetwork.open(QIODevice::ReadOnly); + QString dataText = fileNetwork.readAll(); + fileNetwork.close(); + + dataText.replace("USE_DHCP=\"yes\"", "USE_DHCP=\"no\""); + dataText.replace("IPADDR=", "IPADDR=\"" + _imgWriterSettings["ipaddr"].toString() + "\""); + dataText.replace("NETMASK=", "NETMASK=\"" + _imgWriterSettings["netmask"].toString() + "\""); + dataText.replace("GATEWAY=", "GATEWAY=\"" + _imgWriterSettings["gateway"].toString() + "\""); + dataText.append("DNS_SERVER1=\"" + _imgWriterSettings["dns"].toString() + "\"\r\n"); + + if (fileNetwork.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream out(&fileNetwork); + out << dataText; + } + fileNetwork.close(); + } + + } + if(_imgWriterSettings.contains("servername")) { + QFile fileIdent(folder + "/config/ident.cfg"); + if (fileIdent.exists()) + { + fileIdent.open(QIODevice::ReadOnly); + QString dataText = fileIdent.readAll(); + fileIdent.close(); + + dataText.replace("NAME=\"Tower\"", "NAME=\"" + _imgWriterSettings["servername"].toString() + "\""); + + if (fileIdent.open(QFile::WriteOnly | QFile::Truncate)) + { + QTextStream out(&fileIdent); + out << dataText; + } + fileIdent.close(); + } + } + + // restore make bootable scripts and/or syslinux, if necessary + QDir dirTarget(folder); + if (dirTarget.mkdir("syslinux")) + { + QFile::copy(":/unraid/syslinux/ldlinux.c32", folder + "/syslinux/ldlinux.c32"); + QFile::copy(":/unraid/syslinux/libcom32.c32", folder + "/syslinux/libcom32.c32"); + QFile::copy(":/unraid/syslinux/libutil.c32", folder + "/syslinux/libutil.c32"); + QFile::copy(":/unraid/syslinux/make_bootable_linux.sh", folder + "/syslinux/make_bootable_linux.sh"); + QFile::copy(":/unraid/syslinux/make_bootable_mac.sh", folder + "/syslinux/make_bootable_mac.sh"); + QFile::copy(":/unraid/syslinux/mboot.c32", folder + "/syslinux/mboot.c32"); + QFile::copy(":/unraid/syslinux/mbr.bin", folder + "/syslinux/mbr.bin"); + QFile::copy(":/unraid/syslinux/menu.c32", folder + "/syslinux/menu.c32"); + QFile::copy(":/unraid/syslinux/syslinux", folder + "/syslinux/syslinux"); + QFile::copy(":/unraid/syslinux/syslinux_linux", folder + "/syslinux/syslinux_linux"); + QFile::copy(":/unraid/syslinux/syslinux.cfg", folder + "/syslinux/syslinux.cfg"); + QFile::copy(":/unraid/syslinux/syslinux.cfg-", folder + "/syslinux/syslinux.cfg-"); + QFile::copy(":/unraid/syslinux/syslinux.exe", folder + "/syslinux/syslinux.exe"); + QFile::copy(":/unraid/make_bootable_linux", folder + "/make_bootable_linux"); + QFile::copy(":/unraid/make_bootable_mac", folder + "/make_bootable_mac"); + QFile::copy(":/unraid/make_bootable.bat", folder + "/make_bootable.bat"); + } + +#ifdef Q_OS_WIN + QString program{"cmd.exe"}; + QStringList args; + args << "/C" << "echo Y | make_bootable.bat"; + + int retcode = QProcess::execute(program, args); + + if (retcode) + { + throw runtime_error("Error running make_bootable script"); + } +#endif + } emit success(); } catch (exception &e) @@ -401,7 +480,7 @@ void DownloadExtractThread::extractMultiFileRun() } #endif - eject_disk(_filename.constData()); + //eject_disk(_filename.constData()); } ssize_t DownloadExtractThread::_on_read(struct archive *, const void **buff) diff --git a/src/downloadthread.cpp b/src/downloadthread.cpp index c9ec8e647..f14dc8610 100644 --- a/src/downloadthread.cpp +++ b/src/downloadthread.cpp @@ -115,7 +115,7 @@ bool DownloadThread::_openAndPrepareDevice() { if (_filename.startsWith("/dev/")) { - emit preparationStatusUpdate(tr("unmounting drive")); + emit preparationStatusUpdate(tr("Unmounting drive")); #ifdef Q_OS_DARWIN /* Also unmount any APFS volumes using this physical disk */ auto l = Drivelist::ListStorageDevices(); @@ -135,7 +135,7 @@ bool DownloadThread::_openAndPrepareDevice() qDebug() << "Unmounting:" << _filename; unmount_disk(_filename.constData()); } - emit preparationStatusUpdate(tr("opening drive")); + emit preparationStatusUpdate(tr("Opening drive")); _file.setFileName(_filename); @@ -214,7 +214,7 @@ bool DownloadThread::_openAndPrepareDevice() return false; } else if (authopenresult == _file.authOpenError) { QString msg = tr("Error running authopen to gain access to disk device '%1'").arg(QString(_filename)); - msg += "
"+tr("Please verify if 'Raspberry Pi Imager' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access')."); + msg += "
"+tr("Please verify if 'Unraid USB Creator' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access')."); QStringList args("x-apple.systempreferences:com.apple.preference.security?Privacy_RemovableVolume"); QProcess::execute("open", args); emit error(msg); @@ -278,7 +278,7 @@ bool DownloadThread::_openAndPrepareDevice() qDebug() << "Try to perform TRIM/DISCARD on device"; range[0] = 0; range[1] = devsize; - emit preparationStatusUpdate(tr("discarding existing data on drive")); + emit preparationStatusUpdate(tr("Discarding existing data on drive")); _timer.start(); if (::ioctl(fd, BLKDISCARD, &range) == -1) { @@ -298,7 +298,7 @@ bool DownloadThread::_openAndPrepareDevice() qint64 knownsize = _file.size(); QByteArray emptyMB(1024*1024, 0); - emit preparationStatusUpdate(tr("zeroing out first and last MB of drive")); + emit preparationStatusUpdate(tr("Zeroing out first and last MB of drive")); qDebug() << "Zeroing out first and last MB of drive"; _timer.start(); @@ -405,7 +405,7 @@ void DownloadThread::run() if (!_proxy.isEmpty()) curl_easy_setopt(_c, CURLOPT_PROXY, _proxy.constData()); - emit preparationStatusUpdate(tr("starting download")); + emit preparationStatusUpdate(tr("Starting download")); _timer.start(); CURLcode ret = curl_easy_perform(_c); @@ -667,7 +667,7 @@ void DownloadThread::_onWriteError() QSettings::Registry64Format); if (registry.value("EnableControlledFolderAccess").toInt() == 1) { - msg += "
"+tr("Controlled Folder Access seems to be enabled. Please add both rpi-imager.exe and fat32format.exe to the list of allowed apps and try again."); + msg += "
"+tr("Controlled Folder Access seems to be enabled. Please add both unraid-usb-creator.exe and fat32format.exe to the list of allowed apps and try again."); } _onDownloadError(msg); } @@ -885,7 +885,7 @@ qint64 DownloadThread::_sectorsWritten() return -1; } -void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudInitNetwork, const QByteArray &initFormat) +void DownloadThread::setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudInitNetwork, const QByteArray &initFormat, const QVariantMap& imgWriterSettings) { _config = config; _cmdline = cmdline; @@ -893,6 +893,7 @@ void DownloadThread::setImageCustomization(const QByteArray &config, const QByte _cloudinit = cloudinit; _cloudinitNetwork = cloudInitNetwork; _initFormat = initFormat; + _imgWriterSettings = imgWriterSettings; } bool DownloadThread::_customizeImage() @@ -1005,3 +1006,11 @@ bool DownloadThread::_customizeImage() return true; } + +bool DownloadThread::_allNetworkSettingsPresent() { + return _imgWriterSettings.contains("static") && + _imgWriterSettings.contains("ipaddr") && + _imgWriterSettings.contains("gateway") && + _imgWriterSettings.contains("dns") && + _imgWriterSettings.contains("netmask"); +} diff --git a/src/downloadthread.h b/src/downloadthread.h index 3fb954255..3de26b8ee 100644 --- a/src/downloadthread.h +++ b/src/downloadthread.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -116,7 +117,7 @@ class DownloadThread : public QThread /* * Enable image customization */ - void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const QByteArray &initFormat); + void setImageCustomization(const QByteArray &config, const QByteArray &cmdline, const QByteArray &firstrun, const QByteArray &cloudinit, const QByteArray &cloudinitNetwork, const QByteArray &initFormat, const QVariantMap& imgWriterSettings); /* * Thread safe download progress query functions @@ -165,6 +166,8 @@ class DownloadThread : public QThread static int _curl_xferinfo_callback(void *userdata, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow); static size_t _curl_header_callback( void *ptr, size_t size, size_t nmemb, void *userdata); + bool _allNetworkSettingsPresent(); + CURL *_c; curl_off_t _startOffset; std::atomic _lastDlTotal, _lastDlNow, _verifyTotal, _lastVerifyNow, _bytesWritten; @@ -191,6 +194,7 @@ class DownloadThread : public QThread QFile _cachefile; AcceleratedCryptographicHash _writehash, _verifyhash; + QVariantMap _imgWriterSettings; }; #endif // DOWNLOADTHREAD_H diff --git a/src/driveformatthread.cpp b/src/driveformatthread.cpp index 11a5748f1..d29cc0d4c 100644 --- a/src/driveformatthread.cpp +++ b/src/driveformatthread.cpp @@ -17,8 +17,8 @@ #include #endif -DriveFormatThread::DriveFormatThread(const QByteArray &device, QObject *parent) - : QThread(parent), _device(device) +DriveFormatThread::DriveFormatThread(const QByteArray &device, const QString& label, QObject *parent) + : QThread(parent), _device(device), _label(label) { } @@ -77,6 +77,10 @@ void DriveFormatThread::run() QProcess f32format; QStringList args; + if(!_label.isEmpty()) + { + args << "-l" + _label; + } args << "-y" << driveLetter; f32format.start(QCoreApplication::applicationDirPath()+"/fat32format.exe", args); if (!f32format.waitForStarted()) @@ -111,7 +115,8 @@ void DriveFormatThread::run() #elif defined(Q_OS_DARWIN) QProcess proc; QStringList args; - args << "eraseDisk" << "FAT32" << "SDCARD" << "MBRFormat" << _device; + QString name = (_label.isEmpty()) ? "SDCARD" : _label; + args << "eraseDisk" << "FAT32" << name << "MBRFormat" << _device; proc.start("diskutil", args); proc.waitForFinished(); @@ -133,21 +138,26 @@ void DriveFormatThread::run() if (::access(_device.constData(), W_OK) != 0) { /* Not running as root, try to outsource formatting to udisks2 */ - -#ifndef QT_NO_DBUS - UDisks2Api udisks2; - if (udisks2.formatDrive(_device)) + if(_label.isEmpty()) { - emit success(); +#ifndef QT_NO_DBUS + UDisks2Api udisks2; + if (udisks2.formatDrive(_device)) + { + emit success(); + } + else + { +#endif + emit error(tr("Error formatting (through udisks2)")); +#ifndef QT_NO_DBUS + } +#endif } else { -#endif - emit error(tr("Error formatting (through udisks2)")); -#ifndef QT_NO_DBUS + emit error(tr("Elevated privileges needed to properly format drive.")); } -#endif - return; } @@ -202,6 +212,10 @@ void DriveFormatThread::run() args.clear(); args << fatpartition; + if(!_label.isEmpty()) + { + args << "-n" << _label; + } proc.start("mkfs.fat", args); if (!proc.waitForStarted()) { diff --git a/src/driveformatthread.h b/src/driveformatthread.h index daa122a1f..0a4018a6a 100644 --- a/src/driveformatthread.h +++ b/src/driveformatthread.h @@ -12,7 +12,7 @@ class DriveFormatThread : public QThread { Q_OBJECT public: - DriveFormatThread(const QByteArray &device, QObject *parent = nullptr); + DriveFormatThread(const QByteArray &device, const QString& label, QObject *parent = nullptr); virtual ~DriveFormatThread(); virtual void run(); @@ -22,6 +22,7 @@ class DriveFormatThread : public QThread protected: QByteArray _device; + QString _label; }; #endif // DRIVEFORMATTHREAD_H diff --git a/src/drivelistitem.cpp b/src/drivelistitem.cpp index af3129f0e..467c54cf4 100644 --- a/src/drivelistitem.cpp +++ b/src/drivelistitem.cpp @@ -5,8 +5,8 @@ #include "drivelistitem.h" -DriveListItem::DriveListItem(QString device, QString description, quint64 size, bool isUsb, bool isScsi, bool readOnly, QStringList mountpoints, QObject *parent) - : QObject(parent), _device(device), _description(description), _mountpoints(mountpoints), _size(size), _isUsb(isUsb), _isScsi(isScsi), _isReadOnly(readOnly) +DriveListItem::DriveListItem(QString device, QString description, quint64 size, QString guid, bool guidValid, bool isUsb, bool isScsi, bool readOnly, QStringList mountpoints, QObject *parent) + : QObject(parent), _device(device), _description(description), _mountpoints(mountpoints), _size(size), _guid(guid), _guidValid(guidValid), _isUsb(isUsb), _isScsi(isScsi), _isReadOnly(readOnly) { } diff --git a/src/drivelistitem.h b/src/drivelistitem.h index 843f6bb2d..e74bfccbe 100644 --- a/src/drivelistitem.h +++ b/src/drivelistitem.h @@ -13,7 +13,7 @@ class DriveListItem : public QObject { Q_OBJECT public: - explicit DriveListItem(QString device, QString description, quint64 size, bool isUsb = false, bool isScsi = false, bool readOnly = false, QStringList mountpoints = QStringList(), QObject *parent = nullptr); + explicit DriveListItem(QString device, QString description, quint64 size, QString guid, bool guidValid, bool isUsb = false, bool isScsi = false, bool readOnly = false, QStringList mountpoints = QStringList(), QObject *parent = nullptr); Q_PROPERTY(QString device MEMBER _device CONSTANT) Q_PROPERTY(QString description MEMBER _description CONSTANT) @@ -22,6 +22,8 @@ class DriveListItem : public QObject Q_PROPERTY(bool isUsb MEMBER _isUsb CONSTANT) Q_PROPERTY(bool isScsi MEMBER _isScsi CONSTANT) Q_PROPERTY(bool isReadOnly MEMBER _isReadOnly CONSTANT) + Q_PROPERTY(QString guid MEMBER _guid CONSTANT) + Q_PROPERTY(bool guidValid MEMBER _guidValid CONSTANT) Q_INVOKABLE int sizeInGb(); signals: @@ -33,6 +35,8 @@ public slots: QString _description; QStringList _mountpoints; quint64 _size; + QString _guid; + bool _guidValid; bool _isUsb; bool _isScsi; bool _isReadOnly; diff --git a/src/drivelistmodel.cpp b/src/drivelistmodel.cpp index fa10feb5b..395617986 100644 --- a/src/drivelistmodel.cpp +++ b/src/drivelistmodel.cpp @@ -4,10 +4,14 @@ */ #include "drivelistmodel.h" -#include "config.h" #include "dependencies/drivelist/src/drivelist.hpp" #include #include +#include +#include "unraidguidvalidator.h" + +#include + DriveListModel::DriveListModel(QObject *parent) : QAbstractListModel(parent) @@ -19,11 +23,14 @@ DriveListModel::DriveListModel(QObject *parent) {isUsbRole, "isUsb"}, {isScsiRole, "isScsi"}, {isReadOnlyRole, "isReadOnly"}, - {mountpointsRole, "mountpoints"} + {mountpointsRole, "mountpoints"}, + {guidRole, "guid"}, + {guidValidRole, "guidValid"} }; // Enumerate drives in seperate thread, but process results in UI thread connect(&_thread, SIGNAL(newDriveList(std::vector)), SLOT(processDriveList(std::vector))); + } int DriveListModel::rowCount(const QModelIndex &) const @@ -54,6 +61,8 @@ void DriveListModel::processDriveList(std::vector l bool changes = false; bool filterSystemDrives = DRIVELIST_FILTER_SYSTEM_DRIVES; QSet drivesInNewList; + + UnraidGuidValidator unraidGuidValidator; for (auto &i: l) { @@ -96,7 +105,8 @@ void DriveListModel::processDriveList(std::vector l changes = true; } - _drivelist[deviceNamePlusSize] = new DriveListItem(QString::fromStdString(i.device), QString::fromStdString(i.description), i.size, i.isUSB, i.isSCSI, i.isReadOnly, mountpoints, this); + auto result = unraidGuidValidator.checkDevice(i); + _drivelist[deviceNamePlusSize] = new DriveListItem(QString::fromStdString(i.device), QString::fromStdString(i.description), i.size, result.guid, result.guidValid, i.isUSB, i.isSCSI, i.isReadOnly, mountpoints, this); } } @@ -130,3 +140,13 @@ void DriveListModel::stopPolling() { _thread.stop(); } + +size_t DriveListModel::_curl_write_callback(char *, size_t size, size_t nmemb, void *) +{ + return size * nmemb; +} + +size_t DriveListModel::_curl_header_callback(void *, size_t size, size_t nmemb, void *) +{ + return size*nmemb; +} diff --git a/src/drivelistmodel.h b/src/drivelistmodel.h index e9f2e10a8..a83e4c935 100644 --- a/src/drivelistmodel.h +++ b/src/drivelistmodel.h @@ -24,7 +24,7 @@ class DriveListModel : public QAbstractListModel void stopPolling(); enum driveListRoles { - deviceRole = Qt::UserRole + 1, descriptionRole, sizeRole, isUsbRole, isScsiRole, isReadOnlyRole, mountpointsRole + deviceRole = Qt::UserRole + 1, descriptionRole, sizeRole, isUsbRole, isScsiRole, isReadOnlyRole, mountpointsRole, guidRole, guidValidRole }; public slots: @@ -34,6 +34,8 @@ public slots: QMap _drivelist; QHash _rolenames; DriveListModelPollThread _thread; + size_t _curl_write_callback(char *, size_t size, size_t nmemb, void *); + size_t _curl_header_callback(void *, size_t size, size_t nmemb, void *); }; #endif // DRIVELISTMODEL_H diff --git a/src/i18n/rpi-imager_ca.ts b/src/i18n/legacy/rpi-imager_ca.ts similarity index 100% rename from src/i18n/rpi-imager_ca.ts rename to src/i18n/legacy/rpi-imager_ca.ts diff --git a/src/i18n/rpi-imager_de.ts b/src/i18n/legacy/rpi-imager_de.ts similarity index 100% rename from src/i18n/rpi-imager_de.ts rename to src/i18n/legacy/rpi-imager_de.ts diff --git a/src/i18n/rpi-imager_en.ts b/src/i18n/legacy/rpi-imager_en.ts similarity index 100% rename from src/i18n/rpi-imager_en.ts rename to src/i18n/legacy/rpi-imager_en.ts diff --git a/src/i18n/rpi-imager_es.ts b/src/i18n/legacy/rpi-imager_es.ts similarity index 100% rename from src/i18n/rpi-imager_es.ts rename to src/i18n/legacy/rpi-imager_es.ts diff --git a/src/i18n/rpi-imager_fr.ts b/src/i18n/legacy/rpi-imager_fr.ts similarity index 100% rename from src/i18n/rpi-imager_fr.ts rename to src/i18n/legacy/rpi-imager_fr.ts diff --git a/src/i18n/rpi-imager_it.ts b/src/i18n/legacy/rpi-imager_it.ts similarity index 100% rename from src/i18n/rpi-imager_it.ts rename to src/i18n/legacy/rpi-imager_it.ts diff --git a/src/i18n/rpi-imager_ja.ts b/src/i18n/legacy/rpi-imager_ja.ts similarity index 100% rename from src/i18n/rpi-imager_ja.ts rename to src/i18n/legacy/rpi-imager_ja.ts diff --git a/src/i18n/rpi-imager_ko.ts b/src/i18n/legacy/rpi-imager_ko.ts similarity index 100% rename from src/i18n/rpi-imager_ko.ts rename to src/i18n/legacy/rpi-imager_ko.ts diff --git a/src/i18n/rpi-imager_nl.ts b/src/i18n/legacy/rpi-imager_nl.ts similarity index 100% rename from src/i18n/rpi-imager_nl.ts rename to src/i18n/legacy/rpi-imager_nl.ts diff --git a/src/i18n/rpi-imager_pl.ts b/src/i18n/legacy/rpi-imager_pl.ts similarity index 100% rename from src/i18n/rpi-imager_pl.ts rename to src/i18n/legacy/rpi-imager_pl.ts diff --git a/src/i18n/rpi-imager_ru.ts b/src/i18n/legacy/rpi-imager_ru.ts similarity index 100% rename from src/i18n/rpi-imager_ru.ts rename to src/i18n/legacy/rpi-imager_ru.ts diff --git a/src/i18n/rpi-imager_sk.ts b/src/i18n/legacy/rpi-imager_sk.ts similarity index 100% rename from src/i18n/rpi-imager_sk.ts rename to src/i18n/legacy/rpi-imager_sk.ts diff --git a/src/i18n/rpi-imager_sl.ts b/src/i18n/legacy/rpi-imager_sl.ts similarity index 100% rename from src/i18n/rpi-imager_sl.ts rename to src/i18n/legacy/rpi-imager_sl.ts diff --git a/src/i18n/rpi-imager_tr.ts b/src/i18n/legacy/rpi-imager_tr.ts similarity index 100% rename from src/i18n/rpi-imager_tr.ts rename to src/i18n/legacy/rpi-imager_tr.ts diff --git a/src/i18n/rpi-imager_uk.ts b/src/i18n/legacy/rpi-imager_uk.ts similarity index 100% rename from src/i18n/rpi-imager_uk.ts rename to src/i18n/legacy/rpi-imager_uk.ts diff --git a/src/i18n/rpi-imager_zh-TW.ts b/src/i18n/legacy/rpi-imager_zh-TW.ts similarity index 100% rename from src/i18n/rpi-imager_zh-TW.ts rename to src/i18n/legacy/rpi-imager_zh-TW.ts diff --git a/src/i18n/rpi-imager_zh.ts b/src/i18n/legacy/rpi-imager_zh.ts similarity index 100% rename from src/i18n/rpi-imager_zh.ts rename to src/i18n/legacy/rpi-imager_zh.ts diff --git a/src/i18n/translations.qrc b/src/i18n/translations.qrc index a37fcabce..fccf7a1f9 100644 --- a/src/i18n/translations.qrc +++ b/src/i18n/translations.qrc @@ -1,21 +1,9 @@ - rpi-imager_de.qm - rpi-imager_en.qm - rpi-imager_es.qm - rpi-imager_fr.qm - rpi-imager_it.qm - rpi-imager_ko.qm - rpi-imager_nl.qm - rpi-imager_pl.qm - rpi-imager_ru.qm - rpi-imager_sl.qm - rpi-imager_sk.qm - rpi-imager_tr.qm - rpi-imager_zh.qm - rpi-imager_ca.qm - rpi-imager_ja.qm - rpi-imager_uk.qm - rpi-imager_zh-TW.qm + unraid-usb-creator_en.qm + unraid-usb-creator_es.qm + unraid-usb-creator_de.qm + unraid-usb-creator_fr.qm + unraid-usb-creator_zh.qm diff --git a/src/i18n/unraid-usb-creator_de.ts b/src/i18n/unraid-usb-creator_de.ts new file mode 100644 index 000000000..5e13f8296 --- /dev/null +++ b/src/i18n/unraid-usb-creator_de.ts @@ -0,0 +1,800 @@ + + + + + DownloadExtractThread + + + + Error extracting archive: %1 + Fehler beim Entpacken des Archivs: %1 + + + + Error mounting FAT32 partition + Fehler beim Einhängen der FAT32-Partition + + + + Operating system did not mount FAT32 partition + Betriebssystem hat die FAT32-Partition nicht eingehängt + + + + Error changing to directory '%1' + Fehler beim Wechseln in das Verzeichnis '%1' + + + + + DownloadThread + + + unmounting drive + Laufwerk wird ausgehängt + + + + opening drive + Laufwerk wird geöffnet + + + + Error running diskpart: %1 + Fehler beim Ausführen von diskpart: %1 + + + + Error removing existing partitions + Fehler beim Entfernen bestehender Partitionen + + + + Authentication cancelled + Authentifizierung abgebrochen + + + + Error running authopen to gain access to disk device '%1' + Fehler beim Ausführen von authopen, um Zugriff auf das Festplattengerät '%1' zu erhalten + + + + Please verify if 'Unraid USB Creator' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). + Bitte überprüfen Sie, ob 'Unraid USB Creator' in den Datenschutzeinstellungen auf 'Wechseldatenträger' zugreifen darf (unter 'Dateien und Ordner' oder alternativ geben Sie ihm 'vollen Festplattenzugriff'). + + + + Cannot open storage device '%1'. + Kann das Speichergerät '%1' nicht öffnen. + + + + discarding existing data on drive + Vorhandene Daten auf dem Laufwerk werden verworfen + + + + zeroing out first and last MB of drive + Erste und letzte MB des Laufwerks werden gelöscht + + + + Write error while zero'ing out MBR + Schreibfehler beim Löschen des MBR + + + + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). + Schreibfehler beim Löschen des letzten Teils der Karte.<br>Die Karte könnte eine falsche Kapazität anzeigen (möglicherweise gefälscht). + + + + starting download + Download wird gestartet + + + + Error downloading: %1 + Fehler beim Herunterladen: %1 + + + + Access denied error while writing file to disk. + Zugriffsverweigerungsfehler beim Schreiben der Datei auf die Festplatte. + + + + Controlled Folder Access seems to be enabled. Please add both unraid-usb-creator.exe and fat32format.exe to the list of allowed apps and try again. + Der kontrollierte Ordnerzugriff scheint aktiviert zu sein. Bitte fügen Sie sowohl unraid-usb-creator.exe als auch fat32format.exe zur Liste der erlaubten Apps hinzu und versuchen Sie es erneut. + + + + Error writing file to disk + Fehler beim Schreiben der Datei auf die Festplatte + + + + Download corrupt. Hash does not match + Download beschädigt. Hash stimmt nicht überein + + + + + Error writing to storage (while flushing) + Fehler beim Schreiben auf den Speicher (beim Flush) + + + + + Error writing to storage (while fsync) + Fehler beim Schreiben auf den Speicher (beim fsync) + + + + Error writing first block (partition table) + Fehler beim Schreiben des ersten Blocks (Partitionstabelle) + + + + Error reading from storage.<br>SD card may be broken. + Fehler beim Lesen vom Speicher.<br>SD-Karte könnte defekt sein. + + + + Verifying write failed. Contents of SD card is different from what was written to it. + Überprüfung des Schreibvorgangs fehlgeschlagen. Inhalt der SD-Karte stimmt nicht mit dem überein, was darauf geschrieben wurde. + + + + Customizing image + Bild anpassen + + + + DriveFormatThread + + + + + Error partitioning: %1 + Fehler beim Partitionieren: %1 + + + + Error starting fat32format + Fehler beim Starten von fat32format + + + + Error running fat32format: %1 + Fehler beim Ausführen von fat32format: %1 + + + + Error determining new drive letter + Fehler beim Bestimmen des neuen Laufwerkbuchstabens + + + + Invalid device: %1 + Ungültiges Gerät: %1 + + + + Error formatting (through udisks2) + Fehler beim Formatieren (über udisks2) + + + + Elevated privileges needed to properly format drive. + Erhöhte Berechtigungen erforderlich, um das Laufwerk ordnungsgemäß zu formatieren. + + + + Error starting sfdisk + Fehler beim Starten von sfdisk + + + + Partitioning did not create expected FAT partition %1 + Partitionierung hat die erwartete FAT-Partition %1 nicht erstellt + + + + Error starting mkfs.fat + Fehler beim Starten von mkfs.fat + + + + Error running mkfs.fat: %1 + Fehler beim Ausführen von mkfs.fat: %1 + + + + Formatting not implemented for this platform + Formatierung für diese Plattform nicht implementiert + + + + ImageWriter + + + Storage capacity is not large enough.<br>Needs to be at least %1 GB. + Speicherkapazität reicht nicht aus.<br>Muss mindestens %1 GB betragen. + + + + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. + Eingabedatei ist kein gültiges Disk-Image.<br>Dateigröße %1 Bytes ist kein Vielfaches von 512 Bytes. + + + + Downloading and writing image + Herunterladen und Schreiben des Images + + + + Select image + Bild auswählen + + + + Error synchronizing time. Trying again in 3 seconds + Fehler beim Synchronisieren der Zeit. Erneuter Versuch in 3 Sekunden + + + + STP is enabled on your Ethernet switch. Getting IP will take long time. + STP ist auf Ihrem Ethernet-Switch aktiviert. Das Abrufen der IP wird lange dauern. + + + + Would you like to prefill the wifi password from the system keychain? + Möchten Sie das WLAN-Passwort aus dem System-Schlüsselbund vorab ausfüllen? + + + + LocalFileExtractThread + + + opening image file + Bilddatei wird geöffnet + + + + Error opening image file + Fehler beim Öffnen der Bilddatei + + + + MsgPopup + + + NO + NEIN + + + + YES + JA + + + + CONTINUE + FORTSETZEN + + + + QUIT + BEENDEN + + + + OptionsPopup + + + OS Customization + Betriebssystem-Anpassung + + + + General + Allgemein + + + + Services + Dienste + + + + Options + Optionen + + + + Set hostname: + Hostname festlegen: + + + + Set username and password + Benutzername und Passwort festlegen + + + + Username: + Benutzername: + + + + + Password: + Passwort: + + + + Configure wireless LAN + Drahtloses LAN konfigurieren + + + + SSID: + SSID: + + + + Show password + Passwort anzeigen + + + + Hidden SSID + Versteckte SSID + + + + Wireless LAN country: + Drahtloses LAN Land: + + + + Set locale settings + Regionale Einstellungen festlegen + + + + Time zone: + Zeitzone: + + + + Keyboard layout: + Tastaturlayout: + + + + Enable SSH + SSH aktivieren + + + + Use password authentication + Passwortauthentifizierung verwenden + + + + Allow public-key authentication only + Nur die Authentifizierung mit öffentlichem Schlüssel zulassen + + + + Set authorized_keys for '%1': + authorized_keys für '%1' festlegen: + + + + RUN SSH-KEYGEN + SSH-KEYGEN AUSFÜHREN + + + + Play sound when finished + Ton abspielen, wenn abgeschlossen + + + + Eject media when finished + Medien beim Abschluss auswerfen + + + + SAVE + SPAREN + + + + QObject + + + Internal SD card reader + Interner SD-Kartenleser + + + + UnraidOptionsPopup + + + Settings + Einstellungen + + + + DHCP + DHCP + + + + Static IP + Statische IP + + + + CONTINUE + FORTSETZEN + + + + UseSavedSettingsPopup + + + Use OS customization? + OS-Anpassung verwenden? + + + + Would you like to apply OS customization settings? + Möchten Sie die Einstellungen zur OS-Anpassung anwenden? + + + + EDIT SETTINGS + + + + + NO, CLEAR SETTINGS + + + + + YES + + + + + NO + + + + + main + + + Unraid USB Creator v%1 + + + + + Help + + + + + + Device + + + + + CHOOSE DEVICE + MODELL WÄHLEN + + + + Select this button to choose your target device + + + + + + Operating System + + + + + + CHOOSE OS + OS WÄHLEN + + + + Select this button to change the operating system + + + + + + Storage + + + + + + + CHOOSE STORAGE + SD-KARTE WÄHLEN + + + + Select this button to change the destination storage device + + + + + CANCEL WRITE + + + + + + Cancelling... + + + + + CANCEL VERIFY + + + + + + + Finalizing... + + + + + Next + + + + + Select this button to start writing the image + + + + + Using custom repository: %1 + + + + + Network not ready yet + + + + + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists + + + + + Language: + + + + + Keyboard: + + + + + Info + + + + + Select Language + + + + + [ All ] + + + + + Back + + + + + Go back to main menu + + + + + Released: %1 + + + + + Cached on your computer + + + + + Local file + + + + + Online - %1 GB download + + + + + No storage devices found + + + + + Mounted as %1 + + + + + Mounted as %1 + + + + + [WRITE PROTECTED] + + + + + About + + + + + License, Credits, and History: + + + + + Help / Feedback: + + + + + Are you sure you want to quit? + + + + + Unraid USB Creator is still busy.<br>Are you sure you want to quit? + + + + + Warning + + + + + Preparing to write... + + + + + All existing data on '%1' will be erased.<br>Are you sure you want to continue? + + + + + Update available + + + + + There is a newer version of Unraid USB Creator available.<br>Would you like to visit the website to download it? + + + + + Writing... %1% + + + + + Verifying... %1% + + + + + Preparing to write... (%1) + + + + + Error + + + + + Write Successful + + + + + + Erase + + + + + <b>%1</b> has been erased.<br><br>Your drive has been ejected, you can now safely remove it. + + + + + <b>%1</b> has been written to <b>%2</b>. + + + + + <br><br>If you would like to enable legacy boot (bios), helpful for old hardware, please run the 'make_bootable_(mac/linux/windows)' script from this computer, located in the main folder of the UNRAID flash drive. + + + + + Error parsing os_list.json + + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + + + + + + Selected device cannot be used to create an Unraid USB due to its invalid GUID. + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + + + + + Format USB Drive as FAT32 + + + + + Use custom + + + + + Select an Unraid .zip file from your computer + + + + diff --git a/src/i18n/unraid-usb-creator_en.ts b/src/i18n/unraid-usb-creator_en.ts new file mode 100644 index 000000000..001706319 --- /dev/null +++ b/src/i18n/unraid-usb-creator_en.ts @@ -0,0 +1,799 @@ + + + + + DownloadExtractThread + + + + Error extracting archive: %1 + + + + + Error mounting FAT32 partition + + + + + Operating system did not mount FAT32 partition + + + + + Error changing to directory '%1' + + + + + DownloadThread + + + unmounting drive + + + + + opening drive + + + + + Error running diskpart: %1 + + + + + Error removing existing partitions + + + + + Authentication cancelled + + + + + Error running authopen to gain access to disk device '%1' + + + + + Please verify if 'Unraid USB Creator' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). + + + + + Cannot open storage device '%1'. + + + + + discarding existing data on drive + + + + + zeroing out first and last MB of drive + + + + + Write error while zero'ing out MBR + + + + + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). + + + + + starting download + + + + + Error downloading: %1 + + + + + Access denied error while writing file to disk. + + + + + Controlled Folder Access seems to be enabled. Please add both unraid-usb-creator.exe and fat32format.exe to the list of allowed apps and try again. + + + + + Error writing file to disk + + + + + Download corrupt. Hash does not match + + + + + + Error writing to storage (while flushing) + + + + + + Error writing to storage (while fsync) + + + + + Error writing first block (partition table) + + + + + Error reading from storage.<br>SD card may be broken. + + + + + Verifying write failed. Contents of SD card is different from what was written to it. + + + + + Customizing image + + + + + DriveFormatThread + + + + + Error partitioning: %1 + + + + + Error starting fat32format + + + + + Error running fat32format: %1 + + + + + Error determining new drive letter + + + + + Invalid device: %1 + + + + + Error formatting (through udisks2) + + + + + Elevated privileges needed to properly format drive. + + + + + Error starting sfdisk + + + + + Partitioning did not create expected FAT partition %1 + + + + + Error starting mkfs.fat + + + + + Error running mkfs.fat: %1 + + + + + Formatting not implemented for this platform + + + + + ImageWriter + + + Storage capacity is not large enough.<br>Needs to be at least %1 GB. + + + + + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. + + + + + Downloading and writing image + + + + + Select image + + + + + Error synchronizing time. Trying again in 3 seconds + + + + + STP is enabled on your Ethernet switch. Getting IP will take long time. + + + + + Would you like to prefill the wifi password from the system keychain? + + + + + LocalFileExtractThread + + + opening image file + + + + + Error opening image file + + + + + MsgPopup + + + NO + + + + + YES + + + + + CONTINUE + + + + + QUIT + + + + + OptionsPopup + + + OS Customization + + + + + General + + + + + Services + + + + + Options + + + + + Set hostname: + + + + + Set username and password + + + + + Username: + + + + + + Password: + + + + + Configure wireless LAN + + + + + SSID: + + + + + Show password + + + + + Hidden SSID + + + + + Wireless LAN country: + + + + + Set locale settings + + + + + Time zone: + + + + + Keyboard layout: + + + + + Enable SSH + + + + + Use password authentication + + + + + Allow public-key authentication only + + + + + Set authorized_keys for '%1': + + + + + RUN SSH-KEYGEN + + + + + Play sound when finished + + + + + Eject media when finished + + + + + SAVE + + + + + QObject + + + Internal SD card reader + + + + + UnraidOptionsPopup + + + Settings + + + + + DHCP + + + + + Static IP + + + + + CONTINUE + + + + + UseSavedSettingsPopup + + + Use OS customization? + + + + + Would you like to apply OS customization settings? + + + + + EDIT SETTINGS + + + + + NO, CLEAR SETTINGS + + + + + YES + + + + + NO + + + + + main + + + Unraid USB Creator v%1 + + + + + Help + + + + + + Device + + + + + CHOOSE DEVICE + + + + + Select this button to choose your target device + + + + + + Operating System + + + + + + CHOOSE OS + + + + + Select this button to change the operating system + + + + + + Storage + + + + + + + CHOOSE STORAGE + + + + + Select this button to change the destination storage device + + + + + CANCEL WRITE + + + + + + Cancelling... + + + + + CANCEL VERIFY + + + + + + + Finalizing... + + + + + Next + + + + + Select this button to start writing the image + + + + + Using custom repository: %1 + + + + + Network not ready yet + + + + + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists + + + + + Language: + + + + + Keyboard: + + + + + Info + + + + + Select Language + + + + + [ All ] + + + + + Back + + + + + Go back to main menu + + + + + Released: %1 + + + + + Cached on your computer + + + + + Local file + + + + + Online - %1 GB download + + + + + No storage devices found + + + + + Mounted as %1 + + + + + Mounted as %1 + + + + + [WRITE PROTECTED] + + + + + About + + + + + License, Credits, and History: + + + + + Help / Feedback: + + + + + Are you sure you want to quit? + + + + + Unraid USB Creator is still busy.<br>Are you sure you want to quit? + + + + + Warning + + + + + Preparing to write... + + + + + All existing data on '%1' will be erased.<br>Are you sure you want to continue? + + + + + Update available + + + + + There is a newer version of Unraid USB Creator available.<br>Would you like to visit the website to download it? + + + + + Writing... %1% + + + + + Verifying... %1% + + + + + Preparing to write... (%1) + + + + + Error + + + + + Write Successful + + + + + + Erase + + + + + <b>%1</b> has been erased.<br><br>Your drive has been ejected, you can now safely remove it. + + + + + <b>%1</b> has been written to <b>%2</b>. + + + + + <br><br>If you would like to enable legacy boot (bios), helpful for old hardware, please run the 'make_bootable_(mac/linux/windows)' script from this computer, located in the main folder of the UNRAID flash drive. + + + + + Error parsing os_list.json + + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + + + + + + Selected device cannot be used to create an Unraid USB due to its invalid GUID. + + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + + + + + Format USB Drive as FAT32 + + + + + Use custom + + + + + Select an Unraid .zip file from your computer + + + + diff --git a/src/i18n/unraid-usb-creator_es.ts b/src/i18n/unraid-usb-creator_es.ts new file mode 100644 index 000000000..cfbb42996 --- /dev/null +++ b/src/i18n/unraid-usb-creator_es.ts @@ -0,0 +1,795 @@ + + + + + DownloadExtractThread + + + + Error extracting archive: %1 + Error extrayendo el archivo: %1 + + + + Error mounting FAT32 partition + Error montando la partición FAT32 + + + + Operating system did not mount FAT32 partition + El sistema operativo no montó la partición FAT32 + + + + Error changing to directory '%1' + Error cambiando al directorio '%1' + + + + DownloadThread + + + Unmounting drive + Desmontando unidad + + + + Opening drive + Abriendo unidad + + + + Error running diskpart: %1 + Error ejecutando diskpart: %1 + + + + Error removing existing partitions + Error eliminando las particiones existentes + + + + Authentication cancelled + Autenticación cancelada + + + + Error running authopen to gain access to disk device '%1' + Error ejecutando authopen para acceder al dispositivo de disco '%1' + + + + Please verify if 'Unraid USB Creator' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). + Por favor, compruebe si 'Unraid USB Creator' tiene permitido el acceso a 'volúmenes extraíbles' en los ajustes de privacidad (en 'archivos y carpetas' o alternativamente dele 'acceso total al disco'). + + + + Cannot open storage device '%1'. + No se puede abrir el dispositivo de almacenamiento '1'. + + + + Discarding existing data on drive + Descartando datos existentes en la unidad + + + + Zeroing out first and last MB of drive + Poniendo a cero el primer y el último MB de la unidad + + + + Write error while zero'ing out MBR + Error de escritura al poner a cero MBR + + + + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). + Error de escritura al intentar poner a cero la última parte de la tarjeta.<br>La tarjeta podría estar anunciando una capacidad incorrecta (posible falsificación). + + + + Starting download + Iniciando descarga + + + + Error downloading: %1 + Error descargando: %1 + + + + Access denied error while writing file to disk. + Error de acceso denegado escribiendo el archivo en el disco. + + + + Controlled Folder Access seems to be enabled. Please add both unraid-usb-creator.exe and fat32format.exe to the list of allowed apps and try again. + El acceso controlado a carpetas parece estar activado. Añada rpi-imager.exe y fat32format.exe a la lista de aplicaciones permitidas y vuelva a intentarlo. + + + + Error writing file to disk + Error escribiendo el archivo en el disco + + + + Download corrupt. Hash does not match + Descarga corrupta. El hash no coincide + + + + + Error writing to storage (while flushing) + Error escribiendo en la memoria (durante la limpieza) + + + + + Error writing to storage (while fsync) + Error escribiendo en el almacenamiento (mientras fsync) + + + + Error writing first block (partition table) + Error escribiendo el primer bloque (tabla de particiones) + + + + Error reading from storage.<br>SD card may be broken. + Error leyendo del almacenamiento.<br>La tarjeta SD puede estar rota. + + + + Verifying write failed. Contents of SD card is different from what was written to it. + Error verificando la escritura. El contenido de la tarjeta SD es diferente del que se escribió en ella. + + + + Customizing image + Personalizando imagen + + + + DriveFormatThread + + + + + Error partitioning: %1 + Error particionando: %1 + + + + Error starting fat32format + Error iniciando fat32format + + + + Error running fat32format: %1 + Error ejecutando fat32format: %1 + + + + Error determining new drive letter + Error determinando la nueva letra de unidad + + + + Invalid device: %1 + Dispositivo no válido: %1 + + + + Error formatting (through udisks2) + Error formateando (a través de udisks2) + + + + Elevated privileges needed to properly format drive. + Se necesitan privilegios elevados para formatear correctamente la unidad. + + + + Error starting sfdisk + Error iniciando sfdisk + + + + Partitioning did not create expected FAT partition %1 + El particionado no creó la partición FAT %1 esperada + + + + Error starting mkfs.fat + Error iniciando mkfs.fat + + + + Error running mkfs.fat: %1 + Error ejecutando mkfs.fat: %1 + + + + Formatting not implemented for this platform + Formateo no implementado para esta plataforma + + + + ImageWriter + + + Storage capacity is not large enough.<br>Needs to be at least %1 GB. + La capacidad de almacenamiento no es lo suficientemente grande.<br>Necesita ser de al menos %1 GB. + + + + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. + El archivo de entrada no es una imagen de disco válida.<br>El tamaño del archivo %1 bytes no es múltiplo de 512 bytes. + + + + Downloading and writing image + Descargando y escribiendo imagen + + + + Select image + Seleccionar imagen + + + + Error synchronizing time. Trying again in 3 seconds + Error al sincronizar la hora. Intentaré nuevamente en 3 segundos + + + + STP is enabled on your Ethernet switch. Getting IP will take long time. + STP está habilitado en su switch Ethernet. Obtener IP llevará mucho tiempo. + + + + Would you like to prefill the wifi password from the system keychain? + ¿Desea rellenar previamente la contraseña wifi desde el llavero del sistema? + + + + LocalFileExtractThread + + + Opening image file + Abriendo archivo de imagen + + + + Error opening image file + Error abriendo archivo de imagen + + + + MsgPopup + + + NO + NO + + + + YES + + + + + CONTINUE + CONTINUAR + + + + QUIT + SALIR + + + + OptionsPopup + + + OS Customization + Personalización del SO + + + + General + General + + + + Services + Servicios + + + + Options + Opciones + + + + Set hostname: + Establecer nombre de anfitrión: + + + + Set username and password + Establecer nombre de usuario y contraseña + + + + Username: + Nombre de usuario: + + + + + Password: + Contraseña: + + + + Configure wireless LAN + Configurar LAN inalámbrica + + + + SSID: + SSID: + + + + Show password + Mostrar contraseña + + + + Hidden SSID + SSID oculta + + + + Wireless LAN country: + País de LAN inalámbrica: + + + + Set locale settings + Establecer ajustes regionales + + + + Time zone: + Zona horaria: + + + + Keyboard layout: + Distribución del teclado: + + + + Enable SSH + Activar SSH + + + + Use password authentication + Usar autenticación por contraseña + + + + Allow public-key authentication only + Permitir solo la autenticación de clave pública + + + + Set authorized_keys for '%1': + Establecer authorized_keys para '%1': + + + + RUN SSH-KEYGEN + EJECUTAR SSH-KEYGEN + + + + Play sound when finished + Reproducir sonido al finalizar + + + + Eject media when finished + Expulsar soporte al finalizar + + + + SAVE + GUARDAR + + + + QObject + + + Internal SD card reader + Lector de tarjetas SD interno + + + + UnraidOptionsPopup + + + Settings + Ajustes + + + + DHCP + DHCP + + + + Static IP + IP estática + + + + CONTINUE + CONTINUAR + + + + UseSavedSettingsPopup + + + Use OS customization? + ¿Usar la personalización del SO? + + + + Would you like to apply OS customization settings? + ¿Desea aplicar los ajustes de personalización del SO? + + + + EDIT SETTINGS + EDITAR AJUSTES + + + + NO, CLEAR SETTINGS + NO, BORRAR AJUSTES + + + + YES + + + + + NO + NO + + + + main + + + Unraid USB Creator v%1 + Creador USB Unraid v%1 + + + + Help + Ayuda + + + + + Device + Dispositivo + + + + CHOOSE DEVICE + ELEGIR DISPOSITIVO + + + + Select this button to choose your target device + Seleccione este botón para elegir su dispositivo de destino + + + + + Operating System + Sistema Operativo + + + + + CHOOSE OS + ELEGIR SO + + + + Select this button to change the operating system + Seleccione este botón para cambiar el sistema operativo + + + + + Storage + Almacenamiento + + + + + + CHOOSE STORAGE + ELEGIR ALMACENAMIENTO + + + + Select this button to change the destination storage device + Seleccione este botón para cambiar el dispositivo de almacenamiento de destino + + + + CANCEL WRITE + CANCELAR ESCRITURA + + + + + Cancelling... + Cancelado... + + + + CANCEL VERIFY + Cancelando... + + + + + + Finalizing... + Finalizando... + + + + Next + Siguiente + + + + Select this button to start writing the image + Seleccione este botón para empezar a escribir la imagen + + + + Using custom repository: %1 + Usando repositorio personalizado: %1 + + + + Network not ready yet + La red no está lista todavía + + + + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists + Navegación por teclado: <tab> navegar al botón siguiente <space> pulsar botón/seleccionar elemento <arrow up/down> subir/bajar en listas + + + + Language: + Idioma: + + + + Keyboard: + Teclado: + + + + Info + Información + + + + Select Language + Seleccionar Idioma + + + + [ All ] + [ Todos ] + + + + Back + Volver + + + + Go back to main menu + Volver al menú principal + + + + Released: %1 + Publicado: %1 + + + + Cached on your computer + En caché en su ordenador + + + + Local file + Archivo local + + + + Online - %1 GB download + En línea - %1 GB descarga + + + + No storage devices found + No se encontraron dispositivos de almacenamiento + + + + + Mounted as %1 + Montado como %1 + + + + [WRITE PROTECTED] + [PROTEGIDO CONTRA ESCRITURA] + + + + About + Más información + + + + License, Credits, and History: + Licencia, Créditos e Historia: + + + + Help / Feedback: + Ayuda / Comentarios + + + + Are you sure you want to quit? + ¿Está seguro de que quiere salir? + + + + Unraid USB Creator is still busy.<br>Are you sure you want to quit? + Unraid USB Creator sigue ocupado.<br>¿Está seguro de que quiere salir? + + + + Warning + Advertencia + + + + Preparing to write... + Preparando para escribir... + + + + All existing data on '%1' will be erased.<br>Are you sure you want to continue? + Se borrarán todos los datos existentes en '%1'.<br>¿Está seguro de que desea continuar? + + + + Update available + Actualización disponible + + + + There is a newer version of Unraid USB Creator available.<br>Would you like to visit the website to download it? + Hay una versión más reciente de Unraid USB Creator disponible.<br>¿Desea visitar el sitio web para descargarla? + + + + Writing... %1% + Escribiendo... %1% + + + + Verifying... %1% + Verificando... %1% + + + + Preparing to write... (%1) + Preparando para escribir... (%1) + + + + Error + Error + + + + Write Successful + Escritura exitosa + + + + + Erase + Borrar + + + + <b>%1</b> has been erased.<br><br>Your drive has been ejected, you can now safely remove it. + <b>%1</b> ha sido borrado.<br><br>Su unidad ha sido expulsada, ahora puede extraerla de forma segura. + + + + <b>%1</b> has been written to <b>%2</b>. + <b>%1</b> se ha escrito en <b>%2</b>. + + + + <br><br>If you would like to enable legacy boot (bios), helpful for old hardware, please run the 'make_bootable_(mac/linux/windows)' script from this computer, located in the main folder of the UNRAID flash drive. + <br><br>Si desea habilitar el Legacy Boot (bios), útil para hardware antiguo, ejecute el script 'make_bootable_(mac/linux/windows)' desde esta computadora, ubicado en la carpeta principal de la unidad flash UNRAID. + + + + Error parsing os_list.json + Error al parsear os_list.json + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Conecte primero una memoria USB que contenga imágenes.<br>Las imágenes deben estar ubicadas en la carpeta raíz de la memoria USB. + + + + + Selected device cannot be used to create an Unraid USB due to its invalid GUID. + El dispositivo seleccionado no se puede utilizar para crear un USB Unraid debido a su GUID no válido. + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + La tarjeta SD está protegida contra escritura. Empuje el interruptor de bloqueo en el lado izquierdo de la tarjeta hacia arriba y vuelva a intentarlo. + + + + Format USB Drive as FAT32 + Formatear tarjeta como FAT32 + + + + Use custom + Usar personalizado + + + + Select an Unraid .zip file from your computer + Seleccione un archivo .zip de Unraid de su computadora + + + diff --git a/src/i18n/unraid-usb-creator_fr.ts b/src/i18n/unraid-usb-creator_fr.ts new file mode 100644 index 000000000..4a4dc419c --- /dev/null +++ b/src/i18n/unraid-usb-creator_fr.ts @@ -0,0 +1,803 @@ + + + + + DownloadExtractThread + + + + Error extracting archive: %1 + Erreur lors de l'extraction de l'archive : %1 + + + + Error mounting FAT32 partition + Erreur lors du montage de la partition FAT32 + + + + Operating system did not mount FAT32 partition + Le système d'exploitation n'a pas monté la partition FAT32 + + + + Error changing to directory '%1' + Erreur lors du changement de répertoire vers '%1' + + + + DownloadThread + + + unmounting drive + déconnexion du disque + + + + opening drive + ouverture du disque + + + + Error running diskpart: %1 + Erreur lors de l'exécution de diskpart : %1 + + + + Error removing existing partitions + Erreur lors de la suppression des partitions existantes + + + + Authentication cancelled + Authentification annulée + + + + Error running authopen to gain access to disk device '%1' + Erreur lors de l'exécution d'authopen pour accéder au périphérique disque '%1' + + + + Please verify if 'Unraid USB Creator' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). + Veuillez vérifier si 'Unraid USB Creator' a accès aux 'volumes amovibles' dans les paramètres de confidentialité (sous 'fichiers et dossiers' ou donner un 'accès complet au disque'). + + + + Cannot open storage device '%1'. + Impossible d'ouvrir le périphérique de stockage '%1'. + + + + discarding existing data on drive + suppression des données existantes sur le disque + + + + zeroing out first and last MB of drive + remise à zéro du premier et du dernier Mo du disque + + + + Write error while zero'ing out MBR + Erreur d'écriture lors de la remise à zéro du MBR + + + + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). + Erreur d'écriture lors de la remise à zéro de la dernière partie de la carte.<br>La carte pourrait indiquer une capacité incorrecte (possiblement contrefaite). + + + + starting download + début du téléchargement + + + + Error downloading: %1 + Erreur lors du téléchargement : %1 + + + + Access denied error while writing file to disk. + Erreur d'accès refusé lors de l'écriture du fichier sur le disque. + + + + Controlled Folder Access seems to be enabled. Please add both unraid-usb-creator.exe and fat32format.exe to the list of allowed apps and try again. + L'accès contrôlé aux dossiers semble être activé. Veuillez ajouter unraid-usb-creator.exe et fat32format.exe à la liste des applications autorisées et réessayer. + + + + Error writing file to disk + Erreur lors de l'écriture du fichier sur le disque + + + + Download corrupt. Hash does not match + Téléchargement corrompu. Le hash ne correspond pas + + + + + Error writing to storage (while flushing) + Erreur lors de l'écriture sur le support (lors du vidage) + + + + + Error writing to storage (while fsync) + Erreur lors de l'écriture sur le support (lors de la synchronisation fsync) + + + + Error writing first block (partition table) + Erreur lors de l'écriture du premier bloc (table de partition) + + + + Error reading from storage.<br>SD card may be broken. + Erreur de lecture sur le support.<br>La carte SD peut être endommagée. + + + + Verifying write failed. Contents of SD card is different from what was written to it. + Échec de la vérification de l'écriture. Le contenu de la carte SD est différent de ce qui a été écrit. + + + + Customizing image + Personnalisation de l'image + + + + DriveFormatThread + + + + + Error partitioning: %1 + Erreur de partitionnement : %1 + + + + Error starting fat32format + Erreur lors du démarrage de fat32format + + + + Error running fat32format: %1 + Erreur lors de l'exécution de fat32format : %1 + + + + Error determining new drive letter + Erreur lors de la détermination de la nouvelle lettre de lecteur + + + + Invalid device: %1 + Périphérique invalide : %1 + + + + Error formatting (through udisks2) + Erreur lors du formatage (via udisks2) + + + + Elevated privileges needed to properly format drive. + Des privilèges élevés sont nécessaires pour formater correctement le disque. + + + + Error starting sfdisk + Erreur lors du démarrage de sfdisk + + + + Partitioning did not create expected FAT partition %1 + Le partitionnement n'a pas créé la partition FAT attendue %1 + + + + Error starting mkfs.fat + Erreur lors du démarrage de mkfs.fat + + + + Error running mkfs.fat: %1 + Erreur lors de l'exécution de mkfs.fat : %1 + + + + Formatting not implemented for this platform + Le formatage n'est pas implémenté pour cette plateforme + + + + + ImageWriter + + + Storage capacity is not large enough.<br>Needs to be at least %1 GB. + La capacité de stockage n'est pas suffisante.<br>Elle doit être d'au moins %1 Go. + + + + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. + Le fichier d'entrée n'est pas une image disque valide.<br>La taille du fichier %1 octets n'est pas un multiple de 512 octets. + + + + Downloading and writing image + Téléchargement et écriture de l'image + + + + Select image + Sélectionner l'image + + + + Error synchronizing time. Trying again in 3 seconds + Erreur de synchronisation de l'heure. Nouvelle tentative dans 3 secondes + + + + STP is enabled on your Ethernet switch. Getting IP will take long time. + STP est activé sur votre switch Ethernet. L'obtention de l'IP prendra du temps. + + + + Would you like to prefill the wifi password from the system keychain? + Souhaitez-vous pré-remplir le mot de passe wifi à partir du trousseau de clés du système ? + + + + + LocalFileExtractThread + + + opening image file + ouverture du fichier image + + + + Error opening image file + Erreur lors de l'ouverture du fichier image + + + + + MsgPopup + + + NO + NON + + + + YES + OUI + + + + CONTINUE + CONTINUER + + + + QUIT + QUITTER + + + + + OptionsPopup + + + OS Customization + Personnalisation du système d'exploitation + + + + General + Général + + + + Services + Services + + + + Options + Options + + + + Set hostname: + Définir le nom d'hôte : + + + + Set username and password + Définir un nom d'utilisateur et un mot de passe + + + + Username: + Nom d'utilisateur : + + + + + Password: + Mot de passe : + + + + Configure wireless LAN + Configurer le réseau sans fil (LAN) + + + + SSID: + SSID : + + + + Show password + Afficher le mot de passe + + + + Hidden SSID + SSID masqué + + + + Wireless LAN country: + Pays du réseau sans fil : + + + + Set locale settings + Définir les paramètres de localisation + + + + Time zone: + Fuseau horaire : + + + + Keyboard layout: + Disposition du clavier : + + + + Enable SSH + Activer SSH + + + + Use password authentication + Utiliser l'authentification par mot de passe + + + + Allow public-key authentication only + Autoriser uniquement l'authentification par clé publique + + + + Set authorized_keys for '%1': + Définir les clés autorisées pour '%1' : + + + + RUN SSH-KEYGEN + EXÉCUTER SSH-KEYGEN + + + + Play sound when finished + Jouer un son lorsque terminé + + + + Eject media when finished + Éjecter le support lorsque terminé + + + + SAVE + ENREGISTRER + + + + QObject + + + Internal SD card reader + Lecteur de carte SD interne + + + + UnraidOptionsPopup + + + Settings + Paramètres + + + + DHCP + DHCP + + + + Static IP + IP statique + + + + CONTINUE + CONTINUER + + + + UseSavedSettingsPopup + + + Use OS customization? + Utiliser la personnalisation du système d'exploitation ? + + + + Would you like to apply OS customization settings? + Souhaitez-vous appliquer les paramètres de personnalisation du système d'exploitation ? + + + + EDIT SETTINGS + MODIFIER LES PARAMÈTRES + + + + NO, CLEAR SETTINGS + NON, EFFACER LES PARAMÈTRES + + + + YES + OUI + + + + NO + NON + + + + main + + + Unraid USB Creator v%1 + Créateur USB Unraid v%1 + + + + Help + Aide + + + + + Device + Appareil + + + + CHOOSE DEVICE + CHOISIR L'APPAREIL + + + + Select this button to choose your target device + Sélectionnez ce bouton pour choisir votre appareil cible + + + + + Operating System + Système d'exploitation + + + + + CHOOSE OS + CHOISIR L'OS + + + + Select this button to change the operating system + Sélectionnez ce bouton pour changer le système d'exploitation + + + + + Storage + Stockage + + + + + + CHOOSE STORAGE + CHOISIR LE STOCKAGE + + + + Select this button to change the destination storage device + Sélectionnez ce bouton pour changer l'appareil de stockage de destination + + + + CANCEL WRITE + ANNULER L'ÉCRITURE + + + + + Cancelling... + Annulation... + + + + CANCEL VERIFY + ANNULER LA VÉRIFICATION + + + + + + Finalizing... + Finalisation... + + + + Next + Suivant + + + + Select this button to start writing the image + Sélectionnez ce bouton pour commencer à écrire l'image + + + + Using custom repository: %1 + Utilisation du dépôt personnalisé : %1 + + + + Network not ready yet + Réseau pas encore prêt + + + + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists + Navigation au clavier : <tab> naviguer vers le bouton suivant <espace> appuyer sur le bouton/sélectionner l'élément <flèche haut/bas> monter/descendre dans les listes + + + + Language: + Langue : + + + + Keyboard: + Clavier : + + + + Info + Info + + + + Select Language + Sélectionner la langue + + + + [ All ] + [ Tout ] + + + + Back + Retour + + + + Go back to main menu + Retourner au menu principal + + + + Released: %1 + Publié : %1 + + + + Cached on your computer + Mis en cache sur votre ordinateur + + + + Local file + Fichier local + + + + Online - %1 GB download + En ligne - %1 Go à télécharger + + + + No storage devices found + Aucun périphérique de stockage trouvé + + + + Mounted as %1 + Monté en tant que %1 + + + + Mounted as %1 + Monté en tant que %1 + + + + [WRITE PROTECTED] + [ÉCRITURE PROTÉGÉE] + + + + About + À propos + + + + License, Credits, and History: + Licence, Crédits et Historique : + + + + Help / Feedback: + Aide / Retour : + + + + Are you sure you want to quit? + Êtes-vous sûr de vouloir quitter ? + + + + Unraid USB Creator is still busy.<br>Are you sure you want to quit? + Unraid USB Creator est encore occupé.<br>Êtes-vous sûr de vouloir quitter ? + + + + Warning + Avertissement + + + + Preparing to write... + Préparation à l'écriture... + + + + All existing data on '%1' will be erased.<br>Are you sure you want to continue? + Toutes les données existantes sur '%1' seront effacées.<br>Êtes-vous sûr de vouloir continuer ? + + + + Update available + Mise à jour disponible + + + + There is a newer version of Unraid USB Creator available.<br>Would you like to visit the website to download it? + Une nouvelle version de Unraid USB Creator est disponible.<br>Souhaitez-vous visiter le site pour la télécharger ? + + + + Writing... %1% + Écriture... %1% + + + + Verifying... %1% + Vérification... %1% + + + + Preparing to write... (%1) + Préparation à l'écriture... (%1) + + + + Error + Erreur + + + + Write Successful + Écriture réussie + + + + + Erase + Effacer + + + + <b>%1</b> has been erased.<br><br>Your drive has been ejected, you can now safely remove it. + <b>%1</b> a été effacé.<br><br>Votre disque a été éjecté, vous pouvez maintenant le retirer en toute sécurité. + + + + <b>%1</b> has been written to <b>%2</b>. + <b>%1</b> a été écrit sur <b>%2</b>. + + + + <br><br>If you would like to enable legacy boot (bios), helpful for old hardware, please run the 'make_bootable_(mac/linux/windows)' script from this computer, located in the main folder of the UNRAID flash drive. + <br><br>Si vous souhaitez activer le démarrage hérité (bios), utile pour le matériel ancien, veuillez exécuter le script 'make_bootable_(mac/linux/windows)' depuis cet ordinateur, situé dans le dossier principal de la clé USB UNRAID. + + + + Error parsing os_list.json + Erreur lors de l'analyse de os_list.json + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + Connectez d'abord une clé USB contenant des images.<br>Les images doivent être situées dans le dossier racine de la clé USB. + + + + + Selected device cannot be used to create an Unraid USB due to its invalid GUID. + Le périphérique sélectionné ne peut pas être utilisé pour créer un USB Unraid en raison de son GUID invalide. + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + La carte SD est protégée en écriture.<br>Poussez le commutateur de verrouillage sur le côté gauche de la carte vers le haut et réessayez. + + + + Format USB Drive as FAT32 + Formater la clé USB en FAT32 + + + + Use custom + Utiliser personnalisé + + + + Select an Unraid .zip file from your computer + Sélectionnez un fichier .zip Unraid depuis votre ordinateur + + + diff --git a/src/i18n/unraid-usb-creator_zh.ts b/src/i18n/unraid-usb-creator_zh.ts new file mode 100644 index 000000000..2d1537b2d --- /dev/null +++ b/src/i18n/unraid-usb-creator_zh.ts @@ -0,0 +1,801 @@ + + + + + DownloadExtractThread + + + + Error extracting archive: %1 + 提取存档时出错:%1 + + + + Error mounting FAT32 partition + 挂载FAT32分区时出错 + + + + Operating system did not mount FAT32 partition + 操作系统未能挂载FAT32分区 + + + + Error changing to directory '%1' + 切换到目录'%1'时出错 + + + + DownloadThread + + + unmounting drive + 卸载驱动器 + + + + opening drive + 打开驱动器 + + + + Error running diskpart: %1 + 运行diskpart时出错:%1 + + + + Error removing existing partitions + 删除现有分区时出错 + + + + Authentication cancelled + 身份验证已取消 + + + + Error running authopen to gain access to disk device '%1' + 运行authopen以访问磁盘设备'%1'时出错 + + + + Please verify if 'Unraid USB Creator' is allowed access to 'removable volumes' in privacy settings (under 'files and folders' or alternatively give it 'full disk access'). + 请确认“Unraid USB Creator”是否在隐私设置中允许访问“可移动卷”(在“文件和文件夹”下或为其提供“完整磁盘访问”权限)。 + + + + Cannot open storage device '%1'. + 无法打开存储设备'%1'。 + + + + discarding existing data on drive + 丢弃驱动器上的现有数据 + + + + zeroing out first and last MB of drive + 清空驱动器的首末MB + + + + Write error while zero'ing out MBR + 清空MBR时写入错误 + + + + Write error while trying to zero out last part of card.<br>Card could be advertising wrong capacity (possible counterfeit). + 尝试清空卡的最后部分时写入错误。<br>该卡可能显示错误的容量(可能为假冒产品)。 + + + + starting download + 开始下载 + + + + Error downloading: %1 + 下载时出错:%1 + + + + Access denied error while writing file to disk. + 将文件写入磁盘时发生访问拒绝错误。 + + + + Controlled Folder Access seems to be enabled. Please add both unraid-usb-creator.exe and fat32format.exe to the list of allowed apps and try again. + 受控文件夹访问似乎已启用。请将unraid-usb-creator.exe和fat32format.exe添加到允许的应用程序列表中,然后重试。 + + + + Error writing file to disk + 将文件写入磁盘时出错 + + + + Download corrupt. Hash does not match + 下载损坏。哈希值不匹配 + + + + + Error writing to storage (while flushing) + 写入存储时出错(在刷新期间) + + + + + Error writing to storage (while fsync) + 写入存储时出错(在fsync期间) + + + + Error writing first block (partition table) + 写入第一个块(分区表)时出错 + + + + Error reading from storage.<br>SD card may be broken. + 从存储设备读取时出错。<br>SD卡可能已损坏。 + + + + Verifying write failed. Contents of SD card is different from what was written to it. + 写入验证失败。SD卡的内容与写入的内容不同。 + + + + Customizing image + 自定义映像 + + + + DriveFormatThread + + + + + Error partitioning: %1 + 分区错误:%1 + + + + Error starting fat32format + 启动fat32format时出错 + + + + Error running fat32format: %1 + 运行fat32format时出错:%1 + + + + Error determining new drive letter + 确定新驱动器号时出错 + + + + Invalid device: %1 + 无效设备:%1 + + + + Error formatting (through udisks2) + 格式化时出错(通过udisks2) + + + + Elevated privileges needed to properly format drive. + 需要提升权限以正确格式化驱动器。 + + + + Error starting sfdisk + 启动sfdisk时出错 + + + + Partitioning did not create expected FAT partition %1 + 分区未创建预期的FAT分区:%1 + + + + Error starting mkfs.fat + 启动mkfs.fat时出错 + + + + Error running mkfs.fat: %1 + 运行mkfs.fat时出错:%1 + + + + Formatting not implemented for this platform + 此平台未实现格式化功能 + + + + ImageWriter + + + Storage capacity is not large enough.<br>Needs to be at least %1 GB. + 存储容量不足。<br>需要至少%1 GB。 + + + + Input file is not a valid disk image.<br>File size %1 bytes is not a multiple of 512 bytes. + 输入文件不是有效的磁盘映像。<br>文件大小 %1 字节不是512字节的倍数。 + + + + Downloading and writing image + 正在下载并写入映像 + + + + Select image + 选择映像 + + + + Error synchronizing time. Trying again in 3 seconds + 同步时间时出错。3秒后重试 + + + + STP is enabled on your Ethernet switch. Getting IP will take long time. + 您的以太网交换机上启用了STP。获取IP将花费较长时间。 + + + + Would you like to prefill the wifi password from the system keychain? + 是否要从系统钥匙串预填充WiFi密码? + + + + LocalFileExtractThread + + + opening image file + 正在打开映像文件 + + + + Error opening image file + 打开映像文件时出错 + + + + MsgPopup + + + NO + + + + + YES + + + + + CONTINUE + 继续 + + + + QUIT + 退出 + + + + OptionsPopup + + + OS Customization + 操作系统自定义 + + + + General + 常规 + + + + Services + 服务 + + + + Options + 选项 + + + + Set hostname: + 设置主机名: + + + + Set username and password + 设置用户名和密码 + + + + Username: + 用户名: + + + + + Password: + 密码: + + + + Configure wireless LAN + 配置无线局域网 + + + + SSID: + SSID: + + + + Show password + 显示密码 + + + + Hidden SSID + 隐藏SSID + + + + Wireless LAN country: + 无线局域网国家: + + + + Set locale settings + 设置区域设置 + + + + Time zone: + 时区: + + + + Keyboard layout: + 键盘布局: + + + + Enable SSH + 启用SSH + + + + Use password authentication + 使用密码验证 + + + + Allow public-key authentication only + 仅允许公钥验证 + + + + Set authorized_keys for '%1': + 为“%1”设置authorized_keys: + + + + RUN SSH-KEYGEN + 运行SSH-KEYGEN + + + + + + Play sound when finished + 完成时播放声音 + + + + Eject media when finished + 完成时弹出媒体 + + + + SAVE + 保存 + + + +QObject + + + Internal SD card reader + 内置 SD 卡读卡器 + + + +UnraidOptionsPopup + + + Settings + 设置 + + + + DHCP + 动态主机配置协议(DHCP) + + + + Static IP + 静态 IP + + + + CONTINUE + 继续 + + + +UseSavedSettingsPopup + + + Use OS customization? + 使用操作系统自定义设置吗? + + + + Would you like to apply OS customization settings? + 您想要应用操作系统自定义设置吗? + + + + EDIT SETTINGS + 编辑设置 + + + + NO, CLEAR SETTINGS + 不,清除设置 + + + + YES + + + + + NO + + + + +main + + + Unraid USB Creator v%1 + Unraid USB 创建工具 v%1 + + + + Help + 帮助 + + + + + Device + 设备 + + + + CHOOSE DEVICE + 选择设备 + + + + Select this button to choose your target device + 选择此按钮以选择目标设备 + + + + + Operating System + 操作系统 + + + + + CHOOSE OS + 选择操作系统 + + + + Select this button to change the operating system + 选择此按钮以更改操作系统 + + + + + Storage + 存储 + + + + + + CHOOSE STORAGE + 选择存储设备 + + + + Select this button to change the destination storage device + 选择此按钮以更改目标存储设备 + + + + CANCEL WRITE + 取消写入 + + + + + Cancelling... + 正在取消... + + + + CANCEL VERIFY + 取消验证 + + + + + + Finalizing... + 正在完成... + + + + Next + 下一步 + + + + Select this button to start writing the image + 选择此按钮以开始写入镜像 + + + + Using custom repository: %1 + 使用自定义仓库:%1 + + + + Network not ready yet + 网络尚未准备好 + + + + Keyboard navigation: <tab> navigate to next button <space> press button/select item <arrow up/down> go up/down in lists + 键盘导航:<tab> 导航到下一个按钮 <space> 按下按钮/选择项目 <箭头上/下> 在列表中上下移动 + + + + Language: + 语言: + + + + Keyboard: + 键盘: + + + + Info + 信息 + + + + Select Language + 选择语言 + + + + [ All ] + [所有] + + + + Back + 返回 + + + + Go back to main menu + 返回主菜单 + + + + Released: %1 + 发行版本:%1 + + + + Cached on your computer + 已缓存到您的计算机 + + + + Local file + 本地文件 + + + + Online - %1 GB download + 在线 - %1 GB 下载 + + + + No storage devices found + 未找到存储设备 + + + + Mounted as %1 + 挂载为 %1 + + + + Mounted as %1 + 挂载为 %1 + + + + [WRITE PROTECTED] + [写保护] + + + + About + 关于 + + + + License, Credits, and History: + 许可证、鸣谢和历史: + + + + Help / Feedback: + 帮助 / 反馈: + + + + Are you sure you want to quit? + 您确定要退出吗? + + + + Unraid USB Creator is still busy.<br>Are you sure you want to quit? + Unraid USB Creator 仍在忙碌中。<br>您确定要退出吗? + + + + Warning + 警告 + + + + Preparing to write... + 准备写入... + + + + All existing data on '%1' will be erased.<br>Are you sure you want to continue? + 在 '%1' 上的所有现有数据将被擦除。<br>您确定要继续吗? + + + + Update available + 有可用更新 + + + + There is a newer version of Unraid USB Creator available.<br>Would you like to visit the website to download it? + 有更新版本的 Unraid USB Creator 可用。<br>您想访问网站下载吗? + + + + Writing... %1% + 写入中... %1% + + + + Verifying... %1% + 验证中... %1% + + + + Preparing to write... (%1) + 准备写入... (%1) + + + + Error + 错误 + + + + Write Successful + 写入成功 + + + + + Erase + 擦除 + + + + <b>%1</b> has been erased.<br><br>Your drive has been ejected, you can now safely remove it. + <b>%1</b> 已被擦除。<br><br>您的驱动器已弹出,您现在可以安全地移除它。 + + + + <b>%1</b> has been written to <b>%2</b>. + <b>%1</b> 已写入 <b>%2</b>。 + + + + <br><br>If you would like to enable legacy boot (bios), helpful for old hardware, please run the 'make_bootable_(mac/linux/windows)' script from this computer, located in the main folder of the UNRAID flash drive. + <br><br>如果您想启用传统启动(BIOS),这对于旧硬件很有帮助,请从本计算机运行位于 UNRAID 闪存驱动器主文件夹中的 'make_bootable_(mac/linux/windows)' 脚本。 + + + + Error parsing os_list.json + 解析 os_list.json 时出错 + + + + Connect an USB stick containing images first.<br>The images must be located in the root folder of the USB stick. + 首先连接一个包含镜像的 USB 存储设备。<br>镜像必须位于 USB 存储设备的根文件夹中。 + + + + + Selected device cannot be used to create an Unraid USB due to its invalid GUID. + 由于所选设备的 GUID 无效,无法用于创建 Unraid USB。 + + + + SD card is write protected.<br>Push the lock switch on the left side of the card upwards, and try again. + SD 卡被写保护了。<br>请将卡左侧的锁开关向上推,然后重试。 + + + + Format USB Drive as FAT32 + 将 USB 驱动器格式化为 FAT32 + + + + Use custom + 使用自定义选项 + + + + Select an Unraid .zip file from your computer + 从您的计算机中选择一个 Unraid .zip 文件 + + + diff --git a/src/icons/UN-logotype-gradient.png b/src/icons/UN-logotype-gradient.png new file mode 100644 index 000000000..458007d94 Binary files /dev/null and b/src/icons/UN-logotype-gradient.png differ diff --git a/src/icons/UN-logotype-gradient.svg b/src/icons/UN-logotype-gradient.svg new file mode 100644 index 000000000..a66963d9e --- /dev/null +++ b/src/icons/UN-logotype-gradient.svg @@ -0,0 +1 @@ +UN-logotype-gradient \ No newline at end of file diff --git a/src/icons/help.png b/src/icons/help.png new file mode 100644 index 000000000..3bd347159 Binary files /dev/null and b/src/icons/help.png differ diff --git a/src/icons/ic_chevron_left_40px.svg b/src/icons/ic_chevron_left_40px.svg index c6122c74e..8d3b2bc20 100644 --- a/src/icons/ic_chevron_left_40px.svg +++ b/src/icons/ic_chevron_left_40px.svg @@ -1,6 +1,16 @@ + + + - + diff --git a/src/icons/ic_chevron_left_40px_orange.svg b/src/icons/ic_chevron_left_40px_orange.svg new file mode 100644 index 000000000..386bdfea1 --- /dev/null +++ b/src/icons/ic_chevron_left_40px_orange.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/icons/ic_chevron_right_40px.svg b/src/icons/ic_chevron_right_40px.svg index b8996ba8b..19e15e5b0 100644 --- a/src/icons/ic_chevron_right_40px.svg +++ b/src/icons/ic_chevron_right_40px.svg @@ -1,6 +1,16 @@ + + + - + diff --git a/src/icons/ic_chevron_right_40px_orange.svg b/src/icons/ic_chevron_right_40px_orange.svg new file mode 100644 index 000000000..f60653636 --- /dev/null +++ b/src/icons/ic_chevron_right_40px_orange.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/icons/ic_cog_orange.svg b/src/icons/ic_cog_orange.svg new file mode 100644 index 000000000..51a920f0c --- /dev/null +++ b/src/icons/ic_cog_orange.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/src/icons/ic_delete_40px.svg b/src/icons/ic_delete_40px.svg index c2bcbf382..fb7ccb8fc 100644 --- a/src/icons/ic_delete_40px.svg +++ b/src/icons/ic_delete_40px.svg @@ -1,6 +1,16 @@ + + + - + diff --git a/src/icons/ic_delete_40px_orange.svg b/src/icons/ic_delete_40px_orange.svg new file mode 100644 index 000000000..9a7fe293e --- /dev/null +++ b/src/icons/ic_delete_40px_orange.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/icons/ic_sd_storage_40px.svg b/src/icons/ic_sd_storage_40px.svg index ef75ae8e6..3ed7959a3 100644 --- a/src/icons/ic_sd_storage_40px.svg +++ b/src/icons/ic_sd_storage_40px.svg @@ -1,6 +1,16 @@ + + + - + diff --git a/src/icons/ic_sd_storage_40px_orange.svg b/src/icons/ic_sd_storage_40px_orange.svg new file mode 100644 index 000000000..4db71b7c7 --- /dev/null +++ b/src/icons/ic_sd_storage_40px_orange.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/icons/ic_storage_40px.svg b/src/icons/ic_storage_40px.svg index 22f5fc3d0..1638b8c1b 100644 --- a/src/icons/ic_storage_40px.svg +++ b/src/icons/ic_storage_40px.svg @@ -1,6 +1,16 @@ + + + - + diff --git a/src/icons/ic_storage_40px_orange.svg b/src/icons/ic_storage_40px_orange.svg new file mode 100644 index 000000000..2010e478f --- /dev/null +++ b/src/icons/ic_storage_40px_orange.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/icons/ic_usb_40px.svg b/src/icons/ic_usb_40px.svg index 21567e5da..c203475e1 100644 --- a/src/icons/ic_usb_40px.svg +++ b/src/icons/ic_usb_40px.svg @@ -1,6 +1,16 @@ + + + - + diff --git a/src/icons/ic_usb_40px_orange.svg b/src/icons/ic_usb_40px_orange.svg new file mode 100644 index 000000000..832caed96 --- /dev/null +++ b/src/icons/ic_usb_40px_orange.svg @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/src/icons/info.png b/src/icons/info.png new file mode 100644 index 000000000..21a6d1b13 Binary files /dev/null and b/src/icons/info.png differ diff --git a/src/icons/logo_stacked_imager.png b/src/icons/logo_stacked_imager.png deleted file mode 100644 index e5338a4b4..000000000 Binary files a/src/icons/logo_stacked_imager.png and /dev/null differ diff --git a/src/icons/logo_sxs_imager.png b/src/icons/logo_sxs_imager.png deleted file mode 100644 index d479f56ab..000000000 Binary files a/src/icons/logo_sxs_imager.png and /dev/null differ diff --git a/src/icons/rpi-imager.icns b/src/icons/rpi-imager.icns deleted file mode 100644 index 0df2debbf..000000000 Binary files a/src/icons/rpi-imager.icns and /dev/null differ diff --git a/src/icons/rpi-imager.ico b/src/icons/rpi-imager.ico deleted file mode 100644 index ecb494b90..000000000 Binary files a/src/icons/rpi-imager.ico and /dev/null differ diff --git a/src/icons/rpi-imager.png b/src/icons/rpi-imager.png deleted file mode 100644 index 083b0f60b..000000000 Binary files a/src/icons/rpi-imager.png and /dev/null differ diff --git a/src/icons/unraid.icns b/src/icons/unraid.icns new file mode 100644 index 000000000..43b793c5d Binary files /dev/null and b/src/icons/unraid.icns differ diff --git a/src/icons/unraid.ico b/src/icons/unraid.ico new file mode 100644 index 000000000..e96b93b35 Binary files /dev/null and b/src/icons/unraid.ico differ diff --git a/src/icons/unraid128.png b/src/icons/unraid128.png new file mode 100644 index 000000000..6881f32fc Binary files /dev/null and b/src/icons/unraid128.png differ diff --git a/src/imagewriter.cpp b/src/imagewriter.cpp index 063ba7b62..5281b1f42 100644 --- a/src/imagewriter.cpp +++ b/src/imagewriter.cpp @@ -11,7 +11,6 @@ #include "driveformatthread.h" #include "localfileextractthread.h" #include "downloadstatstelemetry.h" -#include "wlancredentials.h" #include #include #include @@ -34,6 +33,7 @@ #include #include #include +#include #ifndef QT_NO_WIDGETS #include #include @@ -63,7 +63,7 @@ ImageWriter::ImageWriter(QObject *parent) : QObject(parent), _repo(QUrl(QString(OSLIST_URL))), _dlnow(0), _verifynow(0), _engine(nullptr), _thread(nullptr), _verifyEnabled(false), _cachingEnabled(false), _embeddedMode(false), _online(false), _customCacheFile(false), _trans(nullptr), - _networkManager(this) + _networkManager(this), _guidValid(false) { connect(&_polltimer, SIGNAL(timeout()), SLOT(pollProgress())); @@ -154,7 +154,7 @@ ImageWriter::ImageWriter(QObject *parent) } _settings.endGroup(); - QDir dir(":/i18n", "rpi-imager_*.qm"); + QDir dir(":/i18n", "unraid-usb-creator_*.qm"); const QStringList transFiles = dir.entryList(); QLocale currentLocale; QStringList localeComponents = currentLocale.name().split('_'); @@ -164,14 +164,25 @@ ImageWriter::ImageWriter(QObject *parent) for (const QString &tf : transFiles) { - QString langcode = tf.mid(11, tf.length()-14); + QString langcode = tf.mid(19, tf.length()-22); /* FIXME: we currently lack a font with support for Chinese characters in embedded mode */ //if (isEmbeddedMode() && langcode == "zh") // continue; QLocale loc(langcode); /* Use "English" for "en" and not "American English" */ - QString langname = (langcode == "en" ? "English" : loc.nativeLanguageName() ); + QString langname = loc.nativeLanguageName(); + + if (langcode == "en") { + langname = "English"; + } + else if (langcode == "es") { + langname = "Español"; + } + else if (langcode == "fr") { + langname = "Français"; + } + _translations.insert(langname, langcode); if (langcode == currentlangcode) { @@ -218,14 +229,15 @@ void ImageWriter::setSrc(const QUrl &url, quint64 downloadLen, quint64 extrLen, } if (url.isLocalFile()) { - _initFormat = "auto"; + _initFormat = "UNRAID"; } } /* Set device to write to */ -void ImageWriter::setDst(const QString &device, quint64 deviceSize) +void ImageWriter::setDst(const QString &device, bool guidValid, quint64 deviceSize) { _dst = device; + _guidValid = guidValid; _devLen = deviceSize; } @@ -243,7 +255,7 @@ void ImageWriter::startWrite() if (_src.toString() == "internal://format") { - DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), this); + DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), "", this); connect(dft, SIGNAL(success()), SLOT(onSuccess())); connect(dft, SIGNAL(error(QString)), SLOT(onError(QString))); dft->start(); @@ -301,8 +313,8 @@ void ImageWriter::startWrite() connect(_thread, SIGNAL(finalizing()), SLOT(onFinalizing())); connect(_thread, SIGNAL(preparationStatusUpdate(QString)), SLOT(onPreparationStatusUpdate(QString))); _thread->setVerifyEnabled(_verifyEnabled); - _thread->setUserAgent(QString("Mozilla/5.0 rpi-imager/%1").arg(constantVersion()).toUtf8()); - _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat); + _thread->setUserAgent(QString("Mozilla/5.0 unraid-usb-creator/%1").arg(constantVersion()).toUtf8()); + _thread->setImageCustomization(_config, _cmdline, _firstrun, _cloudinit, _cloudinitNetwork, _initFormat, getSavedCustomizationSettings()); if (!_expectedHash.isEmpty() && _cachedFileHash != _expectedHash && _cachingEnabled) { @@ -342,7 +354,12 @@ void ImageWriter::startWrite() if (_multipleFilesInZip) { static_cast(_thread)->enableMultipleFileExtraction(); - DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), this); + QString label{""}; + if(_initFormat == "UNRAID") { + // if this is an unraid zip, the volume label needs to be UNRAID for the make bootable script to work as intended + label = "UNRAID"; + } + DriveFormatThread *dft = new DriveFormatThread(_dst.toLatin1(), label, this); connect(dft, SIGNAL(success()), _thread, SLOT(start())); connect(dft, SIGNAL(error(QString)), SLOT(onError(QString))); dft->start(); @@ -609,14 +626,14 @@ QByteArray ImageWriter::getFilteredOSlist() { reference_os_list_array.append(QJsonObject({ {"name", QCoreApplication::translate("main", "Erase")}, - {"description", QCoreApplication::translate("main", "Format card as FAT32")}, + {"description", QCoreApplication::translate("main", "Format USB Drive as FAT32")}, {"icon", "icons/erase.png"}, {"url", "internal://format"}, })); reference_os_list_array.append(QJsonObject({ {"name", QCoreApplication::translate("main", "Use custom")}, - {"description", QCoreApplication::translate("main", "Select a custom .img from your computer")}, + {"description", QCoreApplication::translate("main", "Select an Unraid .zip file from your computer")}, {"icon", "icons/use_custom.png"}, {"url", ""}, })); @@ -801,7 +818,7 @@ void ImageWriter::openFileDialog() QFileDialog *fd = new QFileDialog(nullptr, tr("Select image"), path, - "Image files (*.img *.zip *.iso *.gz *.xz *.zst);;All files (*)"); + "Unraid Image files (*.zip)"); connect(fd, SIGNAL(fileSelected(QString)), SLOT(onFileSelected(QString))); if (_engine) @@ -1187,7 +1204,7 @@ QStringList ImageWriter::getKeymapLayoutList() QString ImageWriter::getSSID() { - return WlanCredentials::instance()->getSSID(); + return QString(); } QString ImageWriter::getPSK() @@ -1202,8 +1219,7 @@ QString ImageWriter::getPSK() return QString(); } #endif - - return WlanCredentials::instance()->getPSK(); + return QString(); } bool ImageWriter::getBoolSetting(const QString &key) @@ -1305,7 +1321,7 @@ bool ImageWriter::hasSavedCustomizationSettings() bool ImageWriter::imageSupportsCustomization() { - return !_initFormat.isEmpty(); + return _initFormat == "UNRAID"; } QStringList ImageWriter::getTranslations() @@ -1334,7 +1350,7 @@ void ImageWriter::changeLanguage(const QString &newLanguageName) qDebug() << "Changing language to" << langcode; QTranslator *trans = new QTranslator(); - if (trans->load(":/i18n/rpi-imager_"+langcode+".qm")) + if (trans->load(":/i18n/unraid-usb-creator_"+langcode+".qm")) { replaceTranslator(trans); _currentLang = newLanguageName; @@ -1476,3 +1492,31 @@ void MountUtilsLog(std::string msg) { Q_UNUSED(msg) //qDebug() << "mountutils:" << msg.c_str(); } + +QString ImageWriter::getInitFormat() +{ + return _initFormat; +} + +QString ImageWriter::getDstDevice() +{ + return _dst; +} + +bool ImageWriter::getDstGuidValid() +{ + return _guidValid; +} + +bool ImageWriter::openUrl(const QString& url) +{ + return QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode)); +} + +bool ImageWriter::windowsBuild() { +#ifdef Q_OS_WIN + return true; +#else + return false; +#endif +} diff --git a/src/imagewriter.h b/src/imagewriter.h index 2676c0380..c2e5c5c00 100644 --- a/src/imagewriter.h +++ b/src/imagewriter.h @@ -38,7 +38,7 @@ class ImageWriter : public QObject Q_INVOKABLE void setSrc(const QUrl &url, quint64 downloadLen = 0, quint64 extrLen = 0, QByteArray expectedHash = "", bool multifilesinzip = false, QString parentcategory = "", QString osname = "", QByteArray initFormat = ""); /* Set device to write to */ - Q_INVOKABLE void setDst(const QString &device, quint64 deviceSize = 0); + Q_INVOKABLE void setDst(const QString &device, bool guidValid, quint64 deviceSize = 0); /* Enable/disable verification */ Q_INVOKABLE void setVerifyEnabled(bool verify); @@ -145,6 +145,11 @@ class ImageWriter : public QObject void replaceTranslator(QTranslator *trans); QString detectPiKeyboard(); Q_INVOKABLE bool hasMouse(); + Q_INVOKABLE QString getInitFormat(); + Q_INVOKABLE QString getDstDevice(); + Q_INVOKABLE bool getDstGuidValid(); + Q_INVOKABLE bool openUrl(const QString& url); + Q_INVOKABLE bool windowsBuild(); signals: /* We are emiting signals with QVariant as parameters because QML likes it that way */ @@ -206,6 +211,7 @@ protected slots: #ifdef Q_OS_WIN QWinTaskbarButton *_taskbarButton; #endif + bool _guidValid; void _parseCompressedFile(); void _parseXZFile(); diff --git a/src/linux/com.limetech.unraid-usb-creator.desktop b/src/linux/com.limetech.unraid-usb-creator.desktop new file mode 100644 index 000000000..801891487 --- /dev/null +++ b/src/linux/com.limetech.unraid-usb-creator.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Version=1.0 +Name=Unraid USB Creator +Comment=Unraid USB Creator +Icon=unraid128 +Exec=unraid-usb-creator %F +Categories=Utility +StartupNotify=false diff --git a/src/linux/linuxdrivelist.cpp b/src/linux/linuxdrivelist.cpp index 85e0549e8..b1d380401 100644 --- a/src/linux/linuxdrivelist.cpp +++ b/src/linux/linuxdrivelist.cpp @@ -9,6 +9,7 @@ #include #include #include +#include /* * Our third-party drivelist module does not provide a C++ implementation @@ -59,6 +60,9 @@ namespace Drivelist return deviceList; } + + QRegularExpression vidRegExp("VENDOR_ID=([0-9A-Za-z]{4})(?:.*)"), pidRegExp("MODEL_ID=([0-9A-Za-z]{4})(?:.*)"), snRegExp("SERIAL_SHORT=([0-9A-Za-z]+)"); + QJsonDocument d = QJsonDocument::fromJson(output); const QJsonArray a = d.object()["blockdevices"].toArray(); for (const auto &i : a) @@ -153,6 +157,26 @@ namespace Drivelist } } + QString serialNumber = bdev["serial"].toString(); + d.serialNumber = serialNumber.toStdString(); + + // retrieve vendor id (vid) and model id (pid) with udevadm command + args.clear(); + args << "info" << "--name=" + name; + p.start("udevadm", args); + p.waitForFinished(2000); + output = p.readAll(); + + if (p.exitStatus() == QProcess::NormalExit && !output.isEmpty()) + { + auto vidMatch = vidRegExp.match(output); + auto pidMatch = pidRegExp.match(output); + auto snMatch = snRegExp.match(output); + if (vidMatch.hasMatch() && pidMatch.hasMatch() && snMatch.hasMatch() && snMatch.captured(1) == serialNumber) { + d.vid = vidMatch.captured(1).toStdString(); + d.pid = pidMatch.captured(1).toStdString(); + } + } deviceList.push_back(d); } diff --git a/src/linux/org.raspberrypi.rpi-imager.desktop b/src/linux/org.raspberrypi.rpi-imager.desktop deleted file mode 100644 index 557b02878..000000000 --- a/src/linux/org.raspberrypi.rpi-imager.desktop +++ /dev/null @@ -1,11 +0,0 @@ -[Desktop Entry] -Type=Application -Version=1.0 -Name=Imager -Name[zh_CN]=树莓派启动盘制作工具 -Comment=Raspberry Pi Imager -Comment[zh_CN]=树莓派启动盘制作工具 -Icon=rpi-imager -Exec=rpi-imager %F -Categories=Utility -StartupNotify=false diff --git a/src/linux/rpi-imager.metainfo.xml.in b/src/linux/rpi-imager.metainfo.xml.in deleted file mode 100644 index 2a442f935..000000000 --- a/src/linux/rpi-imager.metainfo.xml.in +++ /dev/null @@ -1,63 +0,0 @@ - - - org.raspberrypi.rpi-imager - CC0-1.0 - Apache-2.0 - Raspberry Pi Imager - Raspberry Pi imaging utility - -

- Raspberry Pi Imager downloads a .JSON file from the Raspberry Pi - website with a list of all current download options, ensuring you are - always installing the most up-to-date version. -

-

- Once you’ve selected an operating system from the available options, - the utility reads the relevant file directly from the Raspberry Pi - website and writes it straight to the SD card. This speeds up the - process quite considerably compared to the standard process of reading - it from the website, writing it to a file on your hard drive, and then, - as a separate step, reading it back from the hard drive and writing it - to the SD card. -

-

- During this process, Raspberry Pi Imager also caches the downloaded - operating system image – that is to say, it saves a local copy on your - computer, so you can program additional SD cards without having to - download the file again. -

-
- org.raspberrypi.rpi-imager.desktop - - - http://downloads.raspberrypi.org/imager/IMAGING-UTILITY-MAIN.png - Main window - - - http://downloads.raspberrypi.org/imager/IMAGING-UTILITY-OS.png - Choose OS - - - http://downloads.raspberrypi.org/imager/IMAGING-UTILITY-SD.png - Choose SD - - - http://downloads.raspberrypi.org/imager/IMAGING-UTILITY-WRITE.png - Write in progress - - - http://downloads.raspberrypi.org/imager/IMAGING-UTILITY-DONE.png - Write done - - - https://github.com/raspberrypi/rpi-imager - - rpi-imager - - - - - - moderate - -
diff --git a/src/linux/unraid-usb-creator.metainfo.xml.in b/src/linux/unraid-usb-creator.metainfo.xml.in new file mode 100644 index 000000000..79b3ebbdc --- /dev/null +++ b/src/linux/unraid-usb-creator.metainfo.xml.in @@ -0,0 +1,44 @@ + + + com.limetech.unraid-usb-creator + CC0-1.0 + Apache-2.0 + Unraid USB Creator + Unraid USB Creator + +

+ Unraid USB Creator downloads a .JSON file from the Unraid OS + endpoint with a list of all current download options, ensuring you are + always installing the most up-to-date version. +

+

+ Once you’ve selected an operating system from the available options, + the utility reads the relevant file directly from the Unraid + website and writes it straight to the USB. The utility will then run + an OS-specific script to make the USB bootable using syslinux. +

+

+ During this process, Unraid USB Creator also caches the downloaded + operating system image – that is to say, it saves a local copy on your + computer, so you can create additional USBs without having to + download the file again. +

+
+ com.limetech.unraid-usb-creator.desktop + + + https://github.com/unraid/usb-creator-next/assets/47037553/c4fbe528-3505-40ba-87b4-727b6b333cf3 + Main window + + + https://github.com/unraid/usb-creator-next + + unraid-usb-creator + + + + + + moderate + +
diff --git a/src/localfileextractthread.cpp b/src/localfileextractthread.cpp index 4cef1678e..19ab320dc 100644 --- a/src/localfileextractthread.cpp +++ b/src/localfileextractthread.cpp @@ -31,7 +31,7 @@ void LocalFileExtractThread::run() if (isImage() && !_openAndPrepareDevice()) return; - emit preparationStatusUpdate(tr("opening image file")); + emit preparationStatusUpdate(tr("Opening image file")); _timer.start(); _inputfile.setFileName( QUrl(_url).toLocalFile() ); if (!_inputfile.open(_inputfile.ReadOnly)) diff --git a/src/mac/Info.plist.in b/src/mac/Info.plist.in index a50933dfc..1c2d425f4 100644 --- a/src/mac/Info.plist.in +++ b/src/mac/Info.plist.in @@ -5,19 +5,19 @@ CFBundleDevelopmentRegion English CFBundleExecutable - rpi-imager + unraid-usb-creator CFBundleGetInfoString CFBundleIconFile - rpi-imager.icns + unraid.icns CFBundleIdentifier - org.raspberrypi.imagingutility + com.limetech.unraid-usb-creator CFBundleInfoDictionaryVersion 6.0 CFBundleLongVersionString CFBundleName - Raspberry Pi Imager + Unraid USB Creator CFBundlePackageType APPL CFBundleShortVersionString diff --git a/src/main.cpp b/src/main.cpp index c58a847ad..2b38b7d9d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,7 +2,7 @@ * SPDX-License-Identifier: Apache-2.0 * Copyright (C) 2020 Raspberry Pi Ltd */ - +#include #include #include #include @@ -151,8 +151,9 @@ int main(int argc, char *argv[]) */ qputenv("QML_DISABLE_DISK_CACHE", "true"); #ifdef Q_OS_WIN - // prefer ANGLE (DirectX) over desktop OpenGL - QCoreApplication::setAttribute(Qt::AA_UseOpenGLES); + // QT_QUICK_BACKEND must be set to software for MXE crosscompile + qputenv("QT_QUICK_BACKEND", "software"); + qputenv("QT_QPA_PLATFORM", "windows:darkmode=1"); #endif #ifdef QT_NO_WIDGETS if ( !handleDri() ) @@ -172,10 +173,10 @@ int main(int argc, char *argv[]) #else QApplication app(argc, argv); #endif - app.setOrganizationName("Raspberry Pi"); - app.setOrganizationDomain("raspberrypi.org"); - app.setApplicationName("Imager"); - app.setWindowIcon(QIcon(":/icons/rpi-imager.ico")); + app.setOrganizationName("Lime Technology, Inc"); + app.setOrganizationDomain("unraid.net"); + app.setApplicationName("Unraid USB Creator"); + app.setWindowIcon(QIcon(":/icons/unraid.ico")); ImageWriter imageWriter; NetworkAccessManagerFactory namf; QQmlApplicationEngine engine; @@ -264,7 +265,7 @@ int main(int argc, char *argv[]) } else if (args[i] == "--help") { - cerr << args[0] << " [--debug] [--version] [--repo ] [--qm ] [--disable-telemetry] []" << endl; + cerr << args[0] << " [--debug] [--version] [--repo ] [--qm ] " << /*"[--disable-telemetry]" <<*/ "[]" << endl; cerr << "-OR- " << args[0] << " --cli [--disable-verify] [--sha256 ] [--debug] [--quiet] " << endl; return 0; } @@ -274,6 +275,7 @@ int main(int argc, char *argv[]) cerr << "Repository: " << imageWriter.constantOsListUrl().toString() << endl; return 0; } + /* else if (args[i] == "--disable-telemetry") { cerr << "Disabled telemetry" << endl; @@ -286,12 +288,17 @@ int main(int argc, char *argv[]) settings.remove("telemetry"); settings.sync(); } + */ else { cerr << "Ignoring unknown argument: " << args[i] << endl; } } + // for now, make sure telemetry is always disabled + settings.setValue("telemetry", false); + settings.sync(); + QTranslator *translator = new QTranslator; if (customQm.isEmpty()) { @@ -312,7 +319,7 @@ int main(int argc, char *argv[]) QLocale::setDefault(QLocale(langcode)); #endif - if (translator->load(QLocale(), "rpi-imager", "_", QLatin1String(":/i18n"))) + if (translator->load(QLocale(), "unraid-usb-creator", "_", QLatin1String(":/i18n"))) imageWriter.replaceTranslator(translator); else delete translator; diff --git a/src/main.qml b/src/main.qml index 5dc6e185a..d5ed68ad1 100644 --- a/src/main.qml +++ b/src/main.qml @@ -15,11 +15,14 @@ ApplicationWindow { visible: true width: imageWriter.isEmbeddedMode() ? -1 : 680 - height: imageWriter.isEmbeddedMode() ? -1 : 450 + height: imageWriter.isEmbeddedMode() ? -1 : 465 minimumWidth: imageWriter.isEmbeddedMode() ? -1 : 680 - minimumHeight: imageWriter.isEmbeddedMode() ? -1 : 420 + minimumHeight: imageWriter.isEmbeddedMode() ? -1 : 465 + + + color: UnColors.darkGray - title: qsTr("Raspberry Pi Imager v%1").arg(imageWriter.constantVersion()) + title: qsTr("Unraid USB Creator v%1").arg(imageWriter.constantVersion()) FontLoader {id: roboto; source: "fonts/Roboto-Regular.ttf"} FontLoader {id: robotoLight; source: "fonts/Roboto-Light.ttf"} @@ -42,56 +45,93 @@ ApplicationWindow { } } - Shortcut { - sequences: ["Shift+Ctrl+X", "Shift+Meta+X"] - context: Qt.ApplicationShortcut - onActivated: { - optionspopup.openPopup() - } - } - ColumnLayout { id: bg spacing: 0 + RowLayout { + Layout.fillWidth: true + Rectangle { + id: logoContainer + implicitHeight: (window.height - 15)/6 - Rectangle { - id: logoContainer - implicitHeight: window.height/4 + Image { + id: image + source: "icons/UN-logotype-gradient.png" + + // Specify the maximum size of the image + width: window.width * 0.45 + height: (window.height - 15) / 3 + + // Within the image's specified size rectangle, resize the + // image to fit within the rectangle while keeping its aspect + // ratio the same. Preserving the aspect ratio implies some + // extra padding between the Image's extend and the actual + // image content: align left so all this padding is on the + // right. + fillMode: Image.PreserveAspectFit + horizontalAlignment: Image.AlignLeft + + // Keep the left side of the image 40 pixels from the left + // edge + anchors.left: logoContainer.left + anchors.leftMargin: 10 + + // Equal padding above and below the image + anchors.top: logoContainer.top + anchors.bottom: logoContainer.bottom + anchors.topMargin: (window.height - 15) / 25 + anchors.bottomMargin: (window.height - 15) / 25 + } + } + Item { + Layout.fillWidth: true + } + + Text { + color: UnColors.orange + text: qsTr("Help") + font.pixelSize: 12 + font.family: robotoBold.name + font.bold: true + anchors.right: helpImage.left + anchors.rightMargin: 5 + anchors.top: parent.top + anchors.topMargin: 10 + MouseArea { + anchors.fill: parent + onClicked: imageWriter.openUrl("https://docs.unraid.net/go/quick-install-guide") + } + } Image { - id: image - source: "icons/logo_sxs_imager.png" - - // Specify the maximum size of the image - width: window.width * 0.45 - height: window.height / 3 - - // Within the image's specified size rectangle, resize the - // image to fit within the rectangle while keeping its aspect - // ratio the same. Preserving the aspect ratio implies some - // extra padding between the Image's extend and the actual - // image content: align left so all this padding is on the - // right. + id: helpImage + source: "unraid/icons/help_orange.svg" + MouseArea { + anchors.fill: parent + onClicked: imageWriter.openUrl("https://docs.unraid.net/go/quick-install-guide") + } + Layout.preferredHeight: 15 + Layout.preferredWidth: 15 + sourceSize.width: 15 + sourceSize.height: 15 + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.top: parent.top + anchors.topMargin: 10 fillMode: Image.PreserveAspectFit - horizontalAlignment: Image.AlignLeft - - // Keep the left side of the image 40 pixels from the left - // edge - anchors.left: logoContainer.left - anchors.leftMargin: 40 - - // Equal padding above and below the image - anchors.top: logoContainer.top - anchors.bottom: logoContainer.bottom - anchors.topMargin: window.height / 25 - anchors.bottomMargin: window.height / 25 } } + + Rectangle { + color: UnColors.orange + implicitWidth: window.width + implicitHeight: 1 + } Rectangle { - color: "#cd2355" + color: UnColors.darkGray implicitWidth: window.width - implicitHeight: window.height * (1 - 1/4) + implicitHeight: (window.height - 15) * (1 - 1/6) GridLayout { id: gridLayout @@ -115,8 +155,8 @@ ApplicationWindow { Text { id: text0 - color: "#ffffff" - text: qsTr("Raspberry Pi Device") + color: "white" + text: qsTr("Device") Layout.fillWidth: true Layout.preferredHeight: 17 Layout.preferredWidth: 100 @@ -124,6 +164,8 @@ ApplicationWindow { font.family: robotoBold.name font.bold: true horizontalAlignment: Text.AlignHCenter + visible: false + enabled: false } ImButton { @@ -140,7 +182,9 @@ ApplicationWindow { hwlist.forceActiveFocus() } Accessible.ignored: ospopup.visible || dstpopup.visible || hwpopup.visible - Accessible.description: qsTr("Select this button to choose your target Raspberry Pi") + Accessible.description: qsTr("Select this button to choose your target device") + visible: false + enabled: false } } @@ -153,7 +197,7 @@ ApplicationWindow { Text { id: text1 - color: "#ffffff" + color: "white" text: qsTr("Operating System") Layout.fillWidth: true Layout.preferredHeight: 17 @@ -190,7 +234,7 @@ ApplicationWindow { Text { id: text2 - color: "#ffffff" + color: "white" text: qsTr("Storage") Layout.fillWidth: true Layout.preferredHeight: 17 @@ -245,7 +289,6 @@ ApplicationWindow { id: progressBar Layout.fillWidth: true visible: false - Material.background: "#d15d7d" } } @@ -302,18 +345,20 @@ ApplicationWindow { return } - if (!optionspopup.visible && imageWriter.imageSupportsCustomization()) { - usesavedsettingspopup.openPopup() + + if (imageWriter.imageSupportsCustomization()) { + optionspopup.openPopup() } else { confirmwritepopup.askForConfirmation() } + } } } Text { Layout.columnSpan: 3 - color: "#ffffff" + color: "white" font.pixelSize: 18 font.family: roboto.name visible: imageWriter.isEmbeddedMode() && imageWriter.customRepo() @@ -323,7 +368,7 @@ ApplicationWindow { Text { id: networkInfo Layout.columnSpan: 3 - color: "#ffffff" + color: "white" font.pixelSize: 18 font.family: roboto.name visible: imageWriter.isEmbeddedMode() @@ -332,7 +377,7 @@ ApplicationWindow { Text { Layout.columnSpan: 3 - color: "#ffffff" + color: "white" font.pixelSize: 18 font.family: roboto.name visible: !imageWriter.hasMouse() @@ -343,11 +388,14 @@ ApplicationWindow { id: langbarRect Layout.columnSpan: 3 Layout.alignment: Qt.AlignHCenter | Qt.AlignBottom - Layout.bottomMargin: 5 + Layout.bottomMargin: 15 visible: imageWriter.isEmbeddedMode() implicitWidth: langbar.width implicitHeight: langbar.height - color: "#ffffe3" + color: UnColors.mediumGray + Material.theme: Material.Dark + Material.background: UnColors.darkGray + Material.accent: UnColors.orange radius: 5 RowLayout { @@ -361,8 +409,10 @@ ApplicationWindow { Layout.leftMargin: 30 Layout.topMargin: 10 Layout.bottomMargin: 10 + color: "white" } ComboBox { + id: languageselector font.pixelSize: 12 font.family: roboto.name model: imageWriter.getTranslations() @@ -376,6 +426,28 @@ ApplicationWindow { } Layout.topMargin: 10 Layout.bottomMargin: 10 + popup: Popup { + y: languageselector.height - 1 + width: languageselector.width + implicitHeight: contentItem.implicitHeight + padding: 1 + Material.theme: Material.Dark + Material.accent: UnColors.orange + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: languageselector.popup.visible ? languageselector.delegateModel : null + currentIndex: languageselector.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: UnColors.darkGray + radius: 2 + } + } } Text { font.pixelSize: 12 @@ -383,6 +455,7 @@ ApplicationWindow { text: qsTr("Keyboard: ") Layout.topMargin: 10 Layout.bottomMargin: 10 + color: "white" } ComboBox { enabled: imageWriter.isEmbeddedMode() @@ -399,6 +472,28 @@ ApplicationWindow { Layout.topMargin: 10 Layout.bottomMargin: 10 Layout.rightMargin: 30 + popup: Popup { + y: languageselector.height - 1 + width: languageselector.width + implicitHeight: contentItem.implicitHeight + padding: 1 + Material.theme: Material.Dark + Material.accent: UnColors.orange + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: languageselector.popup.visible ? languageselector.delegateModel : null + currentIndex: languageselector.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: UnColors.darkGray + radius: 2 + } + } } } } @@ -427,6 +522,77 @@ ApplicationWindow { } } } + + } + RowLayout { + Image { + id: infoImage + source: "unraid/icons/info_orange.svg" + MouseArea { + anchors.fill: parent + onClicked: infopopup.openPopup() + } + Layout.preferredHeight: 15 + Layout.preferredWidth: 15 + sourceSize.width: 15 + sourceSize.height: 15 + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + } + Text { + color: UnColors.orange + text: qsTr("Info") + font.pixelSize: 12 + font.family: robotoBold.name + font.bold: true + anchors.left: infoImage.right + anchors.leftMargin: 5 + anchors.bottom: parent.bottom + anchors.bottomMargin: 10 + MouseArea { + anchors.fill: parent + onClicked: infopopup.openPopup() + } + } + } + ColumnLayout { + id: columnLayout4 + Layout.row: 1 + Layout.column: 2 + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + spacing: 0 + + Text { + color: UnColors.orange + text: qsTr("Select Language") + font.pixelSize: 12 + font.family: robotoBold.name + font.bold: true + anchors.right: langIcon.left + anchors.rightMargin: 5 + anchors.bottom: parent.bottom + anchors.bottomMargin: 43 + MouseArea { + anchors.fill: parent + onClicked: langbarRect.visible = !langbarRect.visible + } + } + Image { + id: langIcon + source: "unraid/icons/lang.svg" + Layout.preferredHeight: 18 + Layout.preferredWidth: 18 + sourceSize.width: 25 + sourceSize.height: 25 + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.bottom: parent.bottom + anchors.bottomMargin: 41 + fillMode: Image.PreserveAspectFit + } } } @@ -439,10 +605,15 @@ ApplicationWindow { padding: 0 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside property string hwselected: "" + + background: Rectangle { + color: UnColors.darkGray + border.color: UnColors.mediumGray + } // background of title Rectangle { - color: "#f5f5f5" + color: UnColors.mediumGray anchors.right: parent.right anchors.top: parent.top height: 35 @@ -450,13 +621,14 @@ ApplicationWindow { } // line under title Rectangle { - color: "#afafaf" + color: UnColors.mediumGray width: parent.width y: 35 implicitHeight: 1 } Text { + color: "white" text: "X" anchors.right: parent.right anchors.top: parent.top @@ -478,7 +650,8 @@ ApplicationWindow { spacing: 10 Text { - text: qsTr("Raspberry Pi Device") + color: "white" + text: qsTr("Device") horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter Layout.fillWidth: true @@ -541,10 +714,15 @@ ApplicationWindow { padding: 0 closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside property string categorySelected : "" + + background: Rectangle { + color: UnColors.darkGray + border.color: UnColors.mediumGray + } // background of title Rectangle { - color: "#f5f5f5" + color: UnColors.mediumGray anchors.right: parent.right anchors.top: parent.top height: 35 @@ -552,7 +730,7 @@ ApplicationWindow { } // line under title Rectangle { - color: "#afafaf" + color: UnColors.mediumGray width: parent.width y: 35 implicitHeight: 1 @@ -560,6 +738,7 @@ ApplicationWindow { Text { text: "X" + color: "white" anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 25 @@ -582,6 +761,7 @@ ApplicationWindow { Text { text: qsTr("Operating System") + color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter Layout.fillWidth: true @@ -728,15 +908,16 @@ ApplicationWindow { Rectangle { id: bgrect anchors.fill: parent - color: "#f5f5f5" + color: mouseOver ? UnColors.orange : UnColors.darkGray visible: mouseOver && parent.ListView.view.currentIndex !== index property bool mouseOver: false + border.color: UnColors.mediumGray } Rectangle { id: borderrect implicitHeight: 1 implicitWidth: parent.width - color: "#dcdcdc" + color: UnColors.mediumGray y: parent.height } @@ -751,6 +932,7 @@ ApplicationWindow { spacing: 12 Image { + id: iconimage source: typeof icon === "undefined" ? "" : icon Layout.preferredHeight: 64 Layout.preferredWidth: 64 @@ -768,6 +950,7 @@ ApplicationWindow { elide: Text.ElideRight font.family: roboto.name font.bold: true + color: bgrect.mouseOver ? UnColors.darkGray : "white" } Text { @@ -775,7 +958,7 @@ ApplicationWindow { font.family: roboto.name text: description wrapMode: Text.WordWrap - color: "#1a1a1a" + color: bgrect.mouseOver ? UnColors.darkGray : "white" } ToolTip { @@ -819,15 +1002,16 @@ ApplicationWindow { Rectangle { id: bgrect anchors.fill: parent - color: "#f5f5f5" + color: mouseOver ? UnColors.orange : UnColors.darkGray visible: mouseOver && parent.ListView.view.currentIndex !== index property bool mouseOver: false + border.color: UnColors.mediumGray } Rectangle { id: borderrect implicitHeight: 1 implicitWidth: parent.width - color: "#dcdcdc" + color: UnColors.mediumGray y: parent.height } @@ -842,7 +1026,8 @@ ApplicationWindow { spacing: 12 Image { - source: icon == "icons/ic_build_48px.svg" ? "icons/cat_misc_utility_images.png": icon + id: iconimage + source: (icon == "https://craftassets.unraid.net/static/favicon/favicon.ico") ? (bgrect.mouseOver ? "unraid/icons/un-mark-dark-gray.svg" : "unraid/icons/un-mark-gradient.svg") : (icon == "icons/erase.png" ? (bgrect.mouseOver ? "unraid/icons/erase_dark_gray.svg" : "unraid/icons/erase_orange.svg") : (icon == "icons/use_custom.png" ? (bgrect.mouseOver ? "unraid/icons/use_custom_dark_gray.svg" : "unraid/icons/use_custom_orange.svg") : icon)) Layout.preferredHeight: 40 Layout.preferredWidth: 40 sourceSize.width: 40 @@ -861,15 +1046,16 @@ ApplicationWindow { elide: Text.ElideRight font.family: roboto.name font.bold: true + color: bgrect.mouseOver ? UnColors.darkGray : "white" } Image { - source: "icons/ic_info_16px.png" + source: bgrect.mouseOver ? "unraid/icons/info_dark_gray.svg" : "unraid/icons/info_orange.svg" Layout.preferredHeight: 16 Layout.preferredWidth: 16 visible: typeof(website) == "string" && website MouseArea { anchors.fill: parent - onClicked: Qt.openUrlExternally(website) + onClicked: imageWriter.openUrl(website) } } Item { @@ -882,13 +1068,13 @@ ApplicationWindow { font.family: roboto.name text: description wrapMode: Text.WordWrap - color: "#1a1a1a" + color: bgrect.mouseOver ? UnColors.darkGray : "white" } Text { Layout.fillWidth: true elide: Text.ElideRight - color: "#646464" + color: bgrect.mouseOver ? UnColors.darkGray : "white" font.weight: Font.Light visible: typeof(release_date) == "string" && release_date text: qsTr("Released: %1").arg(release_date) @@ -896,7 +1082,7 @@ ApplicationWindow { Text { Layout.fillWidth: true elide: Text.ElideRight - color: "#646464" + color: bgrect.mouseOver ? UnColors.darkGray : "white" font.weight: Font.Light visible: typeof(url) == "string" && url != "" && url != "internal://format" text: !url ? "" : @@ -915,7 +1101,7 @@ ApplicationWindow { } } Image { - source: "icons/ic_chevron_right_40px.svg" + source: "icons/ic_chevron_right_40px_orange.svg" visible: (typeof(subitems_json) == "string" && subitems_json != "") || (typeof(subitems_url) == "string" && subitems_url != "" && subitems_url != "internal://back") Layout.preferredHeight: 40 Layout.preferredWidth: 40 @@ -938,9 +1124,14 @@ ApplicationWindow { closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside onClosed: imageWriter.stopDriveListPolling() + background: Rectangle { + color: UnColors.darkGray + border.color: UnColors.mediumGray + } + // background of title Rectangle { - color: "#f5f5f5" + color: UnColors.mediumGray anchors.right: parent.right anchors.top: parent.top height: 35 @@ -948,7 +1139,7 @@ ApplicationWindow { } // line under title Rectangle { - color: "#afafaf" + color: UnColors.mediumGray width: parent.width y: 35 implicitHeight: 1 @@ -956,6 +1147,7 @@ ApplicationWindow { Text { text: "X" + color: "white" anchors.right: parent.right anchors.top: parent.top anchors.rightMargin: 25 @@ -977,6 +1169,7 @@ ApplicationWindow { Text { text: qsTr("Storage") + color: "white" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter Layout.fillWidth: true @@ -1006,6 +1199,7 @@ ApplicationWindow { visible: parent.count == 0 text: qsTr("No storage devices found") font.bold: true + color: "white" } ScrollBar.vertical: ScrollBar { @@ -1037,29 +1231,34 @@ ApplicationWindow { width: window.width-100 height: 60 Accessible.name: { - var txt = description+" - "+(size/1000000000).toFixed(1)+" gigabytes" + var txt = description+" - "+(size/1000000000).toFixed(1)+" GB" if (mountpoints.length > 0) { - txt += qsTr("Mounted as %1").arg(mountpoints.join(", ")) + txt += qsTr(" Mounted as %1").arg(mountpoints.join(", ")) } return txt; } property string description: model.description property string device: model.device property string size: model.size + property string guid: model.guid + property bool guidValid: model.guidValid + + enabled: guidValid Rectangle { id: dstbgrect anchors.fill: parent - color: "#f5f5f5" + color: mouseOver ? UnColors.orange : UnColors.darkGray visible: mouseOver && parent.ListView.view.currentIndex !== index property bool mouseOver: false + border.color: UnColors.mediumGray } Rectangle { id: dstborderrect implicitHeight: 1 implicitWidth: parent.width - color: "#dcdcdc" + color: UnColors.mediumGray y: parent.height } @@ -1070,7 +1269,8 @@ ApplicationWindow { width: 64 Image { - source: isUsb ? "icons/ic_usb_40px.svg" : isScsi ? "icons/ic_storage_40px.svg" : "icons/ic_sd_storage_40px.svg" + id: iconimage + source: isUsb ? (dstbgrect.mouseOver ? "icons/ic_usb_40px.svg" : "icons/ic_usb_40px_orange.svg") : isScsi ? (dstbgrect.mouseOver ? "icons/ic_storage_40px.svg" : "icons/ic_storage_40px_orange.svg") : (dstbgrect.mouseOver ? "icons/ic_sd_storage_40px.svg" : "icons/ic_sd_storage_40px_orange.svg") verticalAlignment: Image.AlignVCenter height: parent.parent.parent.height fillMode: Image.Pad @@ -1078,29 +1278,61 @@ ApplicationWindow { } Column { width: parent.parent.width-64 - - Text { - textFormat: Text.StyledText - height: parent.parent.parent.height - verticalAlignment: Text.AlignVCenter - font.family: roboto.name - text: { - var sizeStr = (size/1000000000).toFixed(1)+" GB"; - var txt; - if (isReadOnly) { - txt = "

"+description+" - "+sizeStr+"

" - txt += "" + ColumnLayout { + spacing: 1 + Text { + textFormat: Text.StyledText + height: parent.parent.parent.parent.height / 3 + //verticalAlignment: Text.AlignVCenter + font.family: roboto.name + font.pixelSize: 14 + text: { + var sizeStr = (size/1000000000).toFixed(1) + " GB"; + var txt = description + " - " + sizeStr + return txt + } + color: dstbgrect.mouseOver ? UnColors.darkGray : "white" + opacity: enabled ? 1.0 : 0.3 + Layout.topMargin: 5 + } + Text { + textFormat: Text.StyledText + height: parent.parent.parent.parent.height / 3 + //verticalAlignment: Text.AlignVCenter + font.family: roboto.name + font.pixelSize: 12 + text: { + var txt = "" if (mountpoints.length > 0) { - txt += qsTr("Mounted as %1").arg(mountpoints.join(", "))+" " + txt += qsTr("Mounted as %1 ").arg(mountpoints.join(", ")) } - txt += qsTr("[WRITE PROTECTED]")+"" - } else { - txt = "

"+description+" - "+sizeStr+"

" - if (mountpoints.length > 0) { - txt += ""+qsTr("Mounted as %1").arg(mountpoints.join(", "))+"" + if (isReadOnly) { + txt += qsTr("[WRITE PROTECTED]") + } + return txt + } + color: dstbgrect.mouseOver ? UnColors.darkGray : "white" + opacity: enabled ? 1.0 : 0.3 + anchors.topMargin: 10 + } + Text { + textFormat: Text.StyledText + height: parent.parent.parent.parent.height / 3 + //verticalAlignment: Text.AlignVCenter + font.family: roboto.name + font.pixelSize: 12 + text: { + var txt = "" + if(guid != "") { + txt += "GUID: %1".arg(guid) + if(!guidValid) txt += " [BLACKLISTED - Choose Another Flash Device]" + } else { + txt += "[MISSING GUID - Choose Another Flash Device]" } + return txt; } - return txt; + color: dstbgrect.mouseOver ? UnColors.darkGray : "white" + opacity: enabled ? 1.0 : 0.3 } } } @@ -1131,6 +1363,21 @@ ApplicationWindow { onOpened: { forceActiveFocus() } + onInstallGuide: { + imageWriter.openUrl("https://docs.unraid.net/go/quick-install-guide") + } + } + + MsgPopup { + id: infopopup + x: 50 + width: parent.width - 100 + continueButton: false + yesButton: false + noButton: false + title: qsTr("About") + body.onLinkActivated: imageWriter.openUrl(link) + text: qsTr("License, Credits, and History: ") + "https://github.com/unraid/usb-creator-next

" + qsTr("Help / Feedback: ") + "https://unraid.net/contact" } MsgPopup { @@ -1139,7 +1386,7 @@ ApplicationWindow { yesButton: true noButton: true title: qsTr("Are you sure you want to quit?") - text: qsTr("Raspberry Pi Imager is still busy.
Are you sure you want to quit?") + text: qsTr("Unraid USB Creator is still busy.
Are you sure you want to quit?") onYes: { Qt.quit() } @@ -1163,7 +1410,7 @@ ApplicationWindow { progressText.visible = true progressBar.visible = true progressBar.indeterminate = true - progressBar.Material.accent = "#ffffff" + progressBar.Material.accent = UnColors.orange osbutton.enabled = false dstbutton.enabled = false hwbutton.enabled = false @@ -1189,43 +1436,20 @@ ApplicationWindow { noButton: true property url url title: qsTr("Update available") - text: qsTr("There is a newer version of Imager available.
Would you like to visit the website to download it?") + text: qsTr("There is a newer version of Unraid USB Creator available.
Would you like to visit the website to download it?") onYes: { - Qt.openUrlExternally(url) + imageWriter.openUrl(url) } } - OptionsPopup { + UnraidOptionsPopup { id: optionspopup onSaveSettingsSignal: { imageWriter.setSavedCustomizationSettings(settings) - usesavedsettingspopup.hasSavedSettings = true } - } - - UseSavedSettingsPopup { - id: usesavedsettingspopup - onYes: { - optionspopup.initialize() - optionspopup.applySettings() + onContinueSignal: { confirmwritepopup.askForConfirmation() } - onNo: { - imageWriter.setImageCustomization("", "", "", "", "") - confirmwritepopup.askForConfirmation() - } - onNoClearSettings: { - hasSavedSettings = false - optionspopup.clearCustomizationFields() - imageWriter.clearSavedCustomizationSettings() - confirmwritepopup.askForConfirmation() - } - onEditSettings: { - optionspopup.openPopup() - } - onCloseSettings: { - optionspopup.close() - } } /* Slots for signals imagewrite emits */ @@ -1264,7 +1488,7 @@ ApplicationWindow { return progressText.text = qsTr("Verifying... %1%").arg(Math.floor(newPos*100)) - progressBar.Material.accent = "#6cc04a" + progressBar.Material.accent = UnColors.orange progressBar.value = newPos } } @@ -1299,21 +1523,28 @@ ApplicationWindow { function onSuccess() { msgpopup.title = qsTr("Write Successful") if (osbutton.text === qsTr("Erase")) - msgpopup.text = qsTr("%1 has been erased

You can now remove the SD card from the reader").arg(dstbutton.text) + msgpopup.text = qsTr("%1 has been erased.

Your drive has been ejected, you can now safely remove it.").arg(dstbutton.text) else if (imageWriter.isEmbeddedMode()) { //msgpopup.text = qsTr("%1 has been written to %2").arg(osbutton.text).arg(dstbutton.text) /* Just reboot to the installed OS */ Qt.quit() } - else - msgpopup.text = qsTr("%1 has been written to %2

You can now remove the SD card from the reader").arg(osbutton.text).arg(dstbutton.text) + else { + msgpopup.text = qsTr("%1 has been written to %2.").arg(osbutton.text).arg(dstbutton.text) + if(imageWriter.getInitFormat() === "UNRAID") { + if(!imageWriter.windowsBuild()) { + msgpopup.text += qsTr("

If you would like to enable legacy boot (bios), helpful for old hardware, please run the 'make_bootable_(mac/linux/windows)' script from this computer, located in the main folder of the UNRAID flash drive.") + } + msgpopup.installGuideButton = true + } + } if (imageWriter.isEmbeddedMode()) { msgpopup.continueButton = false msgpopup.quitButton = true } msgpopup.openPopup() - imageWriter.setDst("") + imageWriter.setDst("", false) dstbutton.text = qsTr("CHOOSE STORAGE") resetWriteButton() } @@ -1523,6 +1754,9 @@ ApplicationWindow { hwlist.currentIndex = deviceModel.count-1 } } + // default select first item in hwlist if hwlist not empty + if(hwlist.count > 0) + selectHWitem(hwlist.model.get(0)) } if (imageWriter.getBoolSetting("check_version") && "latest_version" in imager && "url" in imager) { @@ -1562,6 +1796,8 @@ ApplicationWindow { device: drive, description: driveListModel.data(driveListModel.index(i,0), 0x102), size: driveListModel.data(driveListModel.index(i,0), 0x103), + guid: driveListModel.data(driveListModel.index(i,0), 0x108), + guidValid: driveListModel.data(driveListModel.index(i,0), 0x109), readonly: false }) break @@ -1714,6 +1950,12 @@ ApplicationWindow { } } else { imageWriter.setSrc(d.url, d.image_download_size, d.extract_size, typeof(d.extract_sha256) != "undefined" ? d.extract_sha256 : "", typeof(d.contains_multiple_files) != "undefined" ? d.contains_multiple_files : false, ospopup.categorySelected, d.name, typeof(d.init_format) != "undefined" ? d.init_format : "") + if(imageWriter.getInitFormat() === "UNRAID" && imageWriter.getDstDevice() !== "" && !imageWriter.getDstGuidValid()) { + onError(qsTr("Selected device cannot be used to create an Unraid USB due to its invalid GUID.")) + writebutton.enabled = false + imageWriter.setDst("", false) + dstbutton.text = qsTr("CHOOSE STORAGE") + } osbutton.text = d.name ospopup.close() osswipeview.decrementCurrentIndex() @@ -1729,8 +1971,14 @@ ApplicationWindow { return } + if(imageWriter.getInitFormat() === "UNRAID" && !d.guidValid) { + onError(qsTr("Selected device cannot be used to create an Unraid USB due to its invalid GUID.")) + writebutton.enabled = false + return + } + dstpopup.close() - imageWriter.setDst(d.device, d.size) + imageWriter.setDst(d.device, d.guidValid, d.size) dstbutton.text = d.description if (imageWriter.readyToWrite()) { writebutton.enabled = true diff --git a/src/qml.qrc b/src/qml.qrc index 8dcca05a2..ecfa3ac89 100644 --- a/src/qml.qrc +++ b/src/qml.qrc @@ -2,41 +2,61 @@ main.qml qtquickcontrols2.conf - icons/rpi-imager.ico + icons/unraid.ico fonts/Roboto-Bold.ttf fonts/Roboto-Light.ttf fonts/Roboto-Regular.ttf - icons/ic_chevron_left_40px.svg - icons/ic_chevron_right_40px.svg + icons/ic_chevron_left_40px_orange.svg + icons/ic_chevron_right_40px_orange.svg + icons/ic_sd_storage_40px_orange.svg + icons/ic_storage_40px_orange.svg + icons/ic_usb_40px_orange.svg icons/ic_sd_storage_40px.svg icons/ic_storage_40px.svg icons/ic_usb_40px.svg MsgPopup.qml - icons/use_custom.png - icons/erase.png - icons/cat_raspberry_pi_os.png - icons/cat_other_specific_purpose_operating_systems.png - icons/cat_other_general_purpose_operating_systems.png - icons/cat_misc_utility_images.png - icons/cat_media_players.png - icons/cat_emulation_and_games.png + unraid/icons/use_custom_orange.svg + unraid/icons/use_custom_dark_gray.svg + unraid/icons/erase_orange.svg + unraid/icons/erase_dark_gray.svg OptionsPopup.qml countries.txt timezones.txt - UseSavedSettingsPopup.qml - icons/ic_info_16px.png - icons/ic_info_12px.png keymap-layouts.txt - icons/ic_cog_red.svg - icons/cat_home_automation.png - icons/cat_language_specific_operating_systems.png - icons/cat_3d_printing.png - icons/logo_stacked_imager.png - icons/logo_sxs_imager.png + icons/UN-logotype-gradient.svg + icons/UN-logotype-gradient.png + UnraidOptionsPopup.qml qmlcomponents/ImButton.qml - qmlcomponents/ImButtonRed.qml qmlcomponents/ImCheckBox.qml qmlcomponents/ImRadioButton.qml - icons/cat_digital_signage.png + qmlcomponents/UnColors.qml + qmlcomponents/qmldir + qmlcomponents/IpTextField.qml + qmlcomponents/regex_validator_qt5.qml + qmlcomponents/regex_validator_qt6.qml + icons/unraid.icns + unraid/icons/help_orange.svg + unraid/icons/lang.svg + unraid/icons/help_dark_gray.svg + unraid/icons/info_orange.svg + unraid/icons/info_dark_gray.svg + unraid/icons/un-mark-dark-gray.svg + unraid/icons/un-mark-gradient.svg + unraid/syslinux/ldlinux.c32 + unraid/syslinux/libcom32.c32 + unraid/syslinux/libutil.c32 + unraid/syslinux/make_bootable_linux.sh + unraid/syslinux/make_bootable_mac.sh + unraid/syslinux/mboot.c32 + unraid/syslinux/mbr.bin + unraid/syslinux/menu.c32 + unraid/syslinux/syslinux + unraid/syslinux/syslinux_linux + unraid/syslinux/syslinux.cfg + unraid/syslinux/syslinux.cfg- + unraid/syslinux/syslinux.exe + unraid/make_bootable_linux + unraid/make_bootable_mac + unraid/make_bootable.bat diff --git a/src/qmlcomponents/ImButton.qml b/src/qmlcomponents/ImButton.qml index fd88a6e3c..339828862 100644 --- a/src/qmlcomponents/ImButton.qml +++ b/src/qmlcomponents/ImButton.qml @@ -10,8 +10,25 @@ import QtQuick.Controls.Material 2.2 Button { font.family: roboto.name - Material.background: activeFocus ? "#d1dcfb" : "#ffffff" - Material.foreground: "#cd2355" + font.bold: true + background: Rectangle { + implicitWidth: 100 + implicitHeight: 40 + opacity: enabled ? 1.0 : 0.3 + color: parent.hovered ? UnColors.orange : UnColors.darkGray + border.color: UnColors.orange + border.width: 1 + radius: 25 + } + contentItem: Text { + text: parent.text + font: parent.font + opacity: enabled ? 1.0 : 0.3 + color: parent.hovered ? UnColors.darkGray : "white" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } Accessible.onPressAction: clicked() Keys.onEnterPressed: clicked() Keys.onReturnPressed: clicked() diff --git a/src/qmlcomponents/ImButtonRed.qml b/src/qmlcomponents/ImButtonRed.qml deleted file mode 100644 index 3f47014c2..000000000 --- a/src/qmlcomponents/ImButtonRed.qml +++ /dev/null @@ -1,14 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * Copyright (C) 2022 Raspberry Pi Ltd - */ - -import QtQuick 2.9 -import QtQuick.Controls 2.2 -import QtQuick.Layouts 1.0 -import QtQuick.Controls.Material 2.2 - -ImButton { - Material.background: activeFocus ? "#32a0d7" : "#cd2355" - Material.foreground: "#ffffff" -} diff --git a/src/qmlcomponents/ImCheckBox.qml b/src/qmlcomponents/ImCheckBox.qml index 038636100..88ba66962 100644 --- a/src/qmlcomponents/ImCheckBox.qml +++ b/src/qmlcomponents/ImCheckBox.qml @@ -6,9 +6,42 @@ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.0 -import QtQuick.Controls.Material 2.2 CheckBox { + + indicator: Rectangle { + implicitWidth: 20 + implicitHeight: 20 + x: parent.leftPadding + y: parent.height / 2 - height / 2 + radius: 3 + opacity: parent.enabled ? 1.0 : 0.3 + color: parent.checked ? UnColors.orange : UnColors.darkGray + border.color: UnColors.orange + + + Text { + width: 14 + height: 14 + x: 1 + y: -2 + text: "✔" + font.pointSize: 14 + color: UnColors.darkGray + visible: parent.parent.checked + } + + } + + contentItem: Text { + text: parent.text + font: parent.font + opacity: parent.enabled ? 1.0 : 0.3 + color: "white" + verticalAlignment: Text.AlignVCenter + leftPadding: parent.indicator.width + parent.spacing + } + Keys.onEnterPressed: toggle() Keys.onReturnPressed: toggle() -} +} \ No newline at end of file diff --git a/src/qmlcomponents/IpTextField.qml b/src/qmlcomponents/IpTextField.qml new file mode 100644 index 000000000..cf755ca30 --- /dev/null +++ b/src/qmlcomponents/IpTextField.qml @@ -0,0 +1,97 @@ +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.0 +import QtQuick.Controls.Material 2.2 +import "." + +RowLayout { + property alias label: ipEditLabel.text + property string fullAddress: fieldIpAddress1.text + "." + fieldIpAddress2.text + "." + fieldIpAddress3.text + "." + fieldIpAddress4.text + property bool acceptableInput: (fieldIpAddress1.acceptableInput && fieldIpAddress2.acceptableInput && fieldIpAddress3.acceptableInput && fieldIpAddress4.acceptableInput) || !parent.enabled + + TextMetrics { + id: textMetrics + font: fieldIpAddress1.font + text: "255" + } + + Text { + id: ipEditLabel + color: !parent.acceptableInput ? "red" : "white" + opacity: parent.enabled ? 1.0 : 0.3 + } + TextField { + id: fieldIpAddress1 + width: textMetrics.width + leftPadding + rightPadding + Layout.preferredWidth: textMetrics.width + leftPadding + rightPadding + horizontalAlignment: TextInput.AlignHCenter + selectByMouse: true + validator: IntValidator { + bottom: 0 + top: 255 + } + } + Text { + text: "." + color: "white" + opacity: parent.enabled ? 1.0 : 0.3 + } + TextField { + id: fieldIpAddress2 + width: textMetrics.width + leftPadding + rightPadding + Layout.preferredWidth: textMetrics.width + leftPadding + rightPadding + horizontalAlignment: TextInput.AlignHCenter + selectByMouse: true + validator: IntValidator { + bottom: 0 + top: 255 + } + } + Text { + text: "." + color: "white" + opacity: parent.enabled ? 1.0 : 0.3 + } + TextField { + id: fieldIpAddress3 + width: textMetrics.width + leftPadding + rightPadding + Layout.preferredWidth: textMetrics.width + leftPadding + rightPadding + horizontalAlignment: TextInput.AlignHCenter + selectByMouse: true + validator: IntValidator { + bottom: 0 + top: 255 + } + } + Text { + text: "." + color: "white" + opacity: parent.enabled ? 1.0 : 0.3 + } + TextField { + id: fieldIpAddress4 + width: textMetrics.width + leftPadding + rightPadding + Layout.preferredWidth: textMetrics.width + leftPadding + rightPadding + horizontalAlignment: TextInput.AlignHCenter + selectByMouse: true + validator: IntValidator { + bottom: 0 + top: 255 + } + } + + function forceActiveFocus() { + if (!fieldIpAddress1.acceptableInput) { + fieldIpAddress1.forceActiveFocus() + } + else if (!fieldIpAddress2.acceptableInput) { + fieldIpAddress2.forceActiveFocus() + } + else if (!fieldIpAddress3.acceptableInput) { + fieldIpAddress3.forceActiveFocus() + } + else if (!fieldIpAddress4.acceptableInput) { + fieldIpAddress4.forceActiveFocus() + } + } +} \ No newline at end of file diff --git a/src/qmlcomponents/UnColors.qml b/src/qmlcomponents/UnColors.qml new file mode 100644 index 000000000..330c6b0e4 --- /dev/null +++ b/src/qmlcomponents/UnColors.qml @@ -0,0 +1,8 @@ +pragma Singleton +import QtQuick 2.9 + +QtObject { + readonly property color orange: "#FF8C2F" + readonly property color darkGray: "#1C1B1B" + readonly property color mediumGray: "#2B2A29" +} \ No newline at end of file diff --git a/src/qmlcomponents/qmldir b/src/qmlcomponents/qmldir new file mode 100644 index 000000000..ac0159cb2 --- /dev/null +++ b/src/qmlcomponents/qmldir @@ -0,0 +1,2 @@ +module CustomColors +singleton UnColors UnColors.qml \ No newline at end of file diff --git a/src/qmlcomponents/regex_validator_qt5.qml b/src/qmlcomponents/regex_validator_qt5.qml new file mode 100644 index 000000000..4370150b1 --- /dev/null +++ b/src/qmlcomponents/regex_validator_qt5.qml @@ -0,0 +1,5 @@ +import QtQuick 2.9 + +RegExpValidator { + regExp: /^[A-Za-z0-9]([A-Za-z0-9\-\.]{0,13}[A-Za-z0-9])?$/ +} diff --git a/src/qmlcomponents/regex_validator_qt6.qml b/src/qmlcomponents/regex_validator_qt6.qml new file mode 100644 index 000000000..db2c280fd --- /dev/null +++ b/src/qmlcomponents/regex_validator_qt6.qml @@ -0,0 +1,5 @@ +import QtQuick + +RegularExpressionValidator { + regularExpression: /^[A-Za-z0-9]([A-Za-z0-9\-\.]{0,13}[A-Za-z0-9])?$/ +} diff --git a/src/unraid/icons/cat_archived_releases.png b/src/unraid/icons/cat_archived_releases.png new file mode 100644 index 000000000..bd177cf62 Binary files /dev/null and b/src/unraid/icons/cat_archived_releases.png differ diff --git a/src/unraid/icons/cat_archived_releases_dark_gray.svg b/src/unraid/icons/cat_archived_releases_dark_gray.svg new file mode 100644 index 000000000..9ea5d60fb --- /dev/null +++ b/src/unraid/icons/cat_archived_releases_dark_gray.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/unraid/icons/cat_archived_releases_orange.svg b/src/unraid/icons/cat_archived_releases_orange.svg new file mode 100644 index 000000000..e12bea7b7 --- /dev/null +++ b/src/unraid/icons/cat_archived_releases_orange.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/unraid/icons/cat_developer_releases.png b/src/unraid/icons/cat_developer_releases.png new file mode 100644 index 000000000..8a51bd194 Binary files /dev/null and b/src/unraid/icons/cat_developer_releases.png differ diff --git a/src/unraid/icons/cat_developer_releases_dark_gray.svg b/src/unraid/icons/cat_developer_releases_dark_gray.svg new file mode 100644 index 000000000..35a1bf020 --- /dev/null +++ b/src/unraid/icons/cat_developer_releases_dark_gray.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/unraid/icons/cat_developer_releases_orange.svg b/src/unraid/icons/cat_developer_releases_orange.svg new file mode 100644 index 000000000..5123435d3 --- /dev/null +++ b/src/unraid/icons/cat_developer_releases_orange.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/unraid/icons/erase.png b/src/unraid/icons/erase.png new file mode 100644 index 000000000..6473d7f85 Binary files /dev/null and b/src/unraid/icons/erase.png differ diff --git a/src/unraid/icons/erase_dark_gray.svg b/src/unraid/icons/erase_dark_gray.svg new file mode 100644 index 000000000..6628b3202 --- /dev/null +++ b/src/unraid/icons/erase_dark_gray.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/erase_orange.svg b/src/unraid/icons/erase_orange.svg new file mode 100644 index 000000000..c50423c01 --- /dev/null +++ b/src/unraid/icons/erase_orange.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/help.png b/src/unraid/icons/help.png new file mode 100644 index 000000000..443fb9525 Binary files /dev/null and b/src/unraid/icons/help.png differ diff --git a/src/unraid/icons/help_dark_gray.svg b/src/unraid/icons/help_dark_gray.svg new file mode 100644 index 000000000..36c4b7321 --- /dev/null +++ b/src/unraid/icons/help_dark_gray.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/help_orange.svg b/src/unraid/icons/help_orange.svg new file mode 100644 index 000000000..bf2481906 --- /dev/null +++ b/src/unraid/icons/help_orange.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/info.png b/src/unraid/icons/info.png new file mode 100644 index 000000000..60b2dee24 Binary files /dev/null and b/src/unraid/icons/info.png differ diff --git a/src/unraid/icons/info_dark_gray.svg b/src/unraid/icons/info_dark_gray.svg new file mode 100644 index 000000000..0a0f8e7e8 --- /dev/null +++ b/src/unraid/icons/info_dark_gray.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/info_orange.svg b/src/unraid/icons/info_orange.svg new file mode 100644 index 000000000..91c85f17a --- /dev/null +++ b/src/unraid/icons/info_orange.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/lang.svg b/src/unraid/icons/lang.svg new file mode 100644 index 000000000..7762e4344 --- /dev/null +++ b/src/unraid/icons/lang.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/un-mark-dark-gray.svg b/src/unraid/icons/un-mark-dark-gray.svg new file mode 100644 index 000000000..4be68825b --- /dev/null +++ b/src/unraid/icons/un-mark-dark-gray.svg @@ -0,0 +1 @@ +UN-mark-black \ No newline at end of file diff --git a/src/unraid/icons/un-mark-gradient.svg b/src/unraid/icons/un-mark-gradient.svg new file mode 100644 index 000000000..9d00513fb --- /dev/null +++ b/src/unraid/icons/un-mark-gradient.svg @@ -0,0 +1 @@ +un-mark-gradient \ No newline at end of file diff --git a/src/unraid/icons/use_custom.png b/src/unraid/icons/use_custom.png new file mode 100644 index 000000000..f25ed969a Binary files /dev/null and b/src/unraid/icons/use_custom.png differ diff --git a/src/unraid/icons/use_custom_dark_gray.svg b/src/unraid/icons/use_custom_dark_gray.svg new file mode 100644 index 000000000..0cccd9c95 --- /dev/null +++ b/src/unraid/icons/use_custom_dark_gray.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/icons/use_custom_orange.svg b/src/unraid/icons/use_custom_orange.svg new file mode 100644 index 000000000..9b737e167 --- /dev/null +++ b/src/unraid/icons/use_custom_orange.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/unraid/make_bootable.bat b/src/unraid/make_bootable.bat new file mode 100644 index 000000000..d8b819a34 --- /dev/null +++ b/src/unraid/make_bootable.bat @@ -0,0 +1,77 @@ +@echo off +:: v1.0 - script to make the USB Flash device from which this is run bootable. +:: v1.1 - script authorization check code contributed by forum member "Spectrum" +:: v1.2 - include 'f' switch to syslinux invokation to handle devices not marked "removeable" +:: v1.3 - make "script authorization" and "volume label" check language independent +:: v1.4 - add check of syslinux executable presence +:: v1.5 - add Legacy/UEFI boot mode selection + +echo Make Bootable v1.5 + +:: check script authorization +net session >nul 2>&1 +if not %errorlevel% equ 0 ( + echo ERROR - script must be run as administrator + echo: + echo Right click and select 'Run as administrator' + echo or execute from an elevated command prompt. + goto:end +) + +:: check syslinux executable +set syslinux=%~d0\syslinux\syslinux.exe +if not exist %syslinux% ( + echo ERROR - syslinux executable not found, expected: %syslinux% + goto:end +) + +:: output volume information +vol %~d0 + +:: check volume label +set tag=UNRAID +set label= +for /f %%v in ('vol %~d0^|findstr %tag%') do set label=%tag% +if not defined label ( + echo ERROR - volume label must be %tag% + goto:end +) + +:: Skip boot mode selection when not present +if not exist EFI ( +if not exist EFI- ( + goto:make +)) + +:: Select boot mode +:select +echo: +set/p boot="Permit UEFI boot mode [Y/N]: " +if /i %boot%==N ( + if exist EFI ( + if not exist EFI- ( + rename EFI EFI- + )) + goto:make +) +if /i %boot%==Y ( + if exist EFI- ( + if not exist EFI ( + rename EFI- EFI + )) + goto:make +) +echo Please answer Y or N +goto:select + +:: make bootable +:make +echo Ready to make disk bootable! +pause +echo %syslinux% -maf %~d0 +%syslinux% -maf %~d0 +echo Completed + +:end +echo: +pause diff --git a/src/unraid/make_bootable_linux b/src/unraid/make_bootable_linux new file mode 100644 index 000000000..767e8a594 --- /dev/null +++ b/src/unraid/make_bootable_linux @@ -0,0 +1,100 @@ +#!/bin/bash + +################################################################################ +# This file is ported from makebootable_mac used for the creation +# of a bootable USB thumb drive that unRAID can be run from +# Copyright (C) 2009-2012 Kyle Hiltner (stephan@openelec.tv) +# Copyright (C) 2016 Lime Technology +# +# 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 2, 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. +################################################################################ + +VERSION="v1.3" + +echo "INFO: make_bootable_linux $VERSION" +echo "" + +DISTRO=$(uname -s) +if [ $DISTRO != "Linux" ]; then + echo "" + echo "FAIL: This script is only for Linux systems, aborting!" + echo "" + exit 1 +fi + +if [ ! -b "/dev/disk/by-label/UNRAID" ]; then + echo "" + echo "FAIL: There appears to be no drive present with the label UNRAID, aborting!" + echo "" + exit 1 +fi + +UNRAID_PARTITION=$(readlink -f /dev/disk/by-label/UNRAID) +if [ "${UNRAID_PARTITION: -1}" != "1" ]; then + echo "" + echo "FAIL: unRAID Flash drive detected but not installed on the first partition, aborting!" + echo "" + exit 1 +fi + +TARGET=${UNRAID_PARTITION::${#UNRAID_PARTITION}-1} +echo "INFO: The following device appears to be the unRAID USB Flash drive: $TARGET" + +# check if UNRAID partition is already mounted, if not mount to a temp location +if [ -z "$(mount | grep """$UNRAID_PARTITION on """)" ]; then + mkdir -p /tmp/UNRAID_TMP_MOUNT + echo "INFO: Temporarily mounting unRAID USB Flash drive to /tmp/UNRAID_TMP_MOUNT" + mount ${UNRAID_PARTITION} /tmp/UNRAID_TMP_MOUNT || exit 1 +fi + +SOURCE=$(mount | grep "${UNRAID_PARTITION} on " | awk '{print $3}') +if [ ! -d "$SOURCE/syslinux" ]; then + echo "" + echo "FAIL: unRAID Flash drive detected but unable to detect where it is currently mounted, aborting!" + echo "" + exit 1 +fi + +if [ ! -f "$SOURCE/syslinux/syslinux_linux" ]; then + echo "" + echo "FAIL: unRAID Flash drive detected but unable to locate unRAID install files, aborting!" + echo "" + exit 1 +fi + +while true; do + echo -n "Permit UEFI boot mode [Y/N]: " + read BOOT + if [[ ${BOOT^^} == N ]]; then + [[ -d $SOURCE/EFI && ! -d $SOURCE/EFI- ]] && mv $SOURCE/EFI $SOURCE/EFI-; break + elif [[ ${BOOT^^} == Y ]]; then + [[ -d $SOURCE/EFI- && ! -d $SOURCE/EFI ]] && mv $SOURCE/EFI- $SOURCE/EFI; break + else + echo "Please answer Y or N" + fi +done + +echo "INFO: unRAID USB Flash drive currently mounted to $SOURCE, copying temporary installer files to /tmp/UNRAID" +mkdir -p /tmp/UNRAID/syslinux +cp -rp $SOURCE/syslinux/* /tmp/UNRAID/syslinux + +umount $SOURCE +sync +[ -d /tmp/UNRAID_TMP_MOUNT ] && rmdir /tmp/UNRAID_TMP_MOUNT + +echo "" +echo "To continue you may need to enter your admin password" +sudo /tmp/UNRAID/syslinux/make_bootable_linux.sh $TARGET + +# clean up +rm -rf /tmp/UNRAID/syslinux +rmdir /tmp/UNRAID &>/dev/null + diff --git a/src/unraid/make_bootable_mac b/src/unraid/make_bootable_mac new file mode 100644 index 000000000..b762e3ad9 --- /dev/null +++ b/src/unraid/make_bootable_mac @@ -0,0 +1,73 @@ +#!/bin/bash + +################################################################################ +# This file is part of makebootable_mac used for the creation +# of a bootable USB thumb drive that unRAID can be run from +# Copyright (C) 2009-2012 Kyle Hiltner (stephan@openelec.tv) +# Copyright (C) 2013 Lime Technology +# +# 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 2, 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. +################################################################################ + +VERSION="v1.3" + +echo "INFO: make_bootable_mac $VERSION" +echo "" + +DISTRO=$(uname -s) +if [ $DISTRO != "Darwin" ]; then + echo "" + echo "FAIL: This script is only for Mac OS X systems, aborting!" + echo "" + exit 1 +fi + +if [ $(diskutil list | grep UNRAID | wc -l) == 1 ]; then + TARGET=$(diskutil list | sed -En 's/.+UNRAID.+(disk[0-9]+)s1/\/dev\/\1/p') + if [ -n "$TARGET" ]; then + echo "INFO: The following drive appears to be the unRAID USB Flash drive: " + echo $TARGET;diskutil list | grep UNRAID | awk '{print " " $4$5}' + echo "" + else + echo "FAIL: There appears to a drive present with the label UNRAID" + echo "but it's not installed on the first partition" + echo "" + exit 1 + fi +else + echo "FAIL: There appears to be no drive present with the label UNRAID" + echo "Exiting since there is not a drive labeled UNRAID" + echo "" + exit 1 +fi + +while true; do + echo -n "Permit UEFI boot mode [Y/N]: " + read BOOT + case $BOOT in + [nN]) + [[ -d /Volumes/UNRAID/EFI && ! -d /Volumes/UNRAID/EFI- ]] && mv /Volumes/UNRAID/EFI /Volumes/UNRAID/EFI- + break; + ;; + [yY]) + [[ -d /Volumes/UNRAID/EFI- && ! -d /Volumes/UNRAID/EFI ]] && mv /Volumes/UNRAID/EFI- /Volumes/UNRAID/EFI + break; + ;; + *) + echo "Please answer Y or N" + ;; + esac +done + +mkdir -p /tmp/UNRAID/syslinux ; cp -rp /Volumes/UNRAID/syslinux/* /tmp/UNRAID/syslinux + +echo -n "To continue please enter your admin " +sudo /tmp/UNRAID/syslinux/make_bootable_mac.sh $TARGET diff --git a/src/unraid/syslinux/ldlinux.c32 b/src/unraid/syslinux/ldlinux.c32 new file mode 100644 index 000000000..0d8f2ef0c Binary files /dev/null and b/src/unraid/syslinux/ldlinux.c32 differ diff --git a/src/unraid/syslinux/libcom32.c32 b/src/unraid/syslinux/libcom32.c32 new file mode 100644 index 000000000..aedab3330 Binary files /dev/null and b/src/unraid/syslinux/libcom32.c32 differ diff --git a/src/unraid/syslinux/libutil.c32 b/src/unraid/syslinux/libutil.c32 new file mode 100644 index 000000000..496fc303e Binary files /dev/null and b/src/unraid/syslinux/libutil.c32 differ diff --git a/src/unraid/syslinux/make_bootable_linux.sh b/src/unraid/syslinux/make_bootable_linux.sh new file mode 100644 index 000000000..0ed80ab41 --- /dev/null +++ b/src/unraid/syslinux/make_bootable_linux.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +################################################################################ +# This file is ported from makebootable_mac used for the creation +# of a bootable USB thumb drive that unRAID can be run from +# Copyright (C) 2009-2012 Kyle Hiltner (stephan@openelec.tv) +# Copyright (C) 2016 Lime Technology +# +# 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 2, 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. +################################################################################ + +VERBOSE="TRUE" # set to TRUE to put this script in debug mode +TARGET=$1 + +if [ "${VERBOSE}" = "TRUE" ]; then + exec 3>&1 + exec 4>&2 +else + exec 3> /dev/null + exec 4> /dev/null +fi + +if [ ! -b "${TARGET}" ]; then + echo "FAIL: Invalid block device ${TARGET}" + exit 1 +fi + +MOUNTED=$(mount | grep ${TARGET}) +if [ ! -z "${MOUNTED}" ]; then + echo "INFO: Unmounting ${TARGET}" + umount ${TARGET} 1>&3 2>&4 +fi + +echo "INFO: Installing Syslinux bootloader on ${TARGET}1" +/tmp/UNRAID/syslinux/syslinux_linux -f --install ${TARGET}1 1>&3 2>&4 + +echo "INFO: Writing MBR on $TARGET" +dd if=/tmp/UNRAID/syslinux/mbr.bin of=${TARGET} 1>&3 2>&4 + +sync + +echo "" +echo "INFO: the Unraid OS USB Flash drive is now bootable and may be ejected." +echo "" + +exit 0 diff --git a/src/unraid/syslinux/make_bootable_mac.sh b/src/unraid/syslinux/make_bootable_mac.sh new file mode 100644 index 000000000..c679ed339 --- /dev/null +++ b/src/unraid/syslinux/make_bootable_mac.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +################################################################################ +# This file is part of makebootable_mac used for the creation +# of a bootable USB thumb drive that unRAID can be run from +# Copyright (C) 2009-2012 Kyle Hiltner (stephan@openelec.tv) +# Copyright (C) 2013 Lime Technology +# +# 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 2, 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. +################################################################################ + +VERBOSE="TRUE" # set to TRUE to put this script in debug mode +TARGET=$1 + +if [ "${VERBOSE}" = "TRUE" ]; then + exec 3>&1 + exec 4>&2 +else + exec 3> /dev/null + exec 4> /dev/null +fi + +unmount_target(){ + MOUNTED=$(mount | grep ${TARGET}) + if [ ! -z "${MOUNTED}" ]; then + echo "INFO: Unmounting ${TARGET}" + sleep 2 + diskutil unmountDisk force ${TARGET} 1>&3 2>&4 + fi +} + +mount_target(){ + MOUNTED=$(mount | grep ${TARGET}) + if [ -z "${MOUNTED}" ]; then + echo "INFO: Mounting ${TARGET}" + sleep 2 + diskutil mountDisk ${TARGET} 1>&3 2>&4 + fi +} + +unmount_target +echo "INFO: Writing MBR on $TARGET" +dd if=/tmp/UNRAID/syslinux/mbr.bin of=${TARGET} 1>&3 2>&4 +mount_target +/tmp/UNRAID/syslinux/syslinux -f --install ${TARGET}s1 1>&3 2>&4 + +echo "" +echo "INFO: the Unraid OS USB Flash drive is now bootable and may be ejected." +echo "" + +exit 0 diff --git a/src/unraid/syslinux/mboot.c32 b/src/unraid/syslinux/mboot.c32 new file mode 100644 index 000000000..f23ecbec8 Binary files /dev/null and b/src/unraid/syslinux/mboot.c32 differ diff --git a/src/unraid/syslinux/mbr.bin b/src/unraid/syslinux/mbr.bin new file mode 100644 index 000000000..4ce117ea0 Binary files /dev/null and b/src/unraid/syslinux/mbr.bin differ diff --git a/src/unraid/syslinux/menu.c32 b/src/unraid/syslinux/menu.c32 new file mode 100644 index 000000000..a584b00c7 Binary files /dev/null and b/src/unraid/syslinux/menu.c32 differ diff --git a/src/unraid/syslinux/syslinux b/src/unraid/syslinux/syslinux new file mode 100644 index 000000000..d45803646 Binary files /dev/null and b/src/unraid/syslinux/syslinux differ diff --git a/src/unraid/syslinux/syslinux.cfg b/src/unraid/syslinux/syslinux.cfg new file mode 100644 index 000000000..460bcce91 --- /dev/null +++ b/src/unraid/syslinux/syslinux.cfg @@ -0,0 +1,19 @@ +default menu.c32 +menu title Lime Technology, Inc. +prompt 0 +timeout 50 +label Unraid OS + menu default + kernel /bzimage + append initrd=/bzroot +label Unraid OS GUI Mode + kernel /bzimage + append initrd=/bzroot,/bzroot-gui +label Unraid OS Safe Mode (no plugins, no GUI) + kernel /bzimage + append initrd=/bzroot unraidsafemode +label Unraid OS GUI Safe Mode (no plugins) + kernel /bzimage + append initrd=/bzroot,/bzroot-gui unraidsafemode +label Memtest86+ + kernel /memtest diff --git a/src/unraid/syslinux/syslinux.cfg- b/src/unraid/syslinux/syslinux.cfg- new file mode 100644 index 000000000..460bcce91 --- /dev/null +++ b/src/unraid/syslinux/syslinux.cfg- @@ -0,0 +1,19 @@ +default menu.c32 +menu title Lime Technology, Inc. +prompt 0 +timeout 50 +label Unraid OS + menu default + kernel /bzimage + append initrd=/bzroot +label Unraid OS GUI Mode + kernel /bzimage + append initrd=/bzroot,/bzroot-gui +label Unraid OS Safe Mode (no plugins, no GUI) + kernel /bzimage + append initrd=/bzroot unraidsafemode +label Unraid OS GUI Safe Mode (no plugins) + kernel /bzimage + append initrd=/bzroot,/bzroot-gui unraidsafemode +label Memtest86+ + kernel /memtest diff --git a/src/unraid/syslinux/syslinux.exe b/src/unraid/syslinux/syslinux.exe new file mode 100644 index 000000000..8c3c859ef Binary files /dev/null and b/src/unraid/syslinux/syslinux.exe differ diff --git a/src/unraid/syslinux/syslinux_linux b/src/unraid/syslinux/syslinux_linux new file mode 100644 index 000000000..14a100282 Binary files /dev/null and b/src/unraid/syslinux/syslinux_linux differ diff --git a/src/unraidguidvalidator.cpp b/src/unraidguidvalidator.cpp new file mode 100644 index 000000000..5c71c5081 --- /dev/null +++ b/src/unraidguidvalidator.cpp @@ -0,0 +1,52 @@ +#include "unraidguidvalidator.h" + +#include "config.h" + +#include + +UnraidGuidValidator::UnraidGuidValidator() +{ + _curl = curl_easy_init(); +} + +UnraidGuidValidator::~UnraidGuidValidator() +{ + curl_easy_cleanup(_curl); +} + +UnraidGuidValidator::Result UnraidGuidValidator::checkDevice(const Drivelist::DeviceDescriptor& deviceDescriptor) +{ + Result result; + if(!deviceDescriptor.vid.empty() && !deviceDescriptor.pid.empty() && !deviceDescriptor.serialNumber.empty()) + { + QString SerialPadded = QString::fromStdString(deviceDescriptor.serialNumber).rightJustified(16, '0').right(16); + result.guid = (QString::fromStdString(deviceDescriptor.vid) + "-" + QString::fromStdString(deviceDescriptor.pid) + "-" + SerialPadded.left(4) + "-" + SerialPadded.mid(4)).toUpper(); + + CURLcode res{CURLcode::CURLE_OK}; + long http_code{0}; + std::string data{""}; + if(_curl) + { + curl_easy_setopt(_curl, CURLOPT_CUSTOMREQUEST, "POST"); + curl_easy_setopt(_curl, CURLOPT_URL, UNRAID_GUID_URL); + curl_easy_setopt(_curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(_curl, CURLOPT_DEFAULT_PROTOCOL, "https"); + struct curl_slist *headers = NULL; + headers = curl_slist_append(headers, "Content-Type: application/x-www-form-urlencoded"); + curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, headers); + data = "guid=" + result.guid.toStdString(); + curl_easy_setopt(_curl, CURLOPT_POSTFIELDS, data.c_str()); + res = curl_easy_perform(_curl); + curl_slist_free_all(headers); + curl_easy_getinfo (_curl, CURLINFO_RESPONSE_CODE, &http_code); + } + + if (res == CURLcode::CURLE_OK && http_code != 403) + { + result.guidValid = true; + } + } + return result; +} + + diff --git a/src/unraidguidvalidator.h b/src/unraidguidvalidator.h new file mode 100644 index 000000000..548e69535 --- /dev/null +++ b/src/unraidguidvalidator.h @@ -0,0 +1,20 @@ +#include "dependencies/drivelist/src/drivelist.hpp" + +#include + +#include + +class UnraidGuidValidator { +public: + UnraidGuidValidator(); + ~UnraidGuidValidator(); + + struct Result { + QString guid{""}; + bool guidValid{false}; + }; + + Result checkDevice(const Drivelist::DeviceDescriptor& deviceDescriptor); +protected: + CURL *_curl{nullptr}; +}; diff --git a/src/windows/Dockerfile.win32dyn b/src/windows/Dockerfile.win32dyn new file mode 100644 index 000000000..6674f1557 --- /dev/null +++ b/src/windows/Dockerfile.win32dyn @@ -0,0 +1,37 @@ +FROM fffaraz/qt:mxe + +RUN apt update +RUN apt install python3-pip -y +RUN pip3 install packaging +RUN apt install nsis -y + +RUN \ + cd /opt/mxe && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qt5 && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtcharts && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtdeclarative && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtimageformats && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtmultimedia && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtscxml && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtserialport && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtservice && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtsvg && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qttools && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qttranslations && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtwebsockets && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtbase && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' qtwinextras && \ + make --jobs=$(nproc) JOBS=$(nproc) MXE_TARGETS='i686-w64-mingw32.shared' curl && \ + exit 0 + +RUN git clone https://github.com/saidinesh5/mxedeployqt.git + +ENV PATH="${PATH}:/opt/mxe/usr/i686-w64-mingw32.shared/qt5/bin:/mxedeployqt" + +RUN \ + set -eux && \ + echo $PATH && \ + ln -s /opt/mxe/usr/bin/i686-w64-mingw32.shared-cmake /usr/local/bin/cmake && \ + cmake --version && \ + qmake --version && \ + exit 0 \ No newline at end of file diff --git a/src/windows/rpi-imager.nsi.in b/src/windows/rpi-imager.nsi.in deleted file mode 100644 index 4f8a0660c..000000000 --- a/src/windows/rpi-imager.nsi.in +++ /dev/null @@ -1,1147 +0,0 @@ -############################################################################################ -# NSIS Installation Script created by NSIS Quick Setup Script Generator v1.09.18 -# Entirely Edited with NullSoft Scriptable Installation System -# by Vlasis K. Barkas aka Red Wine red_wine@freemail.gr Sep 2006 -############################################################################################ - -!define APP_NAME "Raspberry Pi Imager" -!define COMP_NAME "Raspberry Pi Ltd" -!define VERSION "@IMAGER_VERSION_STR@" -!define INSTALLER_NAME "imager-${VERSION}.exe" -!define COPYRIGHT "Raspberry Pi Ltd" -!define DESCRIPTION "Raspberry Pi Imager" -!define MAIN_APP_EXE "rpi-imager.exe" -!define INSTALL_TYPE "SetShellVarContext current" -!define REG_ROOT "HKCU" -!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}" -!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" - -# Window to close if running -!define EXE_TO_TERMINATE "rpi-imager.exe" - -###################################################################### - -VIProductVersion "@IMAGER_VERSION_MAJOR@.@IMAGER_VERSION_MINOR@.0.0" -VIAddVersionKey "ProductName" "${APP_NAME}" -VIAddVersionKey "CompanyName" "${COMP_NAME}" -VIAddVersionKey "LegalCopyright" "${COPYRIGHT}" -VIAddVersionKey "FileDescription" "${DESCRIPTION}" -VIAddVersionKey "FileVersion" "${VERSION}" -VIAddVersionKey "ProductVersion" "${VERSION}" - -###################################################################### - -SetCompressor LZMA -Name "${APP_NAME}" -Caption "${APP_NAME} ${VERSION}" -OutFile "${INSTALLER_NAME}" -BrandingText "${APP_NAME}" -XPStyle on -InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" "" -InstallDir "$PROGRAMFILES\Raspberry Pi Imager" - -###################################################################### - -### Stuff for signing uninstaller -!ifdef INNER - !echo "Inner invocation" ; just to see what's going on - OutFile "$%TEMP%\tempinstaller.exe" ; not really important where this is - SetCompress off -!else - !echo "Outer invocation" - - ; Call makensis again against current file, defining INNER. This writes an installer for us which, when - ; it is invoked, will just write the uninstaller to some location, and then exit. - - !makensis '/DINNER "${__FILE__}"' = 0 - - ; So now run that installer we just created as %TEMP%\tempinstaller.exe. Since it - ; calls quit the return value isn't zero. - - !system 'set __COMPAT_LAYER=RunAsInvoker &"$%TEMP%\tempinstaller.exe"' = 2 - - ; That will have written an uninstaller binary for us. Now we sign it with your - ; favorite code signing tool. - - !system '"@SIGNTOOL@" sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a "$%TEMP%\uninstall.exe"' = 0 - - ; Good. Now we can carry on writing the real installer. - - OutFile "${INSTALLER_NAME}" - SetCompressor /SOLID lzma - !finalize '"@SIGNTOOL@" sign /tr http://timestamp.digicert.com /td sha256 /fd sha256 /a "%1"' -!endif - -### - - -!include "MUI.nsh" -!include "WinVer.nsh" - -!define MUI_ABORTWARNING -!define MUI_UNABORTWARNING - -!insertmacro MUI_PAGE_WELCOME - -!ifdef LICENSE_TXT -!insertmacro MUI_PAGE_LICENSE "${LICENSE_TXT}" -!endif - -!ifdef REG_START_MENU -!define MUI_STARTMENUPAGE_NODISABLE -!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Raspberry Pi" -!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}" -!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}" -!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}" -!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder -!endif - -!insertmacro MUI_PAGE_INSTFILES - -!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}" -!insertmacro MUI_PAGE_FINISH - -!insertmacro MUI_UNPAGE_CONFIRM - -!insertmacro MUI_UNPAGE_INSTFILES - -!insertmacro MUI_UNPAGE_FINISH - -###################################################################### -# TRANSLATIONS -###################################################################### - -!insertmacro MUI_LANGUAGE "English" -!insertmacro MUI_LANGUAGE "Dutch" -!insertmacro MUI_LANGUAGE "Italian" - -LangString termMsg ${LANG_ENGLISH} "Raspberry Pi Imager seems to be running and busy.$\nDo you want to terminate process?" -LangString stopMsg ${LANG_ENGLISH} "Stopping Raspberry Pi Imager" -LangString win7Msg ${LANG_ENGLISH} "Windows 7 or above required" -LangString termMsg ${LANG_DUTCH} "Raspberry Pi Imager is momenteel actief.$\nWilt u het programma afsluiten?" -LangString stopMsg ${LANG_DUTCH} "Bezig met afsluiten Raspberry Pi Imager" -LangString win7Msg ${LANG_DUTCH} "Minimaal Windows 7 is vereist" -LangString termMsg ${LANG_ITALIAN} "Raspberry Pi Imager sembra essere in esecuzione e occupato.$\nVuoi terminare il processo Raspberry Pi Imager?" -LangString stopMsg ${LANG_ITALIAN} "Chiusura processo Raspberry Pi Imager" -LangString win7Msg ${LANG_ITALIAN} "Per l'esecuzione del programma è richiesto Windows 7 o versioni successive" - -###################################################################### - -!include WinMessages.nsh -!include Logiclib.nsh - -!macro FindWindowOfExe Exe - Push $1 - Push $2 - Push $3 - Push $R0 - Push $R1 - - SetPluginUnload alwaysoff - Push "0" ; Result code - System::Get "(i.r1, i) iss" - Pop $R0 - System::Call "user32::EnumWindows(k R0, i) i.s" - - loop: - Pop $0 - StrCmp $0 "callback1" 0 doneloop - System::Call 'user32.dll::GetWindowThreadProcessId(i r1, *i .r3) i .r2' - System::Call 'kernel32.dll::OpenProcess(i 1040, i 0, i r3) i .r2' - ${IfNot} $2 = 0 - System::Alloc 1024 - Pop $R1 - System::Call "Psapi::EnumProcessModules(i r2, i R1, i 1024, *i .r3) i .r0" - ${IfNot} $0 = 0 - System::Call "*$R1(i .r0)" - System::Call "Psapi::GetModuleBaseName(i r2, i r0, t .r3, i ${NSIS_MAX_STRLEN}) i .r0" - ${IfNot} $0 = 0 - ${If} $3 == "${Exe}" - # Only replace result value if it is first match - Pop $3 - ${If} $3 == 0 - Push $1 - ${Else} - Push $3 - ${EndIf} - ${EndIf} - ${EndIf} - ${EndIf} - System::Free $R1 - System::Call "kernel32::CloseHandle(i r2)" - ${EndIf} - - Push 1 - System::Call "$R0" - Goto loop - doneloop: - SetPluginUnload manual - - System::Free $R0 - Pop $0 - - Pop $R1 - Pop $R0 - Pop $3 - Pop $2 - Pop $1 -!macroend - -!macro TerminateApp - Push $0 ; window handle - Push $1 - Push $2 ; process handle - DetailPrint "$(stopMsg)" - #FindWindow $0 '' '${WND_TITLE}' - !insertmacro FindWindowOfExe '${EXE_TO_TERMINATE}' - IntCmp $0 0 done - System::Call 'user32.dll::GetWindowThreadProcessId(i r0, *i .r1) i .r2' - System::Call 'kernel32.dll::OpenProcess(i 0x00100001, i 0, i r1) i .r2' - SendMessage $0 ${WM_CLOSE} 0 0 /TIMEOUT=2000 - System::Call 'kernel32.dll::WaitForSingleObject(i r2, i 2000) i .r1' - IntCmp $1 0 close - MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(termMsg)" /SD IDYES IDYES terminate IDNO close - System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' - Quit - terminate: - System::Call 'kernel32.dll::TerminateProcess(i r2, i 0) i .r1' - close: - System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' - done: - Pop $2 - Pop $1 - Pop $0 -!macroend - -###################################################################### - -Section -MainProgram -${INSTALL_TYPE} -!insertmacro TerminateApp -SetOverwrite ifnewer -SetOutPath "$INSTDIR" -File "deploy\D3Dcompiler_47.dll" -#File "deploy\opengl32sw.dll" -File "deploy\libssl-1_1.dll" -File "deploy\libcrypto-1_1.dll" -File "deploy\fat32format.exe" -File "deploy\libEGL.dll" -File "deploy\libgcc_s_dw2-1.dll" -File "deploy\libGLESV2.dll" -File "deploy\libstdc++-6.dll" -File "deploy\libwinpthread-1.dll" -File "deploy\license.txt" -File "deploy\Qt5Core.dll" -File "deploy\Qt5Gui.dll" -File "deploy\Qt5Network.dll" -File "deploy\Qt5Qml.dll" -File "deploy\Qt5QmlModels.dll" -File "deploy\Qt5QmlWorkerScript.dll" -File "deploy\Qt5Quick.dll" -File "deploy\Qt5QuickControls2.dll" -File "deploy\Qt5QuickTemplates2.dll" -File "deploy\Qt5Svg.dll" -File "deploy\Qt5Widgets.dll" -File "deploy\Qt5WinExtras.dll" -File "deploy\rpi-imager.exe" -File "deploy\rpi-imager-cli.cmd" -SetOutPath "$INSTDIR\styles" -File "deploy\styles\qwindowsvistastyle.dll" -SetOutPath "$INSTDIR\QtQuick.2" -File "deploy\QtQuick.2\plugins.qmltypes" -File "deploy\QtQuick.2\qmldir" -File "deploy\QtQuick.2\qtquick2plugin.dll" -SetOutPath "$INSTDIR\QtQuick\Window.2" -File "deploy\QtQuick\Window.2\plugins.qmltypes" -File "deploy\QtQuick\Window.2\qmldir" -File "deploy\QtQuick\Window.2\windowplugin.dll" -SetOutPath "$INSTDIR\QtQuick\Templates.2" -File "deploy\QtQuick\Templates.2\plugins.qmltypes" -File "deploy\QtQuick\Templates.2\qmldir" -File "deploy\QtQuick\Templates.2\qtquicktemplates2plugin.dll" -SetOutPath "$INSTDIR\QtQuick\Layouts" -File "deploy\QtQuick\Layouts\plugins.qmltypes" -File "deploy\QtQuick\Layouts\qmldir" -File "deploy\QtQuick\Layouts\qquicklayoutsplugin.dll" -SetOutPath "$INSTDIR\QtQuick\Controls.2" -File "deploy\QtQuick\Controls.2\AbstractButton.qml" -File "deploy\QtQuick\Controls.2\Action.qml" -File "deploy\QtQuick\Controls.2\ActionGroup.qml" -File "deploy\QtQuick\Controls.2\ApplicationWindow.qml" -File "deploy\QtQuick\Controls.2\BusyIndicator.qml" -File "deploy\QtQuick\Controls.2\Button.qml" -File "deploy\QtQuick\Controls.2\ButtonGroup.qml" -File "deploy\QtQuick\Controls.2\CheckBox.qml" -File "deploy\QtQuick\Controls.2\CheckDelegate.qml" -File "deploy\QtQuick\Controls.2\ComboBox.qml" -File "deploy\QtQuick\Controls.2\Container.qml" -File "deploy\QtQuick\Controls.2\Control.qml" -File "deploy\QtQuick\Controls.2\DelayButton.qml" -File "deploy\QtQuick\Controls.2\Dial.qml" -File "deploy\QtQuick\Controls.2\Dialog.qml" -File "deploy\QtQuick\Controls.2\DialogButtonBox.qml" -File "deploy\QtQuick\Controls.2\Drawer.qml" -File "deploy\QtQuick\Controls.2\Frame.qml" -File "deploy\QtQuick\Controls.2\GroupBox.qml" -File "deploy\QtQuick\Controls.2\ItemDelegate.qml" -File "deploy\QtQuick\Controls.2\Label.qml" -File "deploy\QtQuick\Controls.2\Menu.qml" -File "deploy\QtQuick\Controls.2\MenuBar.qml" -File "deploy\QtQuick\Controls.2\MenuBarItem.qml" -File "deploy\QtQuick\Controls.2\MenuItem.qml" -File "deploy\QtQuick\Controls.2\MenuSeparator.qml" -File "deploy\QtQuick\Controls.2\Page.qml" -File "deploy\QtQuick\Controls.2\PageIndicator.qml" -File "deploy\QtQuick\Controls.2\Pane.qml" -File "deploy\QtQuick\Controls.2\plugins.qmltypes" -File "deploy\QtQuick\Controls.2\Popup.qml" -File "deploy\QtQuick\Controls.2\ProgressBar.qml" -File "deploy\QtQuick\Controls.2\qmldir" -File "deploy\QtQuick\Controls.2\qtquickcontrols2plugin.dll" -File "deploy\QtQuick\Controls.2\RadioButton.qml" -File "deploy\QtQuick\Controls.2\RadioDelegate.qml" -File "deploy\QtQuick\Controls.2\RangeSlider.qml" -File "deploy\QtQuick\Controls.2\RoundButton.qml" -File "deploy\QtQuick\Controls.2\ScrollBar.qml" -File "deploy\QtQuick\Controls.2\ScrollIndicator.qml" -File "deploy\QtQuick\Controls.2\ScrollView.qml" -File "deploy\QtQuick\Controls.2\Slider.qml" -File "deploy\QtQuick\Controls.2\SpinBox.qml" -File "deploy\QtQuick\Controls.2\SplitView.qml" -File "deploy\QtQuick\Controls.2\StackView.qml" -File "deploy\QtQuick\Controls.2\SwipeDelegate.qml" -File "deploy\QtQuick\Controls.2\SwipeView.qml" -File "deploy\QtQuick\Controls.2\Switch.qml" -File "deploy\QtQuick\Controls.2\SwitchDelegate.qml" -File "deploy\QtQuick\Controls.2\TabBar.qml" -File "deploy\QtQuick\Controls.2\TabButton.qml" -File "deploy\QtQuick\Controls.2\TextArea.qml" -File "deploy\QtQuick\Controls.2\TextField.qml" -File "deploy\QtQuick\Controls.2\ToolBar.qml" -File "deploy\QtQuick\Controls.2\ToolButton.qml" -File "deploy\QtQuick\Controls.2\ToolSeparator.qml" -File "deploy\QtQuick\Controls.2\ToolTip.qml" -File "deploy\QtQuick\Controls.2\Tumbler.qml" -SetOutPath "$INSTDIR\QtQuick\Controls.2\Universal" -File "deploy\QtQuick\Controls.2\Universal\ApplicationWindow.qml" -File "deploy\QtQuick\Controls.2\Universal\BusyIndicator.qml" -File "deploy\QtQuick\Controls.2\Universal\Button.qml" -File "deploy\QtQuick\Controls.2\Universal\CheckBox.qml" -File "deploy\QtQuick\Controls.2\Universal\CheckDelegate.qml" -File "deploy\QtQuick\Controls.2\Universal\CheckIndicator.qml" -File "deploy\QtQuick\Controls.2\Universal\ComboBox.qml" -File "deploy\QtQuick\Controls.2\Universal\DelayButton.qml" -File "deploy\QtQuick\Controls.2\Universal\Dial.qml" -File "deploy\QtQuick\Controls.2\Universal\Dialog.qml" -File "deploy\QtQuick\Controls.2\Universal\DialogButtonBox.qml" -File "deploy\QtQuick\Controls.2\Universal\Drawer.qml" -File "deploy\QtQuick\Controls.2\Universal\Frame.qml" -File "deploy\QtQuick\Controls.2\Universal\GroupBox.qml" -File "deploy\QtQuick\Controls.2\Universal\ItemDelegate.qml" -File "deploy\QtQuick\Controls.2\Universal\Label.qml" -File "deploy\QtQuick\Controls.2\Universal\Menu.qml" -File "deploy\QtQuick\Controls.2\Universal\MenuBar.qml" -File "deploy\QtQuick\Controls.2\Universal\MenuBarItem.qml" -File "deploy\QtQuick\Controls.2\Universal\MenuItem.qml" -File "deploy\QtQuick\Controls.2\Universal\MenuSeparator.qml" -File "deploy\QtQuick\Controls.2\Universal\Page.qml" -File "deploy\QtQuick\Controls.2\Universal\PageIndicator.qml" -File "deploy\QtQuick\Controls.2\Universal\Pane.qml" -File "deploy\QtQuick\Controls.2\Universal\plugins.qmltypes" -File "deploy\QtQuick\Controls.2\Universal\Popup.qml" -File "deploy\QtQuick\Controls.2\Universal\ProgressBar.qml" -File "deploy\QtQuick\Controls.2\Universal\qmldir" -File "deploy\QtQuick\Controls.2\Universal\qtquickcontrols2universalstyleplugin.dll" -File "deploy\QtQuick\Controls.2\Universal\RadioButton.qml" -File "deploy\QtQuick\Controls.2\Universal\RadioDelegate.qml" -File "deploy\QtQuick\Controls.2\Universal\RadioIndicator.qml" -File "deploy\QtQuick\Controls.2\Universal\RangeSlider.qml" -File "deploy\QtQuick\Controls.2\Universal\RoundButton.qml" -File "deploy\QtQuick\Controls.2\Universal\ScrollBar.qml" -File "deploy\QtQuick\Controls.2\Universal\ScrollIndicator.qml" -File "deploy\QtQuick\Controls.2\Universal\Slider.qml" -File "deploy\QtQuick\Controls.2\Universal\SpinBox.qml" -File "deploy\QtQuick\Controls.2\Universal\SplitView.qml" -File "deploy\QtQuick\Controls.2\Universal\StackView.qml" -File "deploy\QtQuick\Controls.2\Universal\SwipeDelegate.qml" -File "deploy\QtQuick\Controls.2\Universal\Switch.qml" -File "deploy\QtQuick\Controls.2\Universal\SwitchDelegate.qml" -File "deploy\QtQuick\Controls.2\Universal\SwitchIndicator.qml" -File "deploy\QtQuick\Controls.2\Universal\TabBar.qml" -File "deploy\QtQuick\Controls.2\Universal\TabButton.qml" -File "deploy\QtQuick\Controls.2\Universal\TextArea.qml" -File "deploy\QtQuick\Controls.2\Universal\TextField.qml" -File "deploy\QtQuick\Controls.2\Universal\ToolBar.qml" -File "deploy\QtQuick\Controls.2\Universal\ToolButton.qml" -File "deploy\QtQuick\Controls.2\Universal\ToolSeparator.qml" -File "deploy\QtQuick\Controls.2\Universal\ToolTip.qml" -File "deploy\QtQuick\Controls.2\Universal\Tumbler.qml" -SetOutPath "$INSTDIR\QtQuick\Controls.2\Material" -File "deploy\QtQuick\Controls.2\Material\ApplicationWindow.qml" -File "deploy\QtQuick\Controls.2\Material\BoxShadow.qml" -File "deploy\QtQuick\Controls.2\Material\BusyIndicator.qml" -File "deploy\QtQuick\Controls.2\Material\Button.qml" -File "deploy\QtQuick\Controls.2\Material\CheckBox.qml" -File "deploy\QtQuick\Controls.2\Material\CheckDelegate.qml" -File "deploy\QtQuick\Controls.2\Material\CheckIndicator.qml" -File "deploy\QtQuick\Controls.2\Material\ComboBox.qml" -File "deploy\QtQuick\Controls.2\Material\CursorDelegate.qml" -File "deploy\QtQuick\Controls.2\Material\DelayButton.qml" -File "deploy\QtQuick\Controls.2\Material\Dial.qml" -File "deploy\QtQuick\Controls.2\Material\Dialog.qml" -File "deploy\QtQuick\Controls.2\Material\DialogButtonBox.qml" -File "deploy\QtQuick\Controls.2\Material\Drawer.qml" -File "deploy\QtQuick\Controls.2\Material\ElevationEffect.qml" -File "deploy\QtQuick\Controls.2\Material\Frame.qml" -File "deploy\QtQuick\Controls.2\Material\GroupBox.qml" -File "deploy\QtQuick\Controls.2\Material\ItemDelegate.qml" -File "deploy\QtQuick\Controls.2\Material\Label.qml" -File "deploy\QtQuick\Controls.2\Material\Menu.qml" -File "deploy\QtQuick\Controls.2\Material\MenuBar.qml" -File "deploy\QtQuick\Controls.2\Material\MenuBarItem.qml" -File "deploy\QtQuick\Controls.2\Material\MenuItem.qml" -File "deploy\QtQuick\Controls.2\Material\MenuSeparator.qml" -File "deploy\QtQuick\Controls.2\Material\Page.qml" -File "deploy\QtQuick\Controls.2\Material\PageIndicator.qml" -File "deploy\QtQuick\Controls.2\Material\Pane.qml" -File "deploy\QtQuick\Controls.2\Material\plugins.qmltypes" -File "deploy\QtQuick\Controls.2\Material\Popup.qml" -File "deploy\QtQuick\Controls.2\Material\ProgressBar.qml" -File "deploy\QtQuick\Controls.2\Material\qmldir" -File "deploy\QtQuick\Controls.2\Material\qtquickcontrols2materialstyleplugin.dll" -File "deploy\QtQuick\Controls.2\Material\RadioButton.qml" -File "deploy\QtQuick\Controls.2\Material\RadioDelegate.qml" -File "deploy\QtQuick\Controls.2\Material\RadioIndicator.qml" -File "deploy\QtQuick\Controls.2\Material\RangeSlider.qml" -File "deploy\QtQuick\Controls.2\Material\RectangularGlow.qml" -File "deploy\QtQuick\Controls.2\Material\RoundButton.qml" -File "deploy\QtQuick\Controls.2\Material\ScrollBar.qml" -File "deploy\QtQuick\Controls.2\Material\ScrollIndicator.qml" -File "deploy\QtQuick\Controls.2\Material\Slider.qml" -File "deploy\QtQuick\Controls.2\Material\SliderHandle.qml" -File "deploy\QtQuick\Controls.2\Material\SpinBox.qml" -File "deploy\QtQuick\Controls.2\Material\SplitView.qml" -File "deploy\QtQuick\Controls.2\Material\StackView.qml" -File "deploy\QtQuick\Controls.2\Material\SwipeDelegate.qml" -File "deploy\QtQuick\Controls.2\Material\SwipeView.qml" -File "deploy\QtQuick\Controls.2\Material\Switch.qml" -File "deploy\QtQuick\Controls.2\Material\SwitchDelegate.qml" -File "deploy\QtQuick\Controls.2\Material\SwitchIndicator.qml" -File "deploy\QtQuick\Controls.2\Material\TabBar.qml" -File "deploy\QtQuick\Controls.2\Material\TabButton.qml" -File "deploy\QtQuick\Controls.2\Material\TextArea.qml" -File "deploy\QtQuick\Controls.2\Material\TextField.qml" -File "deploy\QtQuick\Controls.2\Material\ToolBar.qml" -File "deploy\QtQuick\Controls.2\Material\ToolButton.qml" -File "deploy\QtQuick\Controls.2\Material\ToolSeparator.qml" -File "deploy\QtQuick\Controls.2\Material\ToolTip.qml" -File "deploy\QtQuick\Controls.2\Material\Tumbler.qml" -SetOutPath "$INSTDIR\QtQuick\Controls.2\Imagine" -File "deploy\QtQuick\Controls.2\Imagine\ApplicationWindow.qml" -File "deploy\QtQuick\Controls.2\Imagine\BusyIndicator.qml" -File "deploy\QtQuick\Controls.2\Imagine\Button.qml" -File "deploy\QtQuick\Controls.2\Imagine\CheckBox.qml" -File "deploy\QtQuick\Controls.2\Imagine\CheckDelegate.qml" -File "deploy\QtQuick\Controls.2\Imagine\ComboBox.qml" -File "deploy\QtQuick\Controls.2\Imagine\DelayButton.qml" -File "deploy\QtQuick\Controls.2\Imagine\Dial.qml" -File "deploy\QtQuick\Controls.2\Imagine\Dialog.qml" -File "deploy\QtQuick\Controls.2\Imagine\DialogButtonBox.qml" -File "deploy\QtQuick\Controls.2\Imagine\Drawer.qml" -File "deploy\QtQuick\Controls.2\Imagine\Frame.qml" -File "deploy\QtQuick\Controls.2\Imagine\GroupBox.qml" -File "deploy\QtQuick\Controls.2\Imagine\ItemDelegate.qml" -File "deploy\QtQuick\Controls.2\Imagine\Label.qml" -File "deploy\QtQuick\Controls.2\Imagine\Menu.qml" -File "deploy\QtQuick\Controls.2\Imagine\MenuItem.qml" -File "deploy\QtQuick\Controls.2\Imagine\MenuSeparator.qml" -File "deploy\QtQuick\Controls.2\Imagine\Page.qml" -File "deploy\QtQuick\Controls.2\Imagine\PageIndicator.qml" -File "deploy\QtQuick\Controls.2\Imagine\Pane.qml" -File "deploy\QtQuick\Controls.2\Imagine\plugins.qmltypes" -File "deploy\QtQuick\Controls.2\Imagine\Popup.qml" -File "deploy\QtQuick\Controls.2\Imagine\ProgressBar.qml" -File "deploy\QtQuick\Controls.2\Imagine\qmldir" -File "deploy\QtQuick\Controls.2\Imagine\qtquickcontrols2imaginestyleplugin.dll" -File "deploy\QtQuick\Controls.2\Imagine\RadioButton.qml" -File "deploy\QtQuick\Controls.2\Imagine\RadioDelegate.qml" -File "deploy\QtQuick\Controls.2\Imagine\RangeSlider.qml" -File "deploy\QtQuick\Controls.2\Imagine\RoundButton.qml" -File "deploy\QtQuick\Controls.2\Imagine\ScrollBar.qml" -File "deploy\QtQuick\Controls.2\Imagine\ScrollIndicator.qml" -File "deploy\QtQuick\Controls.2\Imagine\Slider.qml" -File "deploy\QtQuick\Controls.2\Imagine\SpinBox.qml" -File "deploy\QtQuick\Controls.2\Imagine\SplitView.qml" -File "deploy\QtQuick\Controls.2\Imagine\StackView.qml" -File "deploy\QtQuick\Controls.2\Imagine\SwipeDelegate.qml" -File "deploy\QtQuick\Controls.2\Imagine\SwipeView.qml" -File "deploy\QtQuick\Controls.2\Imagine\Switch.qml" -File "deploy\QtQuick\Controls.2\Imagine\SwitchDelegate.qml" -File "deploy\QtQuick\Controls.2\Imagine\TabBar.qml" -File "deploy\QtQuick\Controls.2\Imagine\TabButton.qml" -File "deploy\QtQuick\Controls.2\Imagine\TextArea.qml" -File "deploy\QtQuick\Controls.2\Imagine\TextField.qml" -File "deploy\QtQuick\Controls.2\Imagine\ToolBar.qml" -File "deploy\QtQuick\Controls.2\Imagine\ToolButton.qml" -File "deploy\QtQuick\Controls.2\Imagine\ToolSeparator.qml" -File "deploy\QtQuick\Controls.2\Imagine\ToolTip.qml" -File "deploy\QtQuick\Controls.2\Imagine\Tumbler.qml" -SetOutPath "$INSTDIR\QtQuick\Controls.2\Fusion" -File "deploy\QtQuick\Controls.2\Fusion\ApplicationWindow.qml" -File "deploy\QtQuick\Controls.2\Fusion\BusyIndicator.qml" -File "deploy\QtQuick\Controls.2\Fusion\Button.qml" -File "deploy\QtQuick\Controls.2\Fusion\ButtonPanel.qml" -File "deploy\QtQuick\Controls.2\Fusion\CheckBox.qml" -File "deploy\QtQuick\Controls.2\Fusion\CheckDelegate.qml" -File "deploy\QtQuick\Controls.2\Fusion\CheckIndicator.qml" -File "deploy\QtQuick\Controls.2\Fusion\ComboBox.qml" -File "deploy\QtQuick\Controls.2\Fusion\DelayButton.qml" -File "deploy\QtQuick\Controls.2\Fusion\Dial.qml" -File "deploy\QtQuick\Controls.2\Fusion\Dialog.qml" -File "deploy\QtQuick\Controls.2\Fusion\DialogButtonBox.qml" -File "deploy\QtQuick\Controls.2\Fusion\Drawer.qml" -File "deploy\QtQuick\Controls.2\Fusion\Frame.qml" -File "deploy\QtQuick\Controls.2\Fusion\GroupBox.qml" -File "deploy\QtQuick\Controls.2\Fusion\ItemDelegate.qml" -File "deploy\QtQuick\Controls.2\Fusion\Label.qml" -File "deploy\QtQuick\Controls.2\Fusion\Menu.qml" -File "deploy\QtQuick\Controls.2\Fusion\MenuBar.qml" -File "deploy\QtQuick\Controls.2\Fusion\MenuBarItem.qml" -File "deploy\QtQuick\Controls.2\Fusion\MenuItem.qml" -File "deploy\QtQuick\Controls.2\Fusion\MenuSeparator.qml" -File "deploy\QtQuick\Controls.2\Fusion\Page.qml" -File "deploy\QtQuick\Controls.2\Fusion\PageIndicator.qml" -File "deploy\QtQuick\Controls.2\Fusion\Pane.qml" -File "deploy\QtQuick\Controls.2\Fusion\plugins.qmltypes" -File "deploy\QtQuick\Controls.2\Fusion\Popup.qml" -File "deploy\QtQuick\Controls.2\Fusion\ProgressBar.qml" -File "deploy\QtQuick\Controls.2\Fusion\qmldir" -File "deploy\QtQuick\Controls.2\Fusion\qtquickcontrols2fusionstyleplugin.dll" -File "deploy\QtQuick\Controls.2\Fusion\RadioButton.qml" -File "deploy\QtQuick\Controls.2\Fusion\RadioDelegate.qml" -File "deploy\QtQuick\Controls.2\Fusion\RadioIndicator.qml" -File "deploy\QtQuick\Controls.2\Fusion\RangeSlider.qml" -File "deploy\QtQuick\Controls.2\Fusion\RoundButton.qml" -File "deploy\QtQuick\Controls.2\Fusion\ScrollBar.qml" -File "deploy\QtQuick\Controls.2\Fusion\ScrollIndicator.qml" -File "deploy\QtQuick\Controls.2\Fusion\Slider.qml" -File "deploy\QtQuick\Controls.2\Fusion\SliderGroove.qml" -File "deploy\QtQuick\Controls.2\Fusion\SliderHandle.qml" -File "deploy\QtQuick\Controls.2\Fusion\SpinBox.qml" -File "deploy\QtQuick\Controls.2\Fusion\SplitView.qml" -File "deploy\QtQuick\Controls.2\Fusion\SwipeDelegate.qml" -File "deploy\QtQuick\Controls.2\Fusion\Switch.qml" -File "deploy\QtQuick\Controls.2\Fusion\SwitchDelegate.qml" -File "deploy\QtQuick\Controls.2\Fusion\SwitchIndicator.qml" -File "deploy\QtQuick\Controls.2\Fusion\TabBar.qml" -File "deploy\QtQuick\Controls.2\Fusion\TabButton.qml" -File "deploy\QtQuick\Controls.2\Fusion\TextArea.qml" -File "deploy\QtQuick\Controls.2\Fusion\TextField.qml" -File "deploy\QtQuick\Controls.2\Fusion\ToolBar.qml" -File "deploy\QtQuick\Controls.2\Fusion\ToolButton.qml" -File "deploy\QtQuick\Controls.2\Fusion\ToolSeparator.qml" -File "deploy\QtQuick\Controls.2\Fusion\ToolTip.qml" -File "deploy\QtQuick\Controls.2\Fusion\Tumbler.qml" -SetOutPath "$INSTDIR\QtGraphicalEffects" -File "deploy\QtGraphicalEffects\Blend.qml" -File "deploy\QtGraphicalEffects\BrightnessContrast.qml" -File "deploy\QtGraphicalEffects\Colorize.qml" -File "deploy\QtGraphicalEffects\ColorOverlay.qml" -File "deploy\QtGraphicalEffects\ConicalGradient.qml" -File "deploy\QtGraphicalEffects\Desaturate.qml" -File "deploy\QtGraphicalEffects\DirectionalBlur.qml" -File "deploy\QtGraphicalEffects\Displace.qml" -File "deploy\QtGraphicalEffects\DropShadow.qml" -File "deploy\QtGraphicalEffects\FastBlur.qml" -File "deploy\QtGraphicalEffects\GammaAdjust.qml" -File "deploy\QtGraphicalEffects\GaussianBlur.qml" -File "deploy\QtGraphicalEffects\Glow.qml" -File "deploy\QtGraphicalEffects\HueSaturation.qml" -File "deploy\QtGraphicalEffects\InnerShadow.qml" -File "deploy\QtGraphicalEffects\LevelAdjust.qml" -File "deploy\QtGraphicalEffects\LinearGradient.qml" -File "deploy\QtGraphicalEffects\MaskedBlur.qml" -File "deploy\QtGraphicalEffects\OpacityMask.qml" -File "deploy\QtGraphicalEffects\plugins.qmltypes" -File "deploy\QtGraphicalEffects\qmldir" -File "deploy\QtGraphicalEffects\qtgraphicaleffectsplugin.dll" -File "deploy\QtGraphicalEffects\RadialBlur.qml" -File "deploy\QtGraphicalEffects\RadialGradient.qml" -File "deploy\QtGraphicalEffects\RectangularGlow.qml" -File "deploy\QtGraphicalEffects\RecursiveBlur.qml" -File "deploy\QtGraphicalEffects\ThresholdMask.qml" -File "deploy\QtGraphicalEffects\ZoomBlur.qml" -SetOutPath "$INSTDIR\QtGraphicalEffects\private" -File "deploy\QtGraphicalEffects\private\DropShadowBase.qml" -File "deploy\QtGraphicalEffects\private\DropShadowBase.qmlc" -File "deploy\QtGraphicalEffects\private\FastGlow.qml" -File "deploy\QtGraphicalEffects\private\FastGlow.qmlc" -File "deploy\QtGraphicalEffects\private\FastInnerShadow.qml" -File "deploy\QtGraphicalEffects\private\FastInnerShadow.qmlc" -File "deploy\QtGraphicalEffects\private\FastMaskedBlur.qml" -File "deploy\QtGraphicalEffects\private\FastMaskedBlur.qmlc" -File "deploy\QtGraphicalEffects\private\GaussianDirectionalBlur.qml" -File "deploy\QtGraphicalEffects\private\GaussianDirectionalBlur.qmlc" -File "deploy\QtGraphicalEffects\private\GaussianGlow.qml" -File "deploy\QtGraphicalEffects\private\GaussianGlow.qmlc" -File "deploy\QtGraphicalEffects\private\GaussianInnerShadow.qml" -File "deploy\QtGraphicalEffects\private\GaussianInnerShadow.qmlc" -File "deploy\QtGraphicalEffects\private\GaussianMaskedBlur.qml" -File "deploy\QtGraphicalEffects\private\GaussianMaskedBlur.qmlc" -File "deploy\QtGraphicalEffects\private\qmldir" -File "deploy\QtGraphicalEffects\private\qtgraphicaleffectsprivate.dll" -#SetOutPath "$INSTDIR\Qt\labs\settings" -#File "deploy\Qt\labs\settings\plugins.qmltypes" -#File "deploy\Qt\labs\settings\qmldir" -#File "deploy\Qt\labs\settings\qmlsettingsplugin.dll" -#SetOutPath "$INSTDIR\qmltooling" -#File "deploy\qmltooling\qmldbg_debugger.dll" -#File "deploy\qmltooling\qmldbg_inspector.dll" -#File "deploy\qmltooling\qmldbg_local.dll" -#File "deploy\qmltooling\qmldbg_messages.dll" -#File "deploy\qmltooling\qmldbg_native.dll" -#File "deploy\qmltooling\qmldbg_nativedebugger.dll" -#File "deploy\qmltooling\qmldbg_preview.dll" -#File "deploy\qmltooling\qmldbg_profiler.dll" -#File "deploy\qmltooling\qmldbg_quickprofiler.dll" -#File "deploy\qmltooling\qmldbg_server.dll" -#File "deploy\qmltooling\qmldbg_tcp.dll" -SetOutPath "$INSTDIR\platforms" -File "deploy\platforms\qwindows.dll" -SetOutPath "$INSTDIR\imageformats" -File "deploy\imageformats\qicns.dll" -File "deploy\imageformats\qico.dll" -File "deploy\imageformats\qjpeg.dll" -File "deploy\imageformats\qsvg.dll" -File "deploy\imageformats\qtga.dll" -File "deploy\imageformats\qwbmp.dll" -SetOutPath "$INSTDIR\iconengines" -File "deploy\iconengines\qsvgicon.dll" -SetOutPath "$INSTDIR\bearer" -File "deploy\bearer\qgenericbearer.dll" -SectionEnd - -###################################################################### - -Section -Icons_Reg - -!ifndef INNER -SetOutPath "$INSTDIR" -File "$%TEMP%\uninstall.exe" -!endif - -!ifdef REG_START_MENU -!insertmacro MUI_STARTMENU_WRITE_BEGIN Application -CreateDirectory "$SMPROGRAMS\$SM_Folder" -CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" -!ifdef WEB_SITE -WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" -CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME} Website.lnk" "$INSTDIR\${APP_NAME} website.url" -!endif -!insertmacro MUI_STARTMENU_WRITE_END -!endif - -!ifndef REG_START_MENU -CreateDirectory "$SMPROGRAMS\Raspberry Pi" -CreateShortCut "$SMPROGRAMS\Raspberry Pi\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" -!ifdef WEB_SITE -WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" -CreateShortCut "$SMPROGRAMS\Raspberry Pi\${APP_NAME} Website.lnk" "$INSTDIR\${APP_NAME} website.url" -!endif -!endif - -WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}" -WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "${APP_NAME}" -WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe" -WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE}" -WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}" -WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}" - -!ifdef WEB_SITE -WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "${WEB_SITE}" -!endif - - -WriteRegStr SHCTX "Software\Classes\.zip\OpenWithProgIds" "RPI_IMAGINGUTILITY" "" -WriteRegStr SHCTX "Software\Classes\.gz\OpenWithProgIds" "RPI_IMAGINGUTILITY" "" -WriteRegStr SHCTX "Software\Classes\.xz\OpenWithProgIds" "RPI_IMAGINGUTILITY" "" -WriteRegStr SHCTX "Software\Classes\.img\OpenWithProgIds" "RPI_IMAGINGUTILITY" "" -WriteRegStr SHCTX "Software\Classes\.zstd\OpenWithProgIds" "RPI_IMAGINGUTILITY" "" -WriteRegStr SHCTX "Software\Classes\RPI_IMAGINGUTILITY\shell\open" "FriendlyAppName" "Raspberry Pi Imager" -WriteRegStr SHCTX "Software\Classes\RPI_IMAGINGUTILITY\shell\open\command" "" '"$INSTDIR\rpi-imager.exe" "%1"' - -SectionEnd - -###################################################################### - -Section Uninstall -${INSTALL_TYPE} -Delete "$INSTDIR\D3Dcompiler_47.dll" -Delete "$INSTDIR\opengl32sw.dll" -Delete "$INSTDIR\libssl-1_1.dll" -Delete "$INSTDIR\libcrypto-1_1.dll" -Delete "$INSTDIR\fat32format.exe" -Delete "$INSTDIR\libEGL.dll" -Delete "$INSTDIR\libgcc_s_dw2-1.dll" -Delete "$INSTDIR\libGLESV2.dll" -Delete "$INSTDIR\libstdc++-6.dll" -Delete "$INSTDIR\libwinpthread-1.dll" -Delete "$INSTDIR\license.txt" -Delete "$INSTDIR\Qt5Core.dll" -Delete "$INSTDIR\Qt5Gui.dll" -Delete "$INSTDIR\Qt5Network.dll" -Delete "$INSTDIR\Qt5Qml.dll" -Delete "$INSTDIR\Qt5QmlModels.dll" -Delete "$INSTDIR\Qt5QmlWorkerScript.dll" -Delete "$INSTDIR\Qt5Quick.dll" -Delete "$INSTDIR\Qt5QuickControls2.dll" -Delete "$INSTDIR\Qt5QuickTemplates2.dll" -Delete "$INSTDIR\Qt5Svg.dll" -Delete "$INSTDIR\Qt5Widgets.dll" -Delete "$INSTDIR\Qt5WinExtras.dll" -# Old name -Delete "$INSTDIR\imagingutility.exe" -Delete "$INSTDIR\rpi-imager.exe" -Delete "$INSTDIR\rpi-imager-cli.cmd" -Delete "$INSTDIR\styles\qwindowsvistastyle.dll" -Delete "$INSTDIR\QtQuick.2\plugins.qmltypes" -Delete "$INSTDIR\QtQuick.2\qmldir" -Delete "$INSTDIR\QtQuick.2\qtquick2plugin.dll" -Delete "$INSTDIR\QtQuick\Window.2\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Window.2\qmldir" -Delete "$INSTDIR\QtQuick\Window.2\windowplugin.dll" -Delete "$INSTDIR\QtQuick\Templates.2\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Templates.2\qmldir" -Delete "$INSTDIR\QtQuick\Templates.2\qtquicktemplates2plugin.dll" -Delete "$INSTDIR\QtQuick\Layouts\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Layouts\qmldir" -Delete "$INSTDIR\QtQuick\Layouts\qquicklayoutsplugin.dll" -Delete "$INSTDIR\QtQuick\Controls.2\AbstractButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Action.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ActionGroup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ApplicationWindow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\BusyIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Button.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ButtonGroup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\CheckBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\CheckDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ComboBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Container.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Control.qml" -Delete "$INSTDIR\QtQuick\Controls.2\DelayButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Dial.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Dialog.qml" -Delete "$INSTDIR\QtQuick\Controls.2\DialogButtonBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Drawer.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Frame.qml" -Delete "$INSTDIR\QtQuick\Controls.2\GroupBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ItemDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Label.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Menu.qml" -Delete "$INSTDIR\QtQuick\Controls.2\MenuBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\MenuBarItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\MenuItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\MenuSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Page.qml" -Delete "$INSTDIR\QtQuick\Controls.2\PageIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Pane.qml" -Delete "$INSTDIR\QtQuick\Controls.2\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Controls.2\Popup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ProgressBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\qmldir" -Delete "$INSTDIR\QtQuick\Controls.2\qtquickcontrols2plugin.dll" -Delete "$INSTDIR\QtQuick\Controls.2\RadioButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\RadioDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\RangeSlider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\RoundButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ScrollBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ScrollIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ScrollView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Slider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\SpinBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\SplitView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\StackView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\SwipeDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\SwipeView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Switch.qml" -Delete "$INSTDIR\QtQuick\Controls.2\SwitchDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\TabBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\TabButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\TextArea.qml" -Delete "$INSTDIR\QtQuick\Controls.2\TextField.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ToolBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ToolButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ToolSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\ToolTip.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Tumbler.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ApplicationWindow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\BusyIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Button.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\CheckBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\CheckDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\CheckIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ComboBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\DelayButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Dial.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Dialog.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\DialogButtonBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Drawer.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Frame.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\GroupBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ItemDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Label.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Menu.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\MenuBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\MenuBarItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\MenuItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\MenuSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Page.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\PageIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Pane.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Popup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ProgressBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\qmldir" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\qtquickcontrols2universalstyleplugin.dll" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\RadioButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\RadioDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\RadioIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\RangeSlider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\RoundButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ScrollBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ScrollIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Slider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\SpinBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\SplitView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\StackView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\SwipeDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Switch.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\SwitchDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\SwitchIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\TabBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\TabButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\TextArea.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\TextField.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ToolBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ToolButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ToolSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\ToolTip.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Universal\Tumbler.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ApplicationWindow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\BoxShadow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\BusyIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Button.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\CheckBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\CheckDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\CheckIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ComboBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\CursorDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\DelayButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Dial.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Dialog.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\DialogButtonBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Drawer.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ElevationEffect.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Frame.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\GroupBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ItemDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Label.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Menu.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\MenuBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\MenuBarItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\MenuItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\MenuSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Page.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\PageIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Pane.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Popup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ProgressBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\qmldir" -Delete "$INSTDIR\QtQuick\Controls.2\Material\qtquickcontrols2materialstyleplugin.dll" -Delete "$INSTDIR\QtQuick\Controls.2\Material\RadioButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\RadioDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\RadioIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\RangeSlider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\RectangularGlow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\RoundButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ScrollBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ScrollIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Slider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SliderHandle.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SpinBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SplitView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\StackView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SwipeDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SwipeView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Switch.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SwitchDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\SwitchIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\TabBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\TabButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\TextArea.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\TextField.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ToolBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ToolButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ToolSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\ToolTip.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Material\Tumbler.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ApplicationWindow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\BusyIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Button.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\CheckBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\CheckDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ComboBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\DelayButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Dial.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Dialog.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\DialogButtonBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Drawer.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Frame.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\GroupBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ItemDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Label.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Menu.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\MenuItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\MenuSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Page.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\PageIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Pane.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Popup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ProgressBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\qmldir" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\qtquickcontrols2imaginestyleplugin.dll" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\RadioButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\RadioDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\RangeSlider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\RoundButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ScrollBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ScrollIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Slider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\SpinBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\SplitView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\StackView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\SwipeDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\SwipeView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Switch.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\SwitchDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\TabBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\TabButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\TextArea.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\TextField.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ToolBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ToolButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ToolSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\ToolTip.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Imagine\Tumbler.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ApplicationWindow.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\BusyIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Button.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ButtonPanel.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\CheckBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\CheckDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\CheckIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ComboBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\DelayButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Dial.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Dialog.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\DialogButtonBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Drawer.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Frame.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\GroupBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ItemDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Label.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Menu.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\MenuBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\MenuBarItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\MenuItem.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\MenuSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Page.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\PageIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Pane.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\plugins.qmltypes" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Popup.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ProgressBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\qmldir" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\qtquickcontrols2fusionstyleplugin.dll" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\RadioButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\RadioDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\RadioIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\RangeSlider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\RoundButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ScrollBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ScrollIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Slider.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SliderGroove.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SliderHandle.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SpinBox.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SplitView.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SwipeDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Switch.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SwitchDelegate.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\SwitchIndicator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\TabBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\TabButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\TextArea.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\TextField.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ToolBar.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ToolButton.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ToolSeparator.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\ToolTip.qml" -Delete "$INSTDIR\QtQuick\Controls.2\Fusion\Tumbler.qml" -Delete "$INSTDIR\QtGraphicalEffects\Blend.qml" -Delete "$INSTDIR\QtGraphicalEffects\BrightnessContrast.qml" -Delete "$INSTDIR\QtGraphicalEffects\Colorize.qml" -Delete "$INSTDIR\QtGraphicalEffects\ColorOverlay.qml" -Delete "$INSTDIR\QtGraphicalEffects\ConicalGradient.qml" -Delete "$INSTDIR\QtGraphicalEffects\Desaturate.qml" -Delete "$INSTDIR\QtGraphicalEffects\DirectionalBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\Displace.qml" -Delete "$INSTDIR\QtGraphicalEffects\DropShadow.qml" -Delete "$INSTDIR\QtGraphicalEffects\FastBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\GammaAdjust.qml" -Delete "$INSTDIR\QtGraphicalEffects\GaussianBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\Glow.qml" -Delete "$INSTDIR\QtGraphicalEffects\HueSaturation.qml" -Delete "$INSTDIR\QtGraphicalEffects\InnerShadow.qml" -Delete "$INSTDIR\QtGraphicalEffects\LevelAdjust.qml" -Delete "$INSTDIR\QtGraphicalEffects\LinearGradient.qml" -Delete "$INSTDIR\QtGraphicalEffects\MaskedBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\OpacityMask.qml" -Delete "$INSTDIR\QtGraphicalEffects\plugins.qmltypes" -Delete "$INSTDIR\QtGraphicalEffects\qmldir" -Delete "$INSTDIR\QtGraphicalEffects\qtgraphicaleffectsplugin.dll" -Delete "$INSTDIR\QtGraphicalEffects\RadialBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\RadialGradient.qml" -Delete "$INSTDIR\QtGraphicalEffects\RectangularGlow.qml" -Delete "$INSTDIR\QtGraphicalEffects\RecursiveBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\ThresholdMask.qml" -Delete "$INSTDIR\QtGraphicalEffects\ZoomBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\DropShadowBase.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\DropShadowBase.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\FastGlow.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\FastGlow.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\FastInnerShadow.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\FastInnerShadow.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\FastMaskedBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\FastMaskedBlur.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianDirectionalBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianDirectionalBlur.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianGlow.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianGlow.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianInnerShadow.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianInnerShadow.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianMaskedBlur.qml" -Delete "$INSTDIR\QtGraphicalEffects\private\GaussianMaskedBlur.qmlc" -Delete "$INSTDIR\QtGraphicalEffects\private\qmldir" -Delete "$INSTDIR\QtGraphicalEffects\private\qtgraphicaleffectsprivate.dll" -Delete "$INSTDIR\Qt\labs\settings\plugins.qmltypes" -Delete "$INSTDIR\Qt\labs\settings\qmldir" -Delete "$INSTDIR\Qt\labs\settings\qmlsettingsplugin.dll" -Delete "$INSTDIR\qmltooling\qmldbg_debugger.dll" -Delete "$INSTDIR\qmltooling\qmldbg_inspector.dll" -Delete "$INSTDIR\qmltooling\qmldbg_local.dll" -Delete "$INSTDIR\qmltooling\qmldbg_messages.dll" -Delete "$INSTDIR\qmltooling\qmldbg_native.dll" -Delete "$INSTDIR\qmltooling\qmldbg_nativedebugger.dll" -Delete "$INSTDIR\qmltooling\qmldbg_preview.dll" -Delete "$INSTDIR\qmltooling\qmldbg_profiler.dll" -Delete "$INSTDIR\qmltooling\qmldbg_quickprofiler.dll" -Delete "$INSTDIR\qmltooling\qmldbg_server.dll" -Delete "$INSTDIR\qmltooling\qmldbg_tcp.dll" -Delete "$INSTDIR\platforms\qwindows.dll" -Delete "$INSTDIR\imageformats\qicns.dll" -Delete "$INSTDIR\imageformats\qico.dll" -Delete "$INSTDIR\imageformats\qjpeg.dll" -Delete "$INSTDIR\imageformats\qsvg.dll" -Delete "$INSTDIR\imageformats\qtga.dll" -Delete "$INSTDIR\imageformats\qwbmp.dll" -Delete "$INSTDIR\iconengines\qsvgicon.dll" -Delete "$INSTDIR\bearer\qgenericbearer.dll" - -RmDir "$INSTDIR\bearer" -RmDir "$INSTDIR\iconengines" -RmDir "$INSTDIR\imageformats" -RmDir "$INSTDIR\platforms" -RmDir "$INSTDIR\qmltooling" -RmDir "$INSTDIR\Qt\labs\settings" -RmDir "$INSTDIR\Qt\labs" -RmDir "$INSTDIR\Qt" -RmDir "$INSTDIR\QtGraphicalEffects\private" -RmDir "$INSTDIR\QtGraphicalEffects" -RmDir "$INSTDIR\QtQuick\Controls.2\Fusion" -RmDir "$INSTDIR\QtQuick\Controls.2\Imagine" -RmDir "$INSTDIR\QtQuick\Controls.2\Material" -RmDir "$INSTDIR\QtQuick\Controls.2\Universal" -RmDir "$INSTDIR\QtQuick\Controls.2" -RmDir "$INSTDIR\QtQuick\Layouts" -RmDir "$INSTDIR\QtQuick\Templates.2" -RmDir "$INSTDIR\QtQuick\Window.2" -RmDir "$INSTDIR\QtQuick" -RmDir "$INSTDIR\QtQuick.2" -RmDir "$INSTDIR\styles" - -Delete "$INSTDIR\uninstall.exe" -!ifdef WEB_SITE -Delete "$INSTDIR\${APP_NAME} website.url" -!endif - -RmDir "$INSTDIR" - -!ifdef REG_START_MENU -!insertmacro MUI_STARTMENU_GETFOLDER "Application" $SM_Folder -Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" -!ifdef WEB_SITE -Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME} Website.lnk" -!endif -RmDir "$SMPROGRAMS\$SM_Folder" -!endif - -!ifndef REG_START_MENU -Delete "$SMPROGRAMS\Raspberry Pi\${APP_NAME}.lnk" -!ifdef WEB_SITE -Delete "$SMPROGRAMS\Raspberry Pi\${APP_NAME} Website.lnk" -!endif -RmDir "$SMPROGRAMS\Raspberry Pi" -!endif - -DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" -DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" - -# File associations -DeleteRegValue SHCTX "Software\Classes\.zip\OpenWithProgIds" "RPI_IMAGINGUTILITY" -DeleteRegValue SHCTX "Software\Classes\.gz\OpenWithProgIds" "RPI_IMAGINGUTILITY" -DeleteRegValue SHCTX "Software\Classes\.xz\OpenWithProgIds" "RPI_IMAGINGUTILITY" -DeleteRegValue SHCTX "Software\Classes\.img\OpenWithProgIds" "RPI_IMAGINGUTILITY" -DeleteRegValue SHCTX "Software\Classes\.zstd\OpenWithProgIds" "RPI_IMAGINGUTILITY" -DeleteRegKey SHCTX "Software\Classes\RPI_IMAGINGUTILITY" - -DeleteRegKey ${REG_ROOT} "" - -RMDir /r "$APPDATA\Raspberry Pi\Imager" -RMDir "$APPDATA\Raspberry Pi" -RMDir /r "$LOCALAPPDATA\Raspberry Pi\Imager" -RMDir "$LOCALAPPDATA\Raspberry Pi" - -SectionEnd - -###################################################################### - - -Function .onInit -!ifdef INNER - - ; If INNER is defined, then we aren't supposed to do anything except write out - ; the uninstaller. This is better than processing a command line option as it means - ; this entire code path is not present in the final (real) installer. - SetSilent silent - WriteUninstaller "$%TEMP%\uninstall.exe" - Quit ; just bail out quickly when running the "inner" installer -!else - ${IfNot} ${AtLeastWin7} - MessageBox MB_OK "$(win7Msg)" - Quit - ${EndIf} -!endif -FunctionEnd diff --git a/src/windows/rpi-imager-cli.cmd b/src/windows/unraid-usb-creator-cli.cmd similarity index 52% rename from src/windows/rpi-imager-cli.cmd rename to src/windows/unraid-usb-creator-cli.cmd index 99c15f717..594e5a5d3 100644 --- a/src/windows/rpi-imager-cli.cmd +++ b/src/windows/unraid-usb-creator-cli.cmd @@ -1,9 +1,9 @@ @echo off rem -rem For scripting: call rpi-imager.exe and wait until it finished before continuing +rem For scripting: call unraid-imager.exe and wait until it finished before continuing rem This is necessary because it is compiled as GUI application, and Windows rem normalling does not wait until those exit rem -start /WAIT rpi-imager.exe --cli %* +start /WAIT unraid-usb-creator.exe --cli %* diff --git a/src/windows/rpi-imager.manifest b/src/windows/unraid-usb-creator.manifest similarity index 81% rename from src/windows/rpi-imager.manifest rename to src/windows/unraid-usb-creator.manifest index 439fefc8f..1c3b03e92 100644 --- a/src/windows/rpi-imager.manifest +++ b/src/windows/unraid-usb-creator.manifest @@ -1,7 +1,7 @@ - - Raspberry Pi Imager + + Unraid USB Creator diff --git a/src/windows/unraid-usb-creator.nsi.in b/src/windows/unraid-usb-creator.nsi.in new file mode 100644 index 000000000..6c0bb3ab2 --- /dev/null +++ b/src/windows/unraid-usb-creator.nsi.in @@ -0,0 +1,292 @@ +############################################################################################ +# NSIS Installation Script created by NSIS Quick Setup Script Generator v1.09.18 +# Entirely Edited with NullSoft Scriptable Installation System +# by Vlasis K. Barkas aka Red Wine red_wine@freemail.gr Sep 2006 +############################################################################################ + +!define APP_NAME "Unraid USB Creator" +!define COMP_NAME "Lime Technology, Inc" +!define VERSION "@IMAGER_VERSION_STR@" +!define INSTALLER_NAME "unraid-usb-creator-${VERSION}.exe" +!define COPYRIGHT "Lime Technology, Inc" +!define DESCRIPTION "Unraid USB Creator" +!define MAIN_APP_EXE "unraid-usb-creator.exe" +!define INSTALL_TYPE "SetShellVarContext current" +!define REG_ROOT "HKCU" +!define REG_APP_PATH "Software\Microsoft\Windows\CurrentVersion\App Paths\${MAIN_APP_EXE}" +!define UNINSTALL_PATH "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" + +# Window to close if running +!define EXE_TO_TERMINATE "unraid-usb-creator.exe" + +###################################################################### + +VIProductVersion "@IMAGER_VERSION_MAJOR@.@IMAGER_VERSION_MINOR@.@IMAGER_VERSION_PATCH@.0" +VIAddVersionKey "ProductName" "${APP_NAME}" +VIAddVersionKey "CompanyName" "${COMP_NAME}" +VIAddVersionKey "LegalCopyright" "${COPYRIGHT}" +VIAddVersionKey "FileDescription" "${DESCRIPTION}" +VIAddVersionKey "FileVersion" "${VERSION}" +VIAddVersionKey "ProductVersion" "${VERSION}" + +###################################################################### + +SetCompressor LZMA +Name "${APP_NAME}" +Caption "${APP_NAME} ${VERSION}" +OutFile "${INSTALLER_NAME}" +BrandingText "${APP_NAME}" +XPStyle on +InstallDirRegKey "${REG_ROOT}" "${REG_APP_PATH}" "" +InstallDir "$PROGRAMFILES\Unraid USB Creator" + +###################################################################### + +!include "MUI.nsh" +!include "WinVer.nsh" + +!define MUI_ABORTWARNING +!define MUI_UNABORTWARNING + +!insertmacro MUI_PAGE_WELCOME + +!ifdef LICENSE_TXT +!insertmacro MUI_PAGE_LICENSE "${LICENSE_TXT}" +!endif + +!ifdef REG_START_MENU +!define MUI_STARTMENUPAGE_NODISABLE +!define MUI_STARTMENUPAGE_DEFAULTFOLDER "Unraid" +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "${REG_ROOT}" +!define MUI_STARTMENUPAGE_REGISTRY_KEY "${UNINSTALL_PATH}" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${REG_START_MENU}" +!insertmacro MUI_PAGE_STARTMENU Application $SM_Folder +!endif + +!insertmacro MUI_PAGE_INSTFILES + +!define MUI_FINISHPAGE_RUN "$INSTDIR\${MAIN_APP_EXE}" +!insertmacro MUI_PAGE_FINISH + +!insertmacro MUI_UNPAGE_CONFIRM + +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_UNPAGE_FINISH + +###################################################################### +# TRANSLATIONS +###################################################################### + +!insertmacro MUI_LANGUAGE "English" +!insertmacro MUI_LANGUAGE "Dutch" +!insertmacro MUI_LANGUAGE "Italian" + +LangString termMsg ${LANG_ENGLISH} "Unraid USB Creator seems to be running and busy.$\nDo you want to terminate process?" +LangString stopMsg ${LANG_ENGLISH} "Stopping Unraid USB Creator" +LangString win7Msg ${LANG_ENGLISH} "Windows 7 or above required" +LangString termMsg ${LANG_DUTCH} "Unraid USB Creator is momenteel actief.$\nWilt u het programma afsluiten?" +LangString stopMsg ${LANG_DUTCH} "Bezig met afsluiten Unraid USB Creator" +LangString win7Msg ${LANG_DUTCH} "Minimaal Windows 7 is vereist" +LangString termMsg ${LANG_ITALIAN} "Unraid USB Creator sembra essere in esecuzione e occupato.$\nVuoi terminare il processo Unraid USB Creator?" +LangString stopMsg ${LANG_ITALIAN} "Chiusura processo Unraid USB Creator" +LangString win7Msg ${LANG_ITALIAN} "Per l'esecuzione del programma è richiesto Windows 7 o versioni successive" + +###################################################################### + +!include WinMessages.nsh +!include Logiclib.nsh + +!macro FindWindowOfExe Exe + Push $1 + Push $2 + Push $3 + Push $R0 + Push $R1 + + SetPluginUnload alwaysoff + Push "0" ; Result code + System::Get "(i.r1, i) iss" + Pop $R0 + System::Call "user32::EnumWindows(k R0, i) i.s" + + loop: + Pop $0 + StrCmp $0 "callback1" 0 doneloop + System::Call 'user32.dll::GetWindowThreadProcessId(i r1, *i .r3) i .r2' + System::Call 'kernel32.dll::OpenProcess(i 1040, i 0, i r3) i .r2' + ${IfNot} $2 = 0 + System::Alloc 1024 + Pop $R1 + System::Call "Psapi::EnumProcessModules(i r2, i R1, i 1024, *i .r3) i .r0" + ${IfNot} $0 = 0 + System::Call "*$R1(i .r0)" + System::Call "Psapi::GetModuleBaseName(i r2, i r0, t .r3, i ${NSIS_MAX_STRLEN}) i .r0" + ${IfNot} $0 = 0 + ${If} $3 == "${Exe}" + # Only replace result value if it is first match + Pop $3 + ${If} $3 == 0 + Push $1 + ${Else} + Push $3 + ${EndIf} + ${EndIf} + ${EndIf} + ${EndIf} + System::Free $R1 + System::Call "kernel32::CloseHandle(i r2)" + ${EndIf} + + Push 1 + System::Call "$R0" + Goto loop + doneloop: + SetPluginUnload manual + + System::Free $R0 + Pop $0 + + Pop $R1 + Pop $R0 + Pop $3 + Pop $2 + Pop $1 +!macroend + +!macro TerminateApp + Push $0 ; window handle + Push $1 + Push $2 ; process handle + DetailPrint "$(stopMsg)" + #FindWindow $0 '' '${WND_TITLE}' + !insertmacro FindWindowOfExe '${EXE_TO_TERMINATE}' + IntCmp $0 0 done + System::Call 'user32.dll::GetWindowThreadProcessId(i r0, *i .r1) i .r2' + System::Call 'kernel32.dll::OpenProcess(i 0x00100001, i 0, i r1) i .r2' + SendMessage $0 ${WM_CLOSE} 0 0 /TIMEOUT=2000 + System::Call 'kernel32.dll::WaitForSingleObject(i r2, i 2000) i .r1' + IntCmp $1 0 close + MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(termMsg)" /SD IDYES IDYES terminate IDNO close + System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' + Quit + terminate: + System::Call 'kernel32.dll::TerminateProcess(i r2, i 0) i .r1' + close: + System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' + done: + Pop $2 + Pop $1 + Pop $0 +!macroend + +###################################################################### + +Section -MainProgram +${INSTALL_TYPE} +!insertmacro TerminateApp +SetOverwrite ifnewer +SetOutPath "$INSTDIR" +WriteUninstaller "uninstall.exe" +File /r "deploy/*.*" +SectionEnd + +###################################################################### + +Section -Icons_Reg + +!ifdef REG_START_MENU +!insertmacro MUI_STARTMENU_WRITE_BEGIN Application +CreateDirectory "$SMPROGRAMS\$SM_Folder" +CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" +!ifdef WEB_SITE +WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" +CreateShortCut "$SMPROGRAMS\$SM_Folder\${APP_NAME} Website.lnk" "$INSTDIR\${APP_NAME} website.url" +!endif +!insertmacro MUI_STARTMENU_WRITE_END +!endif + +!ifndef REG_START_MENU +CreateDirectory "$SMPROGRAMS\Unraid" +CreateShortCut "$SMPROGRAMS\Unraid\${APP_NAME}.lnk" "$INSTDIR\${MAIN_APP_EXE}" +!ifdef WEB_SITE +WriteIniStr "$INSTDIR\${APP_NAME} website.url" "InternetShortcut" "URL" "${WEB_SITE}" +CreateShortCut "$SMPROGRAMS\Unraid\${APP_NAME} Website.lnk" "$INSTDIR\${APP_NAME} website.url" +!endif +!endif + +WriteRegStr ${REG_ROOT} "${REG_APP_PATH}" "" "$INSTDIR\${MAIN_APP_EXE}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayName" "${APP_NAME}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "UninstallString" "$INSTDIR\uninstall.exe" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayIcon" "$INSTDIR\${MAIN_APP_EXE}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "DisplayVersion" "${VERSION}" +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "Publisher" "${COMP_NAME}" + +!ifdef WEB_SITE +WriteRegStr ${REG_ROOT} "${UNINSTALL_PATH}" "URLInfoAbout" "${WEB_SITE}" +!endif + + +WriteRegStr SHCTX "Software\Classes\.zip\OpenWithProgIds" "UNRAID_USB_CREATOR" "" +WriteRegStr SHCTX "Software\Classes\.gz\OpenWithProgIds" "UNRAID_USB_CREATOR" "" +WriteRegStr SHCTX "Software\Classes\.xz\OpenWithProgIds" "UNRAID_USB_CREATOR" "" +WriteRegStr SHCTX "Software\Classes\.img\OpenWithProgIds" "UNRAID_USB_CREATOR" "" +WriteRegStr SHCTX "Software\Classes\.zstd\OpenWithProgIds" "UNRAID_USB_CREATOR" "" +WriteRegStr SHCTX "Software\Classes\UNRAID_USB_CREATOR\shell\open" "FriendlyAppName" "Unraid USB Creator" +WriteRegStr SHCTX "Software\Classes\UNRAID_USB_CREATOR\shell\open\command" "" '"$INSTDIR\unraid-usb-creator.exe" "%1"' + +SectionEnd + +############################################################################################# + +Section Uninstall +${INSTALL_TYPE} + +RmDir /r "$INSTDIR" + +!ifdef REG_START_MENU +!insertmacro MUI_STARTMENU_GETFOLDER "Application" $SM_Folder +Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME}.lnk" +!ifdef WEB_SITE +Delete "$SMPROGRAMS\$SM_Folder\${APP_NAME} Website.lnk" +!endif +RmDir "$SMPROGRAMS\$SM_Folder" +!endif + +!ifndef REG_START_MENU +Delete "$SMPROGRAMS\Unraid\${APP_NAME}.lnk" +!ifdef WEB_SITE +Delete "$SMPROGRAMS\Unraid\${APP_NAME} Website.lnk" +!endif +RmDir "$SMPROGRAMS\Unraid" +!endif + +DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" +DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" + +# File associations +DeleteRegValue SHCTX "Software\Classes\.zip\OpenWithProgIds" "UNRAID_USB_CREATOR" +DeleteRegValue SHCTX "Software\Classes\.gz\OpenWithProgIds" "UNRAID_USB_CREATOR" +DeleteRegValue SHCTX "Software\Classes\.xz\OpenWithProgIds" "UNRAID_USB_CREATOR" +DeleteRegValue SHCTX "Software\Classes\.img\OpenWithProgIds" "UNRAID_USB_CREATOR" +DeleteRegValue SHCTX "Software\Classes\.zstd\OpenWithProgIds" "UNRAID_USB_CREATOR" +DeleteRegKey SHCTX "Software\Classes\UNRAID_USB_CREATOR" + +DeleteRegKey ${REG_ROOT} "" + +RMDir /r "$APPDATA\Unraid\${APP_NAME}" +RMDir "$APPDATA\Unraid" +RMDir /r "$LOCALAPPDATA\Unraid\${APP_NAME}" +RMDir "$LOCALAPPDATA\Unraid" + +SectionEnd + +###################################################################### + + +Function .onInit + ${IfNot} ${AtLeastWin7} + MessageBox MB_OK "$(win7Msg)" + Quit + ${EndIf} +FunctionEnd diff --git a/src/windows/rpi-imager.rc b/src/windows/unraid-usb-creator.rc similarity index 52% rename from src/windows/rpi-imager.rc rename to src/windows/unraid-usb-creator.rc index 84f19b2b0..c67c9028a 100644 --- a/src/windows/rpi-imager.rc +++ b/src/windows/unraid-usb-creator.rc @@ -1,7 +1,7 @@ #include -IDI_ICON1 ICON DISCARDABLE "../icons/rpi-imager.ico" -CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "rpi-imager.manifest" +IDI_ICON1 ICON DISCARDABLE "../icons/unraid.ico" +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "unraid-usb-creator.manifest" 1 VERSIONINFO FILEVERSION IMAGER_VERSION_CSV @@ -11,8 +11,8 @@ PRODUCTVERSION IMAGER_VERSION_CSV { BLOCK "040904b0" { - VALUE "CompanyName", "Raspberry Pi Ltd" - VALUE "FileDescription", "Raspberry Pi Imager" + VALUE "CompanyName", "Lime Technology, Inc" + VALUE "FileDescription", "Unraid USB Creator" } } BLOCK "VarFileInfo"