From 10b0cbe8343ca5ee316e7302aa4c84166f60d51f Mon Sep 17 00:00:00 2001 From: Kroese Date: Sat, 18 May 2024 16:33:24 +0200 Subject: [PATCH] feat: Multi-language support (#119) --- Dockerfile | 2 +- assets/win10arm64.xml | 20 +- assets/win11arm64.xml | 24 +-- readme.md | 26 ++- src/define.sh | 422 ++++++++++++++++++++++++++++++++++------- src/entry.sh | 1 + src/install.sh | 430 ++++++++++++++++-------------------------- src/mido.sh | 267 ++++++++++++++++++++++++++ 8 files changed, 828 insertions(+), 364 deletions(-) create mode 100644 src/mido.sh diff --git a/Dockerfile b/Dockerfile index 8523cec..42b72bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ FROM scratch -COPY --from=qemux/qemu-arm:1.21 / / +COPY --from=qemux/qemu-arm:1.22 / / ARG VERSION_ARG="0.0" ARG DEBCONF_NOWARNINGS="yes" diff --git a/assets/win10arm64.xml b/assets/win10arm64.xml index e7c0a4d..79e5a11 100644 --- a/assets/win10arm64.xml +++ b/assets/win10arm64.xml @@ -158,7 +158,7 @@ 24/7 Dockur - https://github.com/dockur/windows/issues + https://github.com/dockur/windows-arm/issues Windows for Docker @@ -295,15 +295,6 @@ 0 - - - - true - Remote Desktop - all - - - @@ -456,16 +447,21 @@ 21 - netsh advfirewall firewall set rule group="Network Discovery" new enable=Yes + powershell -ExecutionPolicy ByPass -Command "Enable-NetFirewallRule -DisplayGroup @(Get-NetFirewallRule | Where-Object Name -Match "NetDIS.*" | Select-Object DisplayGroup -Unique | % DisplayGroup)" Enable Network Discovery 22 - netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=Yes + powershell -ExecutionPolicy ByPass -Command "Enable-NetFirewallRule -DisplayGroup @(Get-NetFirewallRule | Where-Object Name -Match "FPS-.*" | Select-Object DisplayGroup -Unique | % DisplayGroup)" Enable File Sharing 23 + powershell -ExecutionPolicy ByPass -Command "Enable-NetFirewallRule -DisplayGroup @(Get-NetFirewallRule | Where-Object Name -Match "RemoteDesktop-[^I].*" | Select-Object DisplayGroup -Unique | % DisplayGroup)" + Add RDP in firewall + + + 24 cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat" Execute custom script from the OEM folder if exists diff --git a/assets/win11arm64.xml b/assets/win11arm64.xml index 686020e..f89e69e 100644 --- a/assets/win11arm64.xml +++ b/assets/win11arm64.xml @@ -177,7 +177,7 @@ 24/7 Dockur - https://github.com/dockur/windows/issues + https://github.com/dockur/windows-arm/issues Windows for Docker @@ -314,15 +314,6 @@ 0 - - - - true - Remote Desktop - all - - - @@ -470,26 +461,31 @@ 20 - netsh advfirewall firewall set rule group="Network Discovery" new enable=Yes + powershell -ExecutionPolicy ByPass -Command "Enable-NetFirewallRule -DisplayGroup @(Get-NetFirewallRule | Where-Object Name -Match "NetDIS.*" | Select-Object DisplayGroup -Unique | % DisplayGroup)" Enable Network Discovery 21 - netsh advfirewall firewall set rule group="File and Printer Sharing" new enable=Yes + powershell -ExecutionPolicy ByPass -Command "Enable-NetFirewallRule -DisplayGroup @(Get-NetFirewallRule | Where-Object Name -Match "FPS-.*" | Select-Object DisplayGroup -Unique | % DisplayGroup)" Enable File Sharing 22 + powershell -ExecutionPolicy ByPass -Command "Enable-NetFirewallRule -DisplayGroup @(Get-NetFirewallRule | Where-Object Name -Match "RemoteDesktop-[^I].*" | Select-Object DisplayGroup -Unique | % DisplayGroup)" + Add RDP in firewall + + + 23 reg.exe add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v SV1 /d 0 /t REG_DWORD /f Disable unsupported hardware notifications - 23 + 24 reg.exe add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v SV2 /d 0 /t REG_DWORD /f Disable unsupported hardware notifications - 24 + 25 cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat" Execute custom script from the OEM folder if exists diff --git a/readme.md b/readme.md index cccbfea..9522bb8 100644 --- a/readme.md +++ b/readme.md @@ -17,6 +17,7 @@ Note: for KVM acceleration you need a Linux-based operating system, as it's not ## Features + - Multi-language - ISO downloader - KVM acceleration - Web-based viewer @@ -91,6 +92,19 @@ kubectl apply -f kubernetes.yml To install x86 and x64 versions of Windows, use [dockur/windows](https://github.com/dockur/windows/). +* ### How do I select the Windows language? + + By default, the English version of Windows will be downloaded. But you can add the `LANGUAGE` environment variable to your compose file, in order to specify an alternative language: + + ```yaml + environment: + LANGUAGE: "Chinese" + ``` + + You can choose between `Arabic`, `Bulgarian`, `Chinese`, `Croatian`, `Czech`, `Danish`, `Dutch`, `Estonian`, `Finnish`, `French`, `German`, `Greek`, `Hebrew`, `Hungarian`, `Italian`, `Japanese`, `Korean`, `Latvian`, `Lithuanian`, `Norwegian`, `Polish`, `Portuguese`, `Romanian`, `Russian`, `Serbian`, `Slovak`, `Slovenian`, `Spanish`, `Swedish`, `Turkish`, `Thai` and `Ukrainian`. + + If you want to use a keyboard layout or region/locale that is not the default for the selected language, you can add the `KEYBOARD` and `REGION` variables with a culture code, like `en-US`. + * ### How do I change the storage location? To change the storage location, include the following bind mount in your compose file: @@ -189,11 +203,21 @@ kubectl apply -f kubernetes.yml CPU_CORES: "4" ``` +* ### How do I configure the username and password? + + By default, a user called `Docker` is created during installation with an empty password. You can change these credentials in your compose file: + + ```yaml + environment: + USERNAME: "john" + PASSWORD: "secret" + ``` + * ### How do I connect using RDP? The web-viewer is mainly meant to be used during installation, as its picture quality is low, and it has no audio or clipboard for example. - So for a better experience you can connect using any Microsoft Remote Desktop client to the IP of the container, using the username `docker` and by leaving the password empty. + So for a better experience you can connect using any Microsoft Remote Desktop client to the IP of the container, using the username `Docker` and by leaving the password empty. There is a good RDP client for [Android](https://play.google.com/store/apps/details?id=com.microsoft.rdc.androidx) available from the Play Store and one for [iOS](https://apps.apple.com/nl/app/microsoft-remote-desktop/id714464092?l=en-GB) in the Apple Store. For Linux you can use [FreeRDP](https://www.freerdp.com/) and on Windows just type `mstsc` in the search box. diff --git a/src/define.sh b/src/define.sh index d62eae6..c0cf071 100644 --- a/src/define.sh +++ b/src/define.sh @@ -2,10 +2,15 @@ set -Eeuo pipefail : "${VERIFY:=""}" +: "${REGION:=""}" : "${MANUAL:=""}" : "${REMOVE:=""}" : "${VERSION:=""}" : "${DETECTED:=""}" +: "${KEYBOARD:=""}" +: "${LANGUAGE:=""}" +: "${USERNAME:=""}" +: "${PASSWORD:=""}" MIRRORS=2 PLATFORM="ARM64" @@ -22,16 +27,243 @@ parseVersion() { case "${VERSION,,}" in "11" | "11p" | "win11" | "win11p" | "windows11" | "windows 11" ) - VERSION="win11${PLATFORM,,}" + VERSION="win11arm64" ;; "10" | "10p" | "win10" | "win10p" | "windows10" | "windows 10" ) - VERSION="win10${PLATFORM,,}" + VERSION="win10arm64" ;; esac return 0 } +getLanguage() { + + local id="$1" + local ret="$2" + local lang="" + local desc="" + local culture="" + + case "${id,,}" in + "ar" | "ar-"* ) + lang="Arabic" + desc="$lang" + culture="ar-SA" ;; + "bg" | "bg-"* ) + lang="Bulgarian" + desc="$lang" + culture="bg-BG" ;; + "cs" | "cs-"* | "cz" | "cz-"* ) + lang="Czech" + desc="$lang" + culture="cs-CZ" ;; + "da" | "da-"* | "dk" | "dk-"* ) + lang="Danish" + desc="$lang" + culture="da-DK" ;; + "de" | "de-"* ) + lang="German" + desc="$lang" + culture="de-DE" ;; + "el" | "el-"* | "gr" | "gr-"* ) + lang="Greek" + desc="$lang" + culture="el-GR" ;; + "gb" | "en-gb" ) + lang="English International" + desc="English" + culture="en-GB" ;; + "en" | "en-"* ) + lang="English (United States)" + desc="English" + culture="en-US" ;; + "mx" | "es-mx" ) + lang="Spanish (Mexico)" + desc="Spanish" + culture="es-MX" ;; + "es" | "es-"* ) + lang="Spanish" + desc="$lang" + culture="es-ES" ;; + "et" | "et-"* ) + lang="Estonian" + desc="$lang" + culture="et-EE" ;; + "fi" | "fi-"* ) + lang="Finnish" + desc="$lang" + culture="fi-FI" ;; + "ca" | "fr-ca" ) + lang="French Canadian" + desc="French" + culture="fr-CA" ;; + "fr" | "fr-"* ) + lang="French" + desc="$lang" + culture="fr-FR" ;; + "he" | "he-"* | "il" | "il-"* ) + lang="Hebrew" + desc="$lang" + culture="he-IL" ;; + "hr" | "hr-"* | "cr" | "cr-"* ) + lang="Croatian" + desc="$lang" + culture="hr-HR" ;; + "hu" | "hu-"* ) + lang="Hungarian" + desc="$lang" + culture="hu-HU" ;; + "it" | "it-"* ) + lang="Italian" + desc="$lang" + culture="it-IT" ;; + "ja" | "ja-"* | "jp" | "jp-"* ) + lang="Japanese" + desc="$lang" + culture="ja-JP" ;; + "ko" | "ko-"* | "kr" | "kr-"* ) + lang="Korean" + desc="$lang" + culture="ko-KR" ;; + "lt" | "lt-"* ) + lang="Lithuanian" + desc="$lang" + culture="lv-LV" ;; + "lv" | "lv-"* ) + lang="Latvian" + desc="$lang" + culture="lt-LT" ;; + "nb" | "nb-"* |"nn" | "nn-"* | "no" | "no-"* ) + lang="Norwegian" + desc="$lang" + culture="nb-NO" ;; + "nl" | "nl-"* ) + lang="Dutch" + desc="$lang" + culture="nl-NL" ;; + "pl" | "pl-"* ) + lang="Polish" + desc="$lang" + culture="pl-PL" ;; + "br" | "pt-br" ) + lang="Brazilian Portuguese" + desc="Portuguese" + culture="pt-BR" ;; + "pt" | "pt-"* ) + lang="Portuguese" + desc="$lang" + culture="pt-BR" ;; + "ro" | "ro-"* ) + lang="Romanian" + desc="$lang" + culture="ro-RO" ;; + "ru" | "ru-"* ) + lang="Russian" + desc="$lang" + culture="ru-RU" ;; + "sk" | "sk-"* ) + lang="Slovak" + desc="$lang" + culture="sk-SK" ;; + "sl" | "sl-"* | "si" | "si-"* ) + lang="Slovenian" + desc="$lang" + culture="sl-SI" ;; + "sr" | "sr-"* ) + lang="Serbian Latin" + desc="Serbian" + culture="sr-Latn-RS" ;; + "sv" | "sv-"* | "se" | "se-"* ) + lang="Swedish" + desc="$lang" + culture="sv-SE" ;; + "th" | "th-"* ) + lang="Thai" + desc="$lang" + culture="th-TH" ;; + "tr" | "tr-"* ) + lang="Turkish" + desc="$lang" + culture="tr-TR" ;; + "ua" | "ua-"* | "uk" | "uk-"* ) + lang="Ukrainian" + desc="$lang" + culture="uk-UA" ;; + "hk" | "zh-hk" | "cn-hk" ) + lang="Chinese Traditional" + desc="Chinese HK" + culture="zh-TW" ;; + "tw" | "zh-tw" | "cn-tw" ) + lang="Chinese Traditional" + desc="Chinese TW" + culture="zh-TW" ;; + "zh" | "zh-"* | "cn" | "cn-"* ) + lang="Chinese Simplified" + desc="Chinese" + culture="zh-CN" ;; + esac + + case "${ret,,}" in + "desc" ) echo "$desc" ;; + "name" ) echo "$lang" ;; + "culture" ) echo "$culture" ;; + *) echo "$desc";; + esac + + return 0 +} + +parseLanguage() { + + LANGUAGE="${LANGUAGE/_/-/}" + [ -z "$LANGUAGE" ] && LANGUAGE="en" + + case "${LANGUAGE,,}" in + "arabic" | "arab" ) LANGUAGE="ar" ;; + "bulgarian" | "bu" ) LANGUAGE="bg" ;; + "chinese" | "cn" ) LANGUAGE="zh" ;; + "croatian" | "cr" | "hrvatski" ) LANGUAGE="hr" ;; + "czech" | "cz" | "cesky" ) LANGUAGE="cs" ;; + "danish" | "dk" | "danske" ) LANGUAGE="da" ;; + "dutch" | "nederlands" ) LANGUAGE="nl" ;; + "english" | "gb" | "british" ) LANGUAGE="en" ;; + "estonian" | "eesti" ) LANGUAGE="et" ;; + "finnish" | "suomi" ) LANGUAGE="fi" ;; + "french" | "français" | "francais" ) LANGUAGE="fr" ;; + "german" | "deutsch" ) LANGUAGE="de" ;; + "greek" | "gr" ) LANGUAGE="el" ;; + "hebrew" | "il" ) LANGUAGE="he" ;; + "hungarian" | "magyar" ) LANGUAGE="hu" ;; + "italian" | "italiano" ) LANGUAGE="it" ;; + "japanese" | "jp" ) LANGUAGE="ja" ;; + "korean" | "kr" ) LANGUAGE="ko" ;; + "latvian" | "latvijas" ) LANGUAGE="lv" ;; + "lithuanian" | "lietuvos" ) LANGUAGE="lt" ;; + "norwegian" | "no" | "nb" | "norsk" ) LANGUAGE="nn" ;; + "polish" | "polski" ) LANGUAGE="pl" ;; + "portuguese" | "pt" | "br" ) LANGUAGE="pt-br" ;; + "português" | "portugues" ) LANGUAGE="pt-br" ;; + "romanian" | "română" | "romana" ) LANGUAGE="ro" ;; + "russian" | "ruski" ) LANGUAGE="ru" ;; + "serbian" | "serbian latin" ) LANGUAGE="sr" ;; + "slovak" | "slovenský" | "slovensky" ) LANGUAGE="sk" ;; + "slovenian" | "si" | "slovenski" ) LANGUAGE="sl" ;; + "spanish" | "espanol" | "español" ) LANGUAGE="es" ;; + "swedish" | "se" | "svenska" ) LANGUAGE="sv" ;; + "turkish" | "türk" | "turk" ) LANGUAGE="tr" ;; + "thai" ) LANGUAGE="th" ;; + "ukrainian" | "ua" ) LANGUAGE="uk" ;; + esac + + local culture + culture=$(getLanguage "$LANGUAGE" "culture") + [ -n "$culture" ] && return 0 + + error "Invalid LANGUAGE specified, value \"$LANGUAGE\" is not recognized!" + return 1 +} + printVersion() { local id="$1" @@ -130,7 +362,7 @@ getVersion() { local name="$1" local arch="$2" - id=$(fromName "$arch") + id=$(fromName "$name" "$arch") echo "$id" return 0 @@ -140,65 +372,37 @@ switchEdition() { return 0 } -getCatalog() { - - local id="$1" - local ret="$2" - local url="" - local name="" - local edition="" - - case "${id,,}" in - "win11${PLATFORM,,}" ) - edition="Professional" - name="Windows 11 Pro" - url="https://go.microsoft.com/fwlink?linkid=2156292" - ;; - "win10${PLATFORM,,}" ) - edition="Professional" - name="Windows 10 Pro" - url="https://go.microsoft.com/fwlink/?LinkId=841361" - ;; - esac - - case "${ret,,}" in - "url" ) echo "$url" ;; - "name" ) echo "$name" ;; - "edition" ) echo "$edition" ;; - *) echo "";; - esac - - return 0 -} - getLink1() { # Fallbacks for users who cannot connect to the Microsoft servers local id="$1" - local ret="$2" + local lang="$2" + local ret="$3" local url="" local sum="" local size="" local host="https://dl.bobpony.com/windows" + [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0 + case "${id,,}" in - "win11${PLATFORM,,}") + "win11arm64") size=5946128384 sum="0c8edeae3202cf6f4bf8bb65c9f6176374c48fdcbcc8d0effa8547be75e9fd20" - url="$host/11/en-us_windows_11_23h2_${PLATFORM,,}.iso" + url="11/en-us_windows_11_23h2_arm64.iso" ;; - "win10${PLATFORM,,}") + "win10arm64") size=4957009920 sum="64461471292b79d18cd9cced6cc141d7773b489a9b3e12de7b120312e63bfaf1" - url="$host/10/en-us_windows_10_22h2_${PLATFORM,,}.iso" + url="10/en-us_windows_10_22h2_arm64.iso" ;; esac case "${ret,,}" in "sum" ) echo "$sum" ;; "size" ) echo "$size" ;; - *) echo "$url";; + *) [ -n "$url" ] && echo "$host/$url";; esac return 0 @@ -209,29 +413,112 @@ getLink2() { # Fallbacks for users who cannot connect to the Microsoft servers local id="$1" - local ret="$2" + local lang="$2" + local ret="$3" local url="" local sum="" local size="" local host="https://drive.massgrave.dev" + culture=$(getLanguage "$lang" "culture") + case "${id,,}" in - "win11${PLATFORM,,}") - size=7010680832 - sum="3da19e8c8c418091081186e362fb53a1aa68dad255d1d28ace81e2c88c3f99ba" - url="$host/SW_DVD9_Win_Pro_11_23H2.2_Arm64_English_Pro_Ent_EDU_N_MLF_X23-68023.ISO" + "win11arm64") + case "${culture,,}" in + "ar" | "ar-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Arabic_Pro_Ent_EDU_N_MLF_X23-68013.ISO" ;; + "bg" | "bg-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Bulgarian_Pro_Ent_EDU_N_MLF_X23-68015.ISO" ;; + "cs" | "cs-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Czech_Pro_Ent_EDU_N_MLF_X23-68019.ISO" ;; + "da" | "da-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Danish_Pro_Ent_EDU_N_MLF_X23-68020.ISO" ;; + "de" | "de-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_German_Pro_Ent_EDU_N_MLF_X23-68028.ISO" ;; + "el" | "el-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Greek_Pro_Ent_EDU_N_MLF_X23-68029.ISO" ;; + "gb" | "en-gb" ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Eng_Intl_Pro_Ent_EDU_N_MLF_X23-68022.ISO" ;; + "en" | "en-"* ) + size=7010680832 + sum="3da19e8c8c418091081186e362fb53a1aa68dad255d1d28ace81e2c88c3f99ba" + url="$host/SW_DVD9_Win_Pro_11_23H2.2_Arm64_English_Pro_Ent_EDU_N_MLF_X23-68023.ISO" ;; + "mx" | "es-mx" ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Spanish_Latam_Pro_Ent_EDU_N_MLF_X23-68045.ISO" ;; + "es" | "es-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Spanish_Pro_Ent_EDU_N_MLF_X23-68046.ISO" ;; + "et" | "et-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Estonian_Pro_Ent_EDU_N_MLF_X23-68024.ISO" ;; + "fi" | "fi-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Finnish_Pro_Ent_EDU_N_MLF_X23-68025.ISO" ;; + "ca" | "fr-ca" ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_FrenchCanadian_Pro_Ent_EDU_N_MLF_X23-68027.ISO" ;; + "fr" | "fr-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_French_Pro_Ent_EDU_N_MLF_X23-68026.ISO" ;; + "he" | "he-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Hebrew_Pro_Ent_EDU_N_MLF_X23-68030.ISO" ;; + "hr" | "hr-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Croatian_Pro_Ent_EDU_N_MLF_X23-68018.ISO" ;; + "hu" | "hu-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Hungarian_Pro_Ent_EDU_N_MLF_X23-68031.ISO" ;; + "it" | "it-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Italian_Pro_Ent_EDU_N_MLF_X23-68032.ISO" ;; + "ja" | "ja-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Japanese_Pro_Ent_EDU_N_MLF_X23-68033.ISO" ;; + "ko" | "ko-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Korean_Pro_Ent_EDU_N_MLF_X23-68034.ISO" ;; + "lt" | "lt-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Lithuanian_Pro_Ent_EDU_N_MLF_X23-68036.ISO" ;; + "lv" | "lv-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Latvian_Pro_Ent_EDU_N_MLF_X23-68035.ISO" ;; + "nb" | "nb-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Norwegian_Pro_Ent_EDU_N_MLF_X23-68037.ISO" ;; + "nl" | "nl-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Dutch_Pro_Ent_EDU_N_MLF_X23-68021.ISO" ;; + "pl" | "pl-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Polish_Pro_Ent_EDU_N_MLF_X23-68038.ISO" ;; + "br" | "pt-br" ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Brazilian_Pro_Ent_EDU_N_MLF_X23-68014.ISO" ;; + "pt" | "pt-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Portuguese_Pro_Ent_EDU_N_MLF_X23-68039.ISO" ;; + "ro" | "ro-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Romanian_Pro_Ent_EDU_N_MLF_X23-68040.ISO" ;; + "ru" | "ru-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Russian_Pro_Ent_EDU_N_MLF_X23-68041.ISO" ;; + "sk" | "sk-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Slovak_Pro_Ent_EDU_N_MLF_X23-68043.ISO" ;; + "sl" | "sl-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Slovenian_Pro_Ent_EDU_N_MLF_X23-68044.ISO" ;; + "sr" | "sr-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Serbian_Latin_Pro_Ent_EDU_N_MLF_X23-68042.ISO" ;; + "sv" | "sv-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Swedish_Pro_Ent_EDU_N_MLF_X23-68047.ISO" ;; + "th" | "th-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Thai_Pro_Ent_EDU_N_MLF_X23-68048.ISO" ;; + "tr" | "tr-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Turkish_Pro_Ent_EDU_N_MLF_X23-68049.ISO" ;; + "uk" | "uk-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_Ukrainian_Pro_Ent_EDU_N_MLF_X23-68050.ISO" ;; + "zh-hk" | "zh-tw" ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_ChnTrad_Pro_Ent_EDU_N_MLF_X23-68017.ISO" ;; + "zh" | "zh-"* ) url="SW_DVD9_Win_Pro_11_23H2.2_Arm64_ChnSimp_Pro_Ent_EDU_N_MLF_X23-68016.ISO" ;; + esac ;; - "win10${PLATFORM,,}") - size=5190453248 - sum="bd96b342193f81c0a2e6595d8d8b8dc01dbf789d19211699f6299fec7b712197" - url="$host/SW_DVD9_Win_Pro_10_22H2.15_Arm64_English_Pro_Ent_EDU_N_MLF_X23-67223.ISO" + "win10arm64") + case "${culture,,}" in + "ar" | "ar-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Arabic_Pro_Ent_EDU_N_MLF_X23-67213.ISO" ;; + "bg" | "bg-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Bulgarian_Pro_Ent_EDU_N_MLF_X23-67215.ISO" ;; + "cs" | "cs-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Czech_Pro_Ent_EDU_N_MLF_X23-67219.ISO" ;; + "da" | "da-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Danish_Pro_Ent_EDU_N_MLF_X23-67220.ISO" ;; + "de" | "de-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_German_Pro_Ent_EDU_N_MLF_X23-67228.ISO" ;; + "el" | "el-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Greek_Pro_Ent_EDU_N_MLF_X23-67229.ISO" ;; + "gb" | "en-gb" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Eng_Intl_Pro_Ent_EDU_N_MLF_X23-67222.ISO" ;; + "en" | "en-"* ) + size=5190453248 + sum="bd96b342193f81c0a2e6595d8d8b8dc01dbf789d19211699f6299fec7b712197" + url="$host/SW_DVD9_Win_Pro_10_22H2.15_Arm64_English_Pro_Ent_EDU_N_MLF_X23-67223.ISO" ;; + "mx" | "es-mx" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Spanish_Latam_Pro_Ent_EDU_N_MLF_X23-67245.ISO" ;; + "es" | "es-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Spanish_Pro_Ent_EDU_N_MLF_X23-67246.ISO" ;; + "et" | "et-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Estonian_Pro_Ent_EDU_N_MLF_X23-67224.ISO" ;; + "fi" | "fi-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Finnish_Pro_Ent_EDU_N_MLF_X23-67225.ISO" ;; + "ca" | "fr-ca" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_FrenchCanadian_Pro_Ent_EDU_N_MLF_X23-67227.ISO" ;; + "fr" | "fr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_French_Pro_Ent_EDU_N_MLF_X23-67226.ISO" ;; + "he" | "he-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Hebrew_Pro_Ent_EDU_N_MLF_X23-67230.ISO" ;; + "hr" | "hr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Croatian_Pro_Ent_EDU_N_MLF_X23-67218.ISO" ;; + "hu" | "hu-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Hungarian_Pro_Ent_EDU_N_MLF_X23-67231.ISO" ;; + "it" | "it-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Italian_Pro_Ent_EDU_N_MLF_X23-67232.ISO" ;; + "ja" | "ja-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Japanese_Pro_Ent_EDU_N_MLF_X23-67233.ISO" ;; + "ko" | "ko-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Korean_Pro_Ent_EDU_N_MLF_X23-67234.ISO" ;; + "lt" | "lt-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Lithuanian_Pro_Ent_EDU_N_MLF_X23-67236.ISO" ;; + "lv" | "lv-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Latvian_Pro_Ent_EDU_N_MLF_X23-67235.ISO" ;; + "nb" | "nb-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Norwegian_Pro_Ent_EDU_N_MLF_X23-67237.ISO" ;; + "nl" | "nl-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Dutch_Pro_Ent_EDU_N_MLF_X23-67221.ISO" ;; + "pl" | "pl-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Polish_Pro_Ent_EDU_N_MLF_X23-67238.ISO" ;; + "br" | "pt-br" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Brazilian_Pro_Ent_EDU_N_MLF_X23-67214.ISO" ;; + "pt" | "pt-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Portuguese_Pro_Ent_EDU_N_MLF_X23-67239.ISO" ;; + "ro" | "ro-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Romanian_Pro_Ent_EDU_N_MLF_X23-67240.ISO" ;; + "ru" | "ru-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Russian_Pro_Ent_EDU_N_MLF_X23-67241.ISO" ;; + "sk" | "sk-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Slovak_Pro_Ent_EDU_N_MLF_X23-67243.ISO" ;; + "sl" | "sl-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Slovenian_Pro_Ent_EDU_N_MLF_X23-67244.ISO" ;; + "sr" | "sr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Serbian_Latin_Pro_Ent_EDU_N_MLF_X23-67242.ISO" ;; + "sv" | "sv-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Swedish_Pro_Ent_EDU_N_MLF_X23-67247.ISO" ;; + "th" | "th-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Thai_Pro_Ent_EDU_N_MLF_X23-67248.ISO" ;; + "tr" | "tr-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Turkish_Pro_Ent_EDU_N_MLF_X23-67249.ISO" ;; + "uk" | "uk-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_Ukrainian_Pro_Ent_EDU_N_MLF_X23-67250.ISO" ;; + "zh-hk" | "zh-tw" ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_ChnTrad_Pro_Ent_EDU_N_MLF_X23-67217.ISO" ;; + "zh" | "zh-"* ) url="SW_DVD9_Win_Pro_10_22H2.15_Arm64_ChnSimp_Pro_Ent_EDU_N_MLF_X23-67216.ISO" ;; + esac ;; esac case "${ret,,}" in "sum" ) echo "$sum" ;; "size" ) echo "$size" ;; - *) echo "$url";; + *) [ -n "$url" ] && echo "$host/$url";; esac return 0 @@ -240,12 +527,13 @@ getLink2() { getValue() { local val="" - local id="$3" - local type="$2" + local id="$2" + local lang="$3" + local type="$4" local func="getLink$1" if [ "$1" -gt 0 ] && [ "$1" -le "$MIRRORS" ]; then - val=$($func "$id" "$type") + val=$($func "$id" "$lang" "$type") fi echo "$val" @@ -254,8 +542,8 @@ getValue() { getLink() { - local url="" - url=$(getValue "$1" "" "$2") + local url + url=$(getValue "$1" "$2" "$3" "") echo "$url" return 0 @@ -263,8 +551,8 @@ getLink() { getHash() { - local sum="" - sum=$(getValue "$1" "sum" "$2") + local sum + sum=$(getValue "$1" "$2" "$3" "sum") echo "$sum" return 0 @@ -272,8 +560,8 @@ getHash() { getSize() { - local size="" - size=$(getValue "$1" "size" "$2") + local size + size=$(getValue "$1" "$2" "$3" "size") echo "$size" return 0 @@ -286,10 +574,13 @@ isMido() { isESD() { local id="$1" - local url + local lang="$2" - url=$(getCatalog "$id" "url") - [ -n "$url" ] && return 0 + case "${id,,}" in + "win11${PLATFORM,,}" | "win10${PLATFORM,,}" ) + return 0 + ;; + esac return 1 } @@ -297,14 +588,15 @@ isESD() { validVersion() { local id="$1" + local lang="$2" local url - isESD "$id" && return 0 - isMido "$id" && return 0 + isESD "$id" "$lang" && return 0 + isMido "$id" "$lang" && return 0 for ((i=1;i<=MIRRORS;i++)); do - url=$(getLink "$i" "$id") + url=$(getLink "$i" "$id" "$lang") [ -n "$url" ] && return 0 done diff --git a/src/entry.sh b/src/entry.sh index 7b59927..014d299 100644 --- a/src/entry.sh +++ b/src/entry.sh @@ -10,6 +10,7 @@ cd /run . reset.sh # Initialize system . define.sh # Define versions +. mido.sh # Download code . install.sh # Run installation . disk.sh # Initialize disks . display.sh # Initialize graphics diff --git a/src/install.sh b/src/install.sh index b4eeb27..93a225c 100644 --- a/src/install.sh +++ b/src/install.sh @@ -73,6 +73,16 @@ startInstall() { : "${file//+/ }"; printf -v file '%b' "${_//%/\\x}" file=$(echo "$file" | sed -e 's/[^A-Za-z0-9._-]/_/g') + else + + local language + language=$(getLanguage "$LANGUAGE" "culture") + language="${language%%-*}" + + if [ -n "$language" ] && [[ "${language,,}" != "en" ]]; then + file="${VERSION/\//}_${language,,}.iso" + fi + fi BOOT="$STORAGE/$file" @@ -209,236 +219,6 @@ detectCustom() { return 0 } -getESD() { - - local dir="$1" - local version="$2" - local editionName - local winCatalog size - - if ! isESD "${version,,}"; then - error "Invalid VERSION specified, value \"$version\" is not recognized!" && return 1 - fi - - winCatalog=$(getCatalog "$version" "url") - editionName=$(getCatalog "$version" "edition") - - local msg="Downloading product information from Microsoft..." - info "$msg" && html "$msg" - - rm -rf "$dir" - mkdir -p "$dir" - - local wFile="catalog.cab" - local xFile="products.xml" - local eFile="esd_edition.xml" - local fFile="products_filter.xml" - - { wget "$winCatalog" -O "$dir/$wFile" -q --timeout=10; rc=$?; } || : - (( rc != 0 )) && error "Failed to download $winCatalog , reason: $rc" && return 1 - - cd "$dir" - - if ! cabextract "$wFile" > /dev/null; then - cd /run - error "Failed to extract $wFile!" && return 1 - fi - - cd /run - - if [ ! -s "$dir/$xFile" ]; then - error "Failed to find $xFile in $wFile!" && return 1 - fi - - local esdLang="en-us" - local edQuery='//File[Architecture="'${PLATFORM}'"][Edition="'${editionName}'"]' - - echo -e '' > "$dir/$fFile" - xmllint --nonet --xpath "${edQuery}" "$dir/$xFile" >> "$dir/$fFile" 2>/dev/null - echo -e ''>> "$dir/$fFile" - xmllint --nonet --xpath '//File[LanguageCode="'${esdLang}'"]' "$dir/$fFile" >"$dir/$eFile" - - size=$(stat -c%s "$dir/$eFile") - if ((size<20)); then - error "Failed to find Windows product in $eFile!" && return 1 - fi - - local tag="FilePath" - ESD=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g") - - if [ -z "$ESD" ]; then - error "Failed to find ESD URL in $eFile!" && return 1 - fi - - tag="Sha1" - ESD_SUM=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g") - tag="Size" - ESD_SIZE=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g") - - rm -rf "$dir" - return 0 -} - -verifyFile() { - - local iso="$1" - local size="$2" - local total="$3" - local check="$4" - - if [ -n "$size" ] && [[ "$total" != "$size" ]] && [[ "$size" != "0" ]]; then - warn "The downloaded file has an unexpected size: $total bytes, while expected value was: $size bytes. Please report this at $SUPPORT/issues" - fi - - local hash="" - local algo="SHA256" - - [ -z "$check" ] && return 0 - [[ "$VERIFY" != [Yy1]* ]] && return 0 - [[ "${#check}" == "40" ]] && algo="SHA1" - - local msg="Verifying downloaded ISO..." - info "$msg" && html "$msg" - - if [[ "${algo,,}" != "sha256" ]]; then - hash=$(sha1sum "$iso" | cut -f1 -d' ') - else - hash=$(sha256sum "$iso" | cut -f1 -d' ') - fi - - if [[ "$hash" == "$check" ]]; then - info "Succesfully verified ISO!" && return 0 - fi - - error "The downloaded file has an invalid $algo checksum: $hash , while expected value was: $check. Please report this at $SUPPORT/issues" - - rm -f "$iso" - return 1 -} - -downloadFile() { - - local iso="$1" - local url="$2" - local sum="$3" - local size="$4" - local desc="$5" - local rc total progress domain dots - - rm -f "$iso" - - # Check if running with interactive TTY or redirected to docker log - if [ -t 1 ]; then - progress="--progress=bar:noscroll" - else - progress="--progress=dot:giga" - fi - - local msg="Downloading $desc..." - html "$msg" - - domain=$(echo "$url" | awk -F/ '{print $3}') - dots=$(echo "$domain" | tr -cd '.' | wc -c) - (( dots > 1 )) && domain=$(expr "$domain" : '.*\.\(.*\..*\)') - - if [ -n "$domain" ] && [[ "${domain,,}" != *"microsoft.com" ]]; then - msg="Downloading $desc from $domain..." - fi - - info "$msg" - /run/progress.sh "$iso" "$size" "Downloading $desc ([P])..." & - - { wget "$url" -O "$iso" -q --timeout=10 --show-progress "$progress"; rc=$?; } || : - - fKill "progress.sh" - - if (( rc == 0 )) && [ -f "$iso" ]; then - total=$(stat -c%s "$iso") - if [ "$total" -gt 100000000 ]; then - ! verifyFile "$iso" "$size" "$total" "$sum" && return 1 - html "Download finished successfully..." && return 0 - fi - fi - - error "Failed to download $url , reason: $rc" - - rm -f "$iso" - return 1 -} - -downloadImage() { - - local iso="$1" - local version="$2" - local tried="n" - local url sum size base desc - - if [[ "${version,,}" == "http"* ]]; then - base=$(basename "$iso") - desc=$(fromFile "$base") - downloadFile "$iso" "$version" "" "" "$desc" && return 0 - return 1 - fi - - if ! validVersion "$version"; then - error "Invalid VERSION specified, value \"$version\" is not recognized!" && return 1 - fi - - desc=$(printVersion "$version" "") - - if isMido "$version"; then - tried="y" - doMido "$iso" "$version" "$desc" && return 0 - fi - - switchEdition "$version" - - if isESD "$version"; then - - if [[ "$tried" != "n" ]]; then - info "Failed to download $desc using Mido, will try a diferent method now..." - fi - - tried="y" - - if getESD "$TMP/esd" "$version"; then - ISO="${ISO%.*}.esd" - downloadFile "$ISO" "$ESD" "$ESD_SUM" "$ESD_SIZE" "$desc" && return 0 - ISO="$iso" - fi - - fi - - for ((i=1;i<=MIRRORS;i++)); do - - url=$(getLink "$i" "$version") - - if [ -n "$url" ]; then - if [[ "$tried" != "n" ]]; then - info "Failed to download $desc, will try another mirror now..." - fi - tried="y" - size=$(getSize "$i" "$version") - sum=$(getHash "$i" "$version") - downloadFile "$iso" "$url" "$sum" "$size" "$desc" && return 0 - fi - - done - - return 1 -} - -removeDownload() { - - local iso="$1" - - [ ! -f "$iso" ] && return 0 - [ -n "$CUSTOM" ] && return 0 - ! rm -f "$iso" 2> /dev/null && warn "failed to remove $iso !" - - return 0 -} - extractESD() { local iso="$1" @@ -572,19 +352,6 @@ extractImage() { return 0 } -setXML() { - - local file="/custom.xml" - [ ! -f "$file" ] || [ ! -s "$file" ] && file="$STORAGE/custom.xml" - [ ! -f "$file" ] || [ ! -s "$file" ] && file="/run/assets/custom.xml" - [ ! -f "$file" ] || [ ! -s "$file" ] && file="$1" - [ ! -f "$file" ] || [ ! -s "$file" ] && file="/run/assets/$DETECTED.xml" - [ ! -f "$file" ] || [ ! -s "$file" ] && return 1 - - XML="$file" - return 0 -} - getPlatform() { local xml="$1" @@ -604,6 +371,26 @@ getPlatform() { return 0 } +checkPlatform() { + + local xml="$1" + local platform compat + + platform=$(getPlatform "$xml") + + case "${platform,,}" in + "x86" ) compat="x64" ;; + "x64" ) compat="$platform" ;; + "arm64" ) compat="$platform" ;; + * ) compat="${PLATFORM,,}" ;; + esac + + [[ "${compat,,}" == "${PLATFORM,,}" ]] && return 0 + + error "You cannot boot ${platform^^} images on a $PLATFORM CPU!" + return 1 +} + hasVersion() { local id="$1" @@ -649,26 +436,6 @@ selectVersion() { return 0 } -checkPlatform() { - - local xml="$1" - local platform compat - - platform=$(getPlatform "$xml") - - case "${platform,,}" in - "x86" ) compat="x64" ;; - "x64" ) compat="$platform" ;; - "arm64" ) compat="$platform" ;; - * ) compat="${PLATFORM,,}" ;; - esac - - [[ "${compat,,}" == "${PLATFORM,,}" ]] && return 0 - - error "You cannot boot ${platform^^} images on a $PLATFORM CPU!" - return 1 -} - detectVersion() { local xml="$1" @@ -683,11 +450,51 @@ detectVersion() { return 0 } +detectLanguage() { + + local xml="$1" + local lang="" + + if [[ "$xml" == *"LANGUAGE>"* ]]; then + lang="${xml#*LANGUAGE>}" + lang="${lang%%<*}" + else + if [[ "$xml" == *"FALLBACK>"* ]]; then + lang="${xml#*FALLBACK>}" + lang="${lang%%<*}" + fi + fi + + if [ -z "$lang" ]; then + warn "Language could not be detected from ISO!" && return 0 + fi + + local culture + culture=$(getLanguage "$lang" "culture") + [ -n "$culture" ] && LANGUAGE="$lang" && return 0 + + warn "Invalid language detected: \"$lang\"" + return 0 +} + +setXML() { + + local file="/custom.xml" + [ ! -f "$file" ] || [ ! -s "$file" ] && file="$STORAGE/custom.xml" + [ ! -f "$file" ] || [ ! -s "$file" ] && file="/run/assets/custom.xml" + [ ! -f "$file" ] || [ ! -s "$file" ] && file="$1" + [ ! -f "$file" ] || [ ! -s "$file" ] && file="/run/assets/$DETECTED.xml" + [ ! -f "$file" ] || [ ! -s "$file" ] && return 1 + + XML="$file" + return 0 +} + detectImage() { local dir="$1" local version="$2" - local desc msg + local desc msg language XML="" @@ -750,6 +557,12 @@ detectImage() { fi desc=$(printEdition "$DETECTED" "$DETECTED") + detectLanguage "$info" + + if [[ "${LANGUAGE,,}" != "en" ]] && [[ "${LANGUAGE,,}" != "en-"* ]]; then + language=$(getLanguage "$LANGUAGE" "desc") + desc="$desc ($language)" + fi info "Detected: $desc" setXML "" && return 0 @@ -773,6 +586,12 @@ prepareImage() { local dir="$2" local missing + case "${DETECTED,,}" in + "winxp"* | "winvistax86"* | "win7x86"* ) + MACHINE="pc-q35-2.10" + ;; + esac + case "${DETECTED,,}" in "winxp"* ) BOOT_MODE="windows_legacy" @@ -801,15 +620,62 @@ prepareImage() { return 1 } +updateXML() { + + local asset="$1" + local language="$2" + local culture region keyboard + + culture=$(getLanguage "$language" "culture") + + if [ -n "$culture" ] && [[ "${culture,,}" != "en-us" ]]; then + sed -i "s/en-US<\/UILanguage>/$culture<\/UILanguage>/g" "$asset" + fi + + region="$REGION" + [ -z "$region" ] && region="$culture" + + if [ -n "$region" ] && [[ "${region,,}" != "en-us" ]]; then + sed -i "s/en-US<\/UserLocale>/$region<\/UserLocale>/g" "$asset" + sed -i "s/en-US<\/SystemLocale>/$region<\/SystemLocale>/g" "$asset" + fi + + keyboard="$KEYBOARD" + [ -z "$keyboard" ] && keyboard="$culture" + + if [ -n "$keyboard" ] && [[ "${keyboard,,}" != "en-us" ]]; then + sed -i "s/en-US<\/InputLocale>/$keyboard<\/InputLocale>/g" "$asset" + sed -i "s/0409:00000409<\/InputLocale>/$keyboard<\/InputLocale>/g" "$asset" + fi + + if [ -n "$USERNAME" ]; then + sed -i "s/where name=\"Docker\"/where name=\"$USERNAME\"/g" "$asset" + sed -i "s/Docker<\/Name>/$USERNAME<\/Name>/g" "$asset" + sed -i "s/Docker<\/FullName>/$USERNAME<\/FullName>/g" "$asset" + sed -i "s/Docker<\/Username>/$USERNAME<\/Username>/g" "$asset" + fi + + if [ -n "$PASSWORD" ]; then + sed -i "s/password<\/Value>/$PASSWORD<\/Value>/g" "$asset" + sed -z "s/.........../\n $PASSWORD<\/Value>/g" -i "$asset" + sed -z "s/.............../\n $PASSWORD<\/Value>/g" -i "$asset" + fi + + return 0 +} + updateImage() { local dir="$1" local asset="$2" + local language="$3" local file="autounattend.xml" local org="${file/.xml/.org}" local dat="${file/.xml/.dat}" local desc path src loc xml index result + [[ "${DETECTED,,}" == "winxp"* ]] && return 0 + if [ ! -s "$asset" ] || [ ! -f "$asset" ]; then asset="" if [[ "$MANUAL" != [Yy1]* ]]; then @@ -858,13 +724,19 @@ updateImage() { xml=$(basename "$asset") info "Adding $xml for automatic installation..." - if ! wimlib-imagex update "$loc" "$index" --command "add $asset /$file" > /dev/null; then + local answer="$TMP/$xml" + cp "$asset" "$answer" + updateXML "$answer" "$language" + + if ! wimlib-imagex update "$loc" "$index" --command "add $answer /$file" > /dev/null; then MANUAL="Y" warn "failed to add answer file ($xml) to ISO image, $FB" else - wimlib-imagex update "$loc" "$index" --command "add $asset /$dat" > /dev/null || true + wimlib-imagex update "$loc" "$index" --command "add $answer /$dat" > /dev/null || true fi + rm -f "$answer" + fi if [[ "$MANUAL" == [Yy1]* ]]; then @@ -875,9 +747,10 @@ updateImage() { if ! wimlib-imagex update "$loc" "$index" --command "add $TMP/$org /$file" > /dev/null; then warn "failed to restore original answer file ($org)." fi - rm -f "$TMP/$org" fi + rm -f "$TMP/$org" + fi local find="$file" @@ -895,11 +768,22 @@ updateImage() { return 0 } +removeDownload() { + + local iso="$1" + + [ ! -f "$iso" ] && return 0 + [ -n "$CUSTOM" ] && return 0 + ! rm -f "$iso" 2> /dev/null && warn "failed to remove $iso !" + + return 0 +} + copyOEM() { local dir="$1" local folder="/oem" - local src + local src dest file [ ! -d "$folder" ] && folder="/OEM" [ ! -d "$folder" ] && folder="$STORAGE/oem" @@ -915,13 +799,16 @@ copyOEM() { error "failed to locate 'sources' folder in ISO image!" && return 1 fi - local dest="$src/\$OEM\$/\$1/" + dest="$src/\$OEM\$/\$1/" mkdir -p "$dest" if ! cp -r "$folder" "$dest"; then error "Failed to copy OEM folder!" && return 1 fi + file=$(find "$dest" -maxdepth 1 -type f -iname install.bat | head -n 1) + [ -f "$file" ] && unix2dos -q "$file" + return 0 } @@ -1057,6 +944,7 @@ bootWindows() { ###################################### ! parseVersion && exit 58 +! parseLanguage && exit 56 ! detectCustom && exit 59 if ! startInstall; then @@ -1065,7 +953,7 @@ if ! startInstall; then fi if [ ! -s "$ISO" ] || [ ! -f "$ISO" ]; then - if ! downloadImage "$ISO" "$VERSION"; then + if ! downloadImage "$ISO" "$VERSION" "$LANGUAGE"; then rm -f "$ISO" 2> /dev/null || true exit 61 fi @@ -1086,7 +974,7 @@ if ! prepareImage "$ISO" "$DIR"; then exit 60 fi -if ! updateImage "$DIR" "$XML"; then +if ! updateImage "$DIR" "$XML" "$LANGUAGE"; then abortInstall "$ISO" && return 0 exit 60 fi diff --git a/src/mido.sh b/src/mido.sh new file mode 100644 index 0000000..17ce0b9 --- /dev/null +++ b/src/mido.sh @@ -0,0 +1,267 @@ +#!/usr/bin/env bash +set -Eeuo pipefail + +getCatalog() { + + local id="$1" + local ret="$2" + local url="" + local name="" + local edition="" + + case "${id,,}" in + "win11${PLATFORM,,}" ) + edition="Professional" + name="Windows 11 Pro" + url="https://go.microsoft.com/fwlink?linkid=2156292" ;; + "win10${PLATFORM,,}" ) + edition="Professional" + name="Windows 10 Pro" + url="https://go.microsoft.com/fwlink/?LinkId=841361" ;; + esac + + case "${ret,,}" in + "url" ) echo "$url" ;; + "name" ) echo "$name" ;; + "edition" ) echo "$edition" ;; + *) echo "";; + esac + + return 0 +} + +getESD() { + + local dir="$1" + local version="$2" + local lang="$3" + local desc="$4" + local culture + local language + local editionName + local winCatalog size + + culture=$(getLanguage "$lang" "culture") + winCatalog=$(getCatalog "$version" "url") + editionName=$(getCatalog "$version" "edition") + + if [ -z "$winCatalog" ] || [ -z "$editionName" ]; then + error "Invalid VERSION specified, value \"$version\" is not recognized!" && return 1 + fi + + local msg="Downloading product information from Microsoft server..." + info "$msg" && html "$msg" + + rm -rf "$dir" + mkdir -p "$dir" + + local wFile="catalog.cab" + local xFile="products.xml" + local eFile="esd_edition.xml" + local fFile="products_filter.xml" + + { wget "$winCatalog" -O "$dir/$wFile" -q --timeout=30; rc=$?; } || : + (( rc == 4 )) && error "Failed to download $winCatalog , network failure!" && return 1 + (( rc != 0 )) && error "Failed to download $winCatalog , reason: $rc" && return 1 + + cd "$dir" + + if ! cabextract "$wFile" > /dev/null; then + cd /run + error "Failed to extract $wFile!" && return 1 + fi + + cd /run + + if [ ! -s "$dir/$xFile" ]; then + error "Failed to find $xFile in $wFile!" && return 1 + fi + + local edQuery='//File[Architecture="'${PLATFORM}'"][Edition="'${editionName}'"]' + + echo -e '' > "$dir/$fFile" + xmllint --nonet --xpath "${edQuery}" "$dir/$xFile" >> "$dir/$fFile" 2>/dev/null + echo -e ''>> "$dir/$fFile" + + xmllint --nonet --xpath "//File[LanguageCode=\"${culture,,}\"]" "$dir/$fFile" >"$dir/$eFile" + + size=$(stat -c%s "$dir/$eFile") + if ((size<20)); then + language=$(getLanguage "$lang" "desc") + error "the $language language is not supported by this download method!" && return 1 + fi + + local tag="FilePath" + ESD=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g") + + if [ -z "$ESD" ]; then + error "Failed to find ESD URL in $eFile!" && return 1 + fi + + tag="Sha1" + ESD_SUM=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g") + tag="Size" + ESD_SIZE=$(xmllint --nonet --xpath "//$tag" "$dir/$eFile" | sed -E -e "s/<[\/]?$tag>//g") + + rm -rf "$dir" + return 0 +} + +verifyFile() { + + local iso="$1" + local size="$2" + local total="$3" + local check="$4" + + if [ -n "$size" ] && [[ "$total" != "$size" ]] && [[ "$size" != "0" ]]; then + warn "The downloaded file has an unexpected size: $total bytes, while expected value was: $size bytes. Please report this at $SUPPORT/issues" + fi + + local hash="" + local algo="SHA256" + + [ -z "$check" ] && return 0 + [[ "$VERIFY" != [Yy1]* ]] && return 0 + [[ "${#check}" == "40" ]] && algo="SHA1" + + local msg="Verifying downloaded ISO..." + info "$msg" && html "$msg" + + if [[ "${algo,,}" != "sha256" ]]; then + hash=$(sha1sum "$iso" | cut -f1 -d' ') + else + hash=$(sha256sum "$iso" | cut -f1 -d' ') + fi + + if [[ "$hash" == "$check" ]]; then + info "Succesfully verified ISO!" && return 0 + fi + + error "The downloaded file has an invalid $algo checksum: $hash , while expected value was: $check. Please report this at $SUPPORT/issues" + + rm -f "$iso" + return 1 +} + +downloadFile() { + + local iso="$1" + local url="$2" + local sum="$3" + local size="$4" + local lang="$5" + local desc="$6" + local rc total progress domain dots + + rm -f "$iso" + + # Check if running with interactive TTY or redirected to docker log + if [ -t 1 ]; then + progress="--progress=bar:noscroll" + else + progress="--progress=dot:giga" + fi + + local msg="Downloading $desc..." + html "$msg" + + domain=$(echo "$url" | awk -F/ '{print $3}') + dots=$(echo "$domain" | tr -cd '.' | wc -c) + (( dots > 1 )) && domain=$(expr "$domain" : '.*\.\(.*\..*\)') + + if [ -n "$domain" ] && [[ "${domain,,}" != *"microsoft.com" ]]; then + msg="Downloading $desc from $domain..." + fi + + info "$msg" + /run/progress.sh "$iso" "$size" "Downloading $desc ([P])..." & + + { wget "$url" -O "$iso" -q --timeout=30 --show-progress "$progress"; rc=$?; } || : + + fKill "progress.sh" + + if (( rc == 0 )) && [ -f "$iso" ]; then + total=$(stat -c%s "$iso") + if [ "$total" -gt 100000000 ]; then + ! verifyFile "$iso" "$size" "$total" "$sum" && return 1 + html "Download finished successfully..." && return 0 + fi + fi + + if (( rc != 4 )); then + error "Failed to download $url , reason: $rc" + else + error "Failed to download $url , network failure!" + fi + + rm -f "$iso" + return 1 +} + +downloadImage() { + + local iso="$1" + local version="$2" + local lang="$3" + local tried="n" + local url sum size base desc language + + if [[ "${version,,}" == "http"* ]]; then + base=$(basename "$iso") + desc=$(fromFile "$base") + downloadFile "$iso" "$version" "" "" "" "$desc" && return 0 + return 1 + fi + + if ! validVersion "$version" "en"; then + error "Invalid VERSION specified, value \"$version\" is not recognized!" && return 1 + fi + + desc=$(printVersion "$version" "") + + if [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-"* ]]; then + language=$(getLanguage "$lang" "desc") + if ! validVersion "$version" "$lang"; then + desc=$(printEdition "$version" "$desc") + error "The $language language version of $desc is not available, please switch to English." && return 1 + fi + desc="$desc in $language" + fi + + if isESD "$version" "$lang"; then + + if [[ "$tried" != "n" ]]; then + info "Failed to download $desc, will try a diferent method now..." + fi + + tried="y" + + if getESD "$TMP/esd" "$version" "$lang" "$desc"; then + ISO="${ISO%.*}.esd" + downloadFile "$ISO" "$ESD" "$ESD_SUM" "$ESD_SIZE" "$lang" "$desc" && return 0 + ISO="$iso" + fi + + fi + + for ((i=1;i<=MIRRORS;i++)); do + + url=$(getLink "$i" "$version" "$lang") + + if [ -n "$url" ]; then + if [[ "$tried" != "n" ]]; then + info "Failed to download $desc, will try another mirror now..." + fi + tried="y" + size=$(getSize "$i" "$version" "$lang") + sum=$(getHash "$i" "$version" "$lang") + downloadFile "$iso" "$url" "$sum" "$size" "$lang" "$desc" && return 0 + fi + + done + + return 1 +} + +return 0