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