Ver código fonte

feat: Improved installation (#545)

Kroese 1 ano atrás
pai
commit
fd83861e7d
9 arquivos alterados com 1435 adições e 269 exclusões
  1. 3 2
      Dockerfile
  2. 453 0
      assets/win11x64-iot.xml
  3. 453 0
      assets/win11x64-ltsc.xml
  4. 14 51
      readme.md
  5. 99 29
      src/define.sh
  6. 1 1
      src/entry.sh
  7. 276 111
      src/install.sh
  8. 120 65
      src/mido.sh
  9. 16 10
      src/power.sh

+ 3 - 2
Dockerfile

@@ -1,5 +1,5 @@
 FROM scratch
-COPY --from=qemux/qemu-docker:5.07 / /
+COPY --from=qemux/qemu-docker:5.08 / /
 
 ARG VERSION_ARG="0.0"
 ARG DEBCONF_NOWARNINGS="yes"
@@ -14,6 +14,7 @@ RUN set -eu && \
         7zip \
         wsdd \
         samba \
+        xz-utils \
         wimtools \
         dos2unix \
         cabextract \
@@ -27,7 +28,7 @@ COPY --chmod=755 ./src /run/
 COPY --chmod=755 ./assets /run/assets
 
 ADD --chmod=755 https://raw.githubusercontent.com/christgau/wsdd/v0.8/src/wsdd.py /usr/sbin/wsdd
-ADD --chmod=664 https://github.com/qemus/virtiso/releases/download/v0.1.248/virtio-win-0.1.248.iso /run/drivers.iso
+ADD --chmod=664 https://github.com/qemus/virtiso/releases/download/v0.1.248/virtio-win-0.1.248.tar.xz /drivers.txz
 
 EXPOSE 8006 3389
 VOLUME /storage

+ 453 - 0
assets/win11x64-iot.xml

@@ -0,0 +1,453 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
+  <settings pass="windowsPE">
+    <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <SetupUILanguage>
+        <UILanguage>en-US</UILanguage>
+      </SetupUILanguage>
+      <InputLocale>0409:00000409</InputLocale>
+      <SystemLocale>en-US</SystemLocale>
+      <UILanguage>en-US</UILanguage>
+      <UserLocale>en-US</UserLocale>
+    </component>
+    <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DiskConfiguration>
+        <Disk wcm:action="add">
+          <DiskID>0</DiskID>
+          <WillWipeDisk>true</WillWipeDisk>
+          <CreatePartitions>
+            <!-- System partition (ESP) -->
+            <CreatePartition wcm:action="add">
+              <Order>1</Order>
+              <Type>EFI</Type>
+              <Size>128</Size>
+            </CreatePartition>
+            <!-- Microsoft reserved partition (MSR) -->
+            <CreatePartition wcm:action="add">
+              <Order>2</Order>
+              <Type>MSR</Type>
+              <Size>128</Size>
+            </CreatePartition>
+            <!-- Windows partition -->
+            <CreatePartition wcm:action="add">
+              <Order>3</Order>
+              <Type>Primary</Type>
+              <Extend>true</Extend>
+            </CreatePartition>
+          </CreatePartitions>
+          <ModifyPartitions>
+            <!-- System partition (ESP) -->
+            <ModifyPartition wcm:action="add">
+              <Order>1</Order>
+              <PartitionID>1</PartitionID>
+              <Label>System</Label>
+              <Format>FAT32</Format>
+            </ModifyPartition>
+            <!-- MSR partition does not need to be modified -->
+            <ModifyPartition wcm:action="add">
+              <Order>2</Order>
+              <PartitionID>2</PartitionID>
+            </ModifyPartition>
+            <!-- Windows partition -->
+            <ModifyPartition wcm:action="add">
+              <Order>3</Order>
+              <PartitionID>3</PartitionID>
+              <Label>Windows</Label>
+              <Letter>C</Letter>
+              <Format>NTFS</Format>
+            </ModifyPartition>
+          </ModifyPartitions>
+        </Disk>
+      </DiskConfiguration>
+      <ImageInstall>
+        <OSImage>
+          <InstallFrom>
+           <MetaData wcm:action="add">
+              <Key>/image/index</Key>
+              <Value>2</Value>
+            </MetaData>
+          </InstallFrom>
+          <InstallTo>
+            <DiskID>0</DiskID>
+            <PartitionID>3</PartitionID>
+          </InstallTo>
+          <InstallToAvailablePartition>false</InstallToAvailablePartition>
+        </OSImage>
+      </ImageInstall>
+      <DynamicUpdate>
+        <Enable>true</Enable>
+        <WillShowUI>Never</WillShowUI>
+      </DynamicUpdate>
+      <UpgradeData>
+        <Upgrade>false</Upgrade>
+        <WillShowUI>Never</WillShowUI>
+      </UpgradeData>
+      <UserData>
+        <AcceptEula>true</AcceptEula>
+        <FullName>Docker</FullName>
+        <Organization>Windows for Docker</Organization>
+      </UserData>
+      <EnableFirewall>false</EnableFirewall>
+      <Diagnostics>
+        <OptIn>false</OptIn>
+      </Diagnostics>
+      <RunSynchronous>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>1</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\LabConfig" /v BypassTPMCheck /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>2</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\LabConfig" /v BypassSecureBootCheck /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>3</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\LabConfig" /v BypassRAMCheck /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>4</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\MoSetup" /v AllowUpgradesWithUnsupportedTPMOrCPU /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+      </RunSynchronous>
+    </component>
+  </settings>
+  <settings pass="offlineServicing">
+    <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <EnableLUA>false</EnableLUA>
+    </component>
+  </settings>
+  <settings pass="generalize">
+    <component name="Microsoft-Windows-PnPSysprep" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
+    </component>
+    <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <SkipRearm>1</SkipRearm>
+    </component>
+  </settings>
+  <settings pass="specialize">
+    <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <SkipAutoActivation>true</SkipAutoActivation>
+    </component>
+    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <ComputerName>*</ComputerName>
+      <OEMInformation>
+        <Manufacturer>Dockur</Manufacturer>
+        <Model>Windows for Docker</Model>
+        <SupportHours>24/7</SupportHours>
+        <SupportPhone />
+        <SupportProvider>Dockur</SupportProvider>
+        <SupportURL>https://github.com/dockur/windows/issues</SupportURL>
+      </OEMInformation>
+      <OEMName>Windows for Docker</OEMName>
+    </component>
+    <component name="Microsoft-Windows-ErrorReportingCore" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableWER>1</DisableWER>
+    </component>
+    <component name="Microsoft-Windows-IE-InternetExplorer" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableAccelerators>true</DisableAccelerators>
+      <DisableFirstRunWizard>true</DisableFirstRunWizard>
+      <Home_Page>https://google.com</Home_Page>
+      <Help_Page>about:blank</Help_Page>
+    </component>
+    <component name="Microsoft-Windows-IE-InternetExplorer" processorArchitecture="wow64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableAccelerators>true</DisableAccelerators>
+      <DisableFirstRunWizard>true</DisableFirstRunWizard>
+      <Home_Page>https://google.com</Home_Page>
+      <Help_Page>about:blank</Help_Page>
+    </component>
+    <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <CEIPEnabled>0</CEIPEnabled>
+    </component>
+    <component name="Microsoft-Windows-SystemRestore-Main" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableSR>1</DisableSR>
+    </component>
+    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <InputLocale>0409:00000409</InputLocale>
+      <SystemLocale>en-US</SystemLocale>
+      <UILanguage>en-US</UILanguage>
+      <UserLocale>en-US</UserLocale>
+    </component>
+    <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <RunSynchronous>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>1</Order>
+          <Path>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE" /v BypassNRO /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>2</Order>
+          <Path>reg.exe load "HKU\mount" "C:\Users\Default\NTUSER.DAT"</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>3</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "ContentDeliveryAllowed" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>4</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "FeatureManagementEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>5</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "OEMPreInstalledAppsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>6</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "PreInstalledAppsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>7</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "PreInstalledAppsEverEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>8</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SilentInstalledAppsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>9</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SoftLandingEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>10</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContentEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>11</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-310093Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>12</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338387Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>13</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338388Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>14</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338389Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>15</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338393Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>16</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-353698Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>17</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SystemPaneSuggestionsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>18</Order>
+          <Path>reg.exe add "HKU\mount\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableCloudOptimizedContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>19</Order>
+          <Path>reg.exe add "HKU\mount\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>20</Order>
+          <Path>reg.exe add "HKU\mount\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableConsumerAccountStateContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>21</Order>
+          <Path>reg.exe unload "HKU\mount"</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>22</Order>
+          <Path>reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableCloudOptimizedContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>23</Order>
+          <Path>reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>24</Order>
+          <Path>reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableConsumerAccountStateContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>25</Order>
+          <Path>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\FirstNetwork" /v Category /t REG_DWORD /d 1 /f</Path>
+          <Description>Set Network Location to Home</Description>
+        </RunSynchronousCommand>
+      </RunSynchronous>
+    </component>
+    <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <fDenyTSConnections>false</fDenyTSConnections>
+    </component>
+    <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <UserAuthentication>0</UserAuthentication>
+    </component>
+    <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <FirewallGroups>
+        <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
+          <Active>true</Active>
+          <Profile>all</Profile>
+          <Group>@FirewallAPI.dll,-28752</Group>
+        </FirewallGroup>
+      </FirewallGroups>
+    </component>
+  </settings>
+  <settings pass="auditSystem" />
+  <settings pass="auditUser" />
+  <settings pass="oobeSystem">
+    <component name="Microsoft-Windows-SecureStartup-FilterDriver" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <PreventDeviceEncryption>true</PreventDeviceEncryption>
+    </component>
+    <component name="Microsoft-Windows-EnhancedStorage-Adm" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <TCGSecurityActivationDisabled>1</TCGSecurityActivationDisabled>
+    </component>
+    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <UserAccounts>
+        <LocalAccounts>
+          <LocalAccount wcm:action="add">
+            <Name>Docker</Name>
+            <Group>Administrators</Group>
+            <Password>
+              <Value />
+              <PlainText>true</PlainText>
+            </Password>
+          </LocalAccount>
+        </LocalAccounts>
+        <AdministratorPassword>
+          <Value>password</Value>
+          <PlainText>true</PlainText>
+        </AdministratorPassword>
+      </UserAccounts>
+      <AutoLogon>
+        <Username>Docker</Username>
+        <Enabled>true</Enabled>
+        <LogonCount>65432</LogonCount>
+        <Password>
+          <Value />
+          <PlainText>true</PlainText>
+        </Password>
+      </AutoLogon>
+      <Display>
+        <ColorDepth>32</ColorDepth>
+        <HorizontalResolution>1920</HorizontalResolution>
+        <VerticalResolution>1080</VerticalResolution>
+      </Display>
+      <OOBE>
+        <HideEULAPage>true</HideEULAPage>
+        <HideLocalAccountScreen>true</HideLocalAccountScreen>
+        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
+        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
+        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
+        <NetworkLocation>Home</NetworkLocation>
+        <ProtectYourPC>3</ProtectYourPC>
+        <SkipUserOOBE>true</SkipUserOOBE>
+        <SkipMachineOOBE>true</SkipMachineOOBE>
+      </OOBE>
+      <RegisteredOrganization>Dockur</RegisteredOrganization>
+      <RegisteredOwner>Windows for Docker</RegisteredOwner>
+      <FirstLogonCommands>
+        <SynchronousCommand wcm:action="add">
+          <Order>1</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Allow guest access to network shares</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>2</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Allow RDP login with blank password</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>3</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Enable option for passwordless sign-in</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>4</Order>
+          <CommandLine>cmd /C wmic useraccount where name="Docker" set PasswordExpires=false</CommandLine>
+          <Description>Password Never Expires</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>5</Order>
+          <CommandLine>cmd /C POWERCFG -H OFF</CommandLine>
+          <Description>Disable Hibernation</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>6</Order>
+          <CommandLine>cmd /C POWERCFG -X -monitor-timeout-ac 0</CommandLine>
+          <Description>Disable monitor blanking</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>7</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Edge" /v "HideFirstRunExperience" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Disable first-run experience in Edge</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>8</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "HideFileExt" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Show file extensions in Explorer</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>9</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Power" /v "HibernateFileSizePercent" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Zero Hibernation File</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>10</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Power" /v "HibernateEnabled" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Disable Hibernation</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>11</Order>
+          <CommandLine>cmd /C POWERCFG -X -standby-timeout-ac 0</CommandLine>
+          <Description>Disable Sleep</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>12</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" /v "fAllowUnlistedRemotePrograms" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Enable RemoteAPP to launch unlisted programs</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>13</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowTaskViewButton" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Remove Task View from the Taskbar</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>14</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarDa" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Remove Widgets from the Taskbar</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>15</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarMn" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Remove Chat from the Taskbar</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>16</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v "NoAutoUpdate" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Turn off Windows Update auto download</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>17</Order>
+          <CommandLine>netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Yes</CommandLine>
+          <Description>Enable Network Discovery</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>18</Order>
+          <CommandLine>netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes</CommandLine>
+          <Description>Enable File Sharing</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>19</Order>
+          <CommandLine>reg.exe add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v SV1 /d 0 /t REG_DWORD /f</CommandLine>
+          <Description>Disable unsupported hardware notifications</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>20</Order>
+          <CommandLine>reg.exe add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v SV2 /d 0 /t REG_DWORD /f</CommandLine>
+          <Description>Disable unsupported hardware notifications</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>21</Order>
+          <CommandLine>cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat"</CommandLine>
+          <Description>Execute custom script from the OEM folder if exists</Description>
+        </SynchronousCommand>
+      </FirstLogonCommands>
+    </component>
+  </settings>
+</unattend>

+ 453 - 0
assets/win11x64-ltsc.xml

@@ -0,0 +1,453 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<unattend xmlns="urn:schemas-microsoft-com:unattend" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
+  <settings pass="windowsPE">
+    <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <SetupUILanguage>
+        <UILanguage>en-US</UILanguage>
+      </SetupUILanguage>
+      <InputLocale>0409:00000409</InputLocale>
+      <SystemLocale>en-US</SystemLocale>
+      <UILanguage>en-US</UILanguage>
+      <UserLocale>en-US</UserLocale>
+    </component>
+    <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DiskConfiguration>
+        <Disk wcm:action="add">
+          <DiskID>0</DiskID>
+          <WillWipeDisk>true</WillWipeDisk>
+          <CreatePartitions>
+            <!-- System partition (ESP) -->
+            <CreatePartition wcm:action="add">
+              <Order>1</Order>
+              <Type>EFI</Type>
+              <Size>128</Size>
+            </CreatePartition>
+            <!-- Microsoft reserved partition (MSR) -->
+            <CreatePartition wcm:action="add">
+              <Order>2</Order>
+              <Type>MSR</Type>
+              <Size>128</Size>
+            </CreatePartition>
+            <!-- Windows partition -->
+            <CreatePartition wcm:action="add">
+              <Order>3</Order>
+              <Type>Primary</Type>
+              <Extend>true</Extend>
+            </CreatePartition>
+          </CreatePartitions>
+          <ModifyPartitions>
+            <!-- System partition (ESP) -->
+            <ModifyPartition wcm:action="add">
+              <Order>1</Order>
+              <PartitionID>1</PartitionID>
+              <Label>System</Label>
+              <Format>FAT32</Format>
+            </ModifyPartition>
+            <!-- MSR partition does not need to be modified -->
+            <ModifyPartition wcm:action="add">
+              <Order>2</Order>
+              <PartitionID>2</PartitionID>
+            </ModifyPartition>
+            <!-- Windows partition -->
+            <ModifyPartition wcm:action="add">
+              <Order>3</Order>
+              <PartitionID>3</PartitionID>
+              <Label>Windows</Label>
+              <Letter>C</Letter>
+              <Format>NTFS</Format>
+            </ModifyPartition>
+          </ModifyPartitions>
+        </Disk>
+      </DiskConfiguration>
+      <ImageInstall>
+        <OSImage>
+          <InstallFrom>
+           <MetaData wcm:action="add">
+              <Key>/image/index</Key>
+              <Value>1</Value>
+            </MetaData>
+          </InstallFrom>
+          <InstallTo>
+            <DiskID>0</DiskID>
+            <PartitionID>3</PartitionID>
+          </InstallTo>
+          <InstallToAvailablePartition>false</InstallToAvailablePartition>
+        </OSImage>
+      </ImageInstall>
+      <DynamicUpdate>
+        <Enable>true</Enable>
+        <WillShowUI>Never</WillShowUI>
+      </DynamicUpdate>
+      <UpgradeData>
+        <Upgrade>false</Upgrade>
+        <WillShowUI>Never</WillShowUI>
+      </UpgradeData>
+      <UserData>
+        <AcceptEula>true</AcceptEula>
+        <FullName>Docker</FullName>
+        <Organization>Windows for Docker</Organization>
+      </UserData>
+      <EnableFirewall>false</EnableFirewall>
+      <Diagnostics>
+        <OptIn>false</OptIn>
+      </Diagnostics>
+      <RunSynchronous>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>1</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\LabConfig" /v BypassTPMCheck /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>2</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\LabConfig" /v BypassSecureBootCheck /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>3</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\LabConfig" /v BypassRAMCheck /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>4</Order>
+          <Path>reg.exe add "HKLM\SYSTEM\Setup\MoSetup" /v AllowUpgradesWithUnsupportedTPMOrCPU /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+      </RunSynchronous>
+    </component>
+  </settings>
+  <settings pass="offlineServicing">
+    <component name="Microsoft-Windows-LUA-Settings" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <EnableLUA>false</EnableLUA>
+    </component>
+  </settings>
+  <settings pass="generalize">
+    <component name="Microsoft-Windows-PnPSysprep" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
+    </component>
+    <component name="Microsoft-Windows-Security-SPP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <SkipRearm>1</SkipRearm>
+    </component>
+  </settings>
+  <settings pass="specialize">
+    <component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <SkipAutoActivation>true</SkipAutoActivation>
+    </component>
+    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <ComputerName>*</ComputerName>
+      <OEMInformation>
+        <Manufacturer>Dockur</Manufacturer>
+        <Model>Windows for Docker</Model>
+        <SupportHours>24/7</SupportHours>
+        <SupportPhone />
+        <SupportProvider>Dockur</SupportProvider>
+        <SupportURL>https://github.com/dockur/windows/issues</SupportURL>
+      </OEMInformation>
+      <OEMName>Windows for Docker</OEMName>
+    </component>
+    <component name="Microsoft-Windows-ErrorReportingCore" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableWER>1</DisableWER>
+    </component>
+    <component name="Microsoft-Windows-IE-InternetExplorer" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableAccelerators>true</DisableAccelerators>
+      <DisableFirstRunWizard>true</DisableFirstRunWizard>
+      <Home_Page>https://google.com</Home_Page>
+      <Help_Page>about:blank</Help_Page>
+    </component>
+    <component name="Microsoft-Windows-IE-InternetExplorer" processorArchitecture="wow64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableAccelerators>true</DisableAccelerators>
+      <DisableFirstRunWizard>true</DisableFirstRunWizard>
+      <Home_Page>https://google.com</Home_Page>
+      <Help_Page>about:blank</Help_Page>
+    </component>
+    <component name="Microsoft-Windows-SQMApi" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <CEIPEnabled>0</CEIPEnabled>
+    </component>
+    <component name="Microsoft-Windows-SystemRestore-Main" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <DisableSR>1</DisableSR>
+    </component>
+    <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <InputLocale>0409:00000409</InputLocale>
+      <SystemLocale>en-US</SystemLocale>
+      <UILanguage>en-US</UILanguage>
+      <UserLocale>en-US</UserLocale>
+    </component>
+    <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <RunSynchronous>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>1</Order>
+          <Path>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\OOBE" /v BypassNRO /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>2</Order>
+          <Path>reg.exe load "HKU\mount" "C:\Users\Default\NTUSER.DAT"</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>3</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "ContentDeliveryAllowed" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>4</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "FeatureManagementEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>5</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "OEMPreInstalledAppsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>6</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "PreInstalledAppsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>7</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "PreInstalledAppsEverEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>8</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SilentInstalledAppsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>9</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SoftLandingEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>10</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContentEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>11</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-310093Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>12</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338387Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>13</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338388Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>14</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338389Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>15</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-338393Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>16</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SubscribedContent-353698Enabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>17</Order>
+          <Path>reg.exe add "HKU\mount\Software\Microsoft\Windows\CurrentVersion\ContentDeliveryManager" /v "SystemPaneSuggestionsEnabled" /t REG_DWORD /d 0 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>18</Order>
+          <Path>reg.exe add "HKU\mount\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableCloudOptimizedContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>19</Order>
+          <Path>reg.exe add "HKU\mount\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>20</Order>
+          <Path>reg.exe add "HKU\mount\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableConsumerAccountStateContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>21</Order>
+          <Path>reg.exe unload "HKU\mount"</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>22</Order>
+          <Path>reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableCloudOptimizedContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>23</Order>
+          <Path>reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableWindowsConsumerFeatures" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>24</Order>
+          <Path>reg.exe add "HKLM\Software\Policies\Microsoft\Windows\CloudContent" /v "DisableConsumerAccountStateContent" /t REG_DWORD /d 1 /f</Path>
+        </RunSynchronousCommand>
+        <RunSynchronousCommand wcm:action="add">
+          <Order>25</Order>
+          <Path>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\CurrentVersion\NetworkList\Signatures\FirstNetwork" /v Category /t REG_DWORD /d 1 /f</Path>
+          <Description>Set Network Location to Home</Description>
+        </RunSynchronousCommand>
+      </RunSynchronous>
+    </component>
+    <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <fDenyTSConnections>false</fDenyTSConnections>
+    </component>
+    <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <UserAuthentication>0</UserAuthentication>
+    </component>
+    <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <FirewallGroups>
+        <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
+          <Active>true</Active>
+          <Profile>all</Profile>
+          <Group>@FirewallAPI.dll,-28752</Group>
+        </FirewallGroup>
+      </FirewallGroups>
+    </component>
+  </settings>
+  <settings pass="auditSystem" />
+  <settings pass="auditUser" />
+  <settings pass="oobeSystem">
+    <component name="Microsoft-Windows-SecureStartup-FilterDriver" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <PreventDeviceEncryption>true</PreventDeviceEncryption>
+    </component>
+    <component name="Microsoft-Windows-EnhancedStorage-Adm" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <TCGSecurityActivationDisabled>1</TCGSecurityActivationDisabled>
+    </component>
+    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
+      <UserAccounts>
+        <LocalAccounts>
+          <LocalAccount wcm:action="add">
+            <Name>Docker</Name>
+            <Group>Administrators</Group>
+            <Password>
+              <Value />
+              <PlainText>true</PlainText>
+            </Password>
+          </LocalAccount>
+        </LocalAccounts>
+        <AdministratorPassword>
+          <Value>password</Value>
+          <PlainText>true</PlainText>
+        </AdministratorPassword>
+      </UserAccounts>
+      <AutoLogon>
+        <Username>Docker</Username>
+        <Enabled>true</Enabled>
+        <LogonCount>65432</LogonCount>
+        <Password>
+          <Value />
+          <PlainText>true</PlainText>
+        </Password>
+      </AutoLogon>
+      <Display>
+        <ColorDepth>32</ColorDepth>
+        <HorizontalResolution>1920</HorizontalResolution>
+        <VerticalResolution>1080</VerticalResolution>
+      </Display>
+      <OOBE>
+        <HideEULAPage>true</HideEULAPage>
+        <HideLocalAccountScreen>true</HideLocalAccountScreen>
+        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
+        <HideOnlineAccountScreens>true</HideOnlineAccountScreens>
+        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
+        <NetworkLocation>Home</NetworkLocation>
+        <ProtectYourPC>3</ProtectYourPC>
+        <SkipUserOOBE>true</SkipUserOOBE>
+        <SkipMachineOOBE>true</SkipMachineOOBE>
+      </OOBE>
+      <RegisteredOrganization>Dockur</RegisteredOrganization>
+      <RegisteredOwner>Windows for Docker</RegisteredOwner>
+      <FirstLogonCommands>
+        <SynchronousCommand wcm:action="add">
+          <Order>1</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Services\LanmanWorkstation\Parameters" /v "AllowInsecureGuestAuth" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Allow guest access to network shares</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>2</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" /v LimitBlankPasswordUse /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Allow RDP login with blank password</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>3</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v "DevicePasswordLessBuildVersion" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Enable option for passwordless sign-in</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>4</Order>
+          <CommandLine>cmd /C wmic useraccount where name="Docker" set PasswordExpires=false</CommandLine>
+          <Description>Password Never Expires</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>5</Order>
+          <CommandLine>cmd /C POWERCFG -H OFF</CommandLine>
+          <Description>Disable Hibernation</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>6</Order>
+          <CommandLine>cmd /C POWERCFG -X -monitor-timeout-ac 0</CommandLine>
+          <Description>Disable monitor blanking</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>7</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Edge" /v "HideFirstRunExperience" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Disable first-run experience in Edge</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>8</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "HideFileExt" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Show file extensions in Explorer</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>9</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Power" /v "HibernateFileSizePercent" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Zero Hibernation File</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>10</Order>
+          <CommandLine>reg.exe add "HKLM\SYSTEM\CurrentControlSet\Control\Power" /v "HibernateEnabled" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Disable Hibernation</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>11</Order>
+          <CommandLine>cmd /C POWERCFG -X -standby-timeout-ac 0</CommandLine>
+          <Description>Disable Sleep</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>12</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Terminal Services" /v "fAllowUnlistedRemotePrograms" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Enable RemoteAPP to launch unlisted programs</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>13</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "ShowTaskViewButton" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Remove Task View from the Taskbar</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>14</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarDa" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Remove Widgets from the Taskbar</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>15</Order>
+          <CommandLine>reg.exe add "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Advanced" /v "TaskbarMn" /t REG_DWORD /d 0 /f</CommandLine>
+          <Description>Remove Chat from the Taskbar</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>16</Order>
+          <CommandLine>reg.exe add "HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU" /v "NoAutoUpdate" /t REG_DWORD /d 1 /f</CommandLine>
+          <Description>Turn off Windows Update auto download</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>17</Order>
+          <CommandLine>netsh advfirewall firewall set rule group="@FirewallAPI.dll,-32752" new enable=Yes</CommandLine>
+          <Description>Enable Network Discovery</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>18</Order>
+          <CommandLine>netsh advfirewall firewall set rule group="@FirewallAPI.dll,-28502" new enable=Yes</CommandLine>
+          <Description>Enable File Sharing</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>19</Order>
+          <CommandLine>reg.exe add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v SV1 /d 0 /t REG_DWORD /f</CommandLine>
+          <Description>Disable unsupported hardware notifications</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>20</Order>
+          <CommandLine>reg.exe add "HKCU\Control Panel\UnsupportedHardwareNotificationCache" /v SV2 /d 0 /t REG_DWORD /f</CommandLine>
+          <Description>Disable unsupported hardware notifications</Description>
+        </SynchronousCommand>
+        <SynchronousCommand wcm:action="add">
+          <Order>21</Order>
+          <CommandLine>cmd /C if exist "C:\OEM\install.bat" start "Install" "cmd /C C:\OEM\install.bat"</CommandLine>
+          <Description>Execute custom script from the OEM folder if exists</Description>
+        </SynchronousCommand>
+      </FirstLogonCommands>
+    </component>
+  </settings>
+</unattend>

+ 14 - 51
readme.md

@@ -87,12 +87,12 @@ kubectl apply -f kubernetes.yml
   |---|---|---|
   | `win11`   | Windows 11 Pro           | 6.4 GB   |
   | `win11e`  | Windows 11 Enterprise    | 5.8 GB   |
-  | `win10`   | Windows 10 Pro           | 5.8 GB   |
+  | `win10`   | Windows 10 Pro           | 5.7 GB   |
   | `ltsc10`  | Windows 10 LTSC          | 4.6 GB   |
   | `win10e`  | Windows 10 Enterprise    | 5.2 GB   |
   ||||  
-  | `win8`    | Windows 8.1 Pro          | 4.2 GB   |
-  | `win8e`   | Windows 8.1 Enterprise   | 3.8 GB   |
+  | `win8`    | Windows 8.1 Pro          | 4.0 GB   |
+  | `win8e`   | Windows 8.1 Enterprise   | 3.7 GB   |
   | `win7`    | Windows 7 Enterprise     | 3.0 GB   |
   | `vista`   | Windows Vista Enterprise | 3.0 GB   |
   | `winxp`   | Windows XP Professional  | 0.6 GB   |
@@ -179,22 +179,9 @@ kubectl apply -f kubernetes.yml
 
   Replace the example path `/home/user/example.iso` with the filename of your desired ISO file, the value of `VERSION` will be ignored in this case.
 
-* ### How do I customize the installation?
-
-  If you want to modify the settings used during the automatic installation, you can do this by editing the answer file corresponding to your Windows edition, for example [win11x64.xml](https://raw.githubusercontent.com/dockur/windows/master/assets/win11x64.xml) in the case of Windows 11 Pro.
-
-  Apply your modifications to it, and add this line to your compose file:
-
-  ```yaml
-  volumes:
-    -  /home/user/example.xml:/custom.xml
-  ```
-
-  Replace the example path `/home/user/example.xml` with the filename of the modified XML file.
-
 * ### How do I run a script after installation?
 
-  To run your own script after installation, you can create a file called `install.bat` and place it in a folder together with other files it needs (programs to install for example). Then bind it in your compose file like this:
+  To run your own script after installation, you can create a file called `install.bat` and place it in a folder together with any additional files it needs (software to be installed for example). Then bind that folder in your compose file like this:
 
   ```yaml
   volumes:
@@ -205,7 +192,7 @@ kubectl apply -f kubernetes.yml
 
 * ### How do I perform a manual installation?
 
-  It's best to use the automatic installation, as it prevents common issues that occur when running Windows inside a virtualized environment and optimizes various settings to give you maximum performance.
+  It's best to stick to the automatic installation, as it adjusts various settings to prevent common issues when running Windows inside a virtual environment.
 
   However, if you insist on performing the installation manually, add the following environment variable to your compose file:
 
@@ -214,32 +201,6 @@ kubectl apply -f kubernetes.yml
     MANUAL: "Y"
   ```
 
-  Then follow these steps:
-
-  - Start the container and connect to [port 8006](http://localhost:8006) of the container in your web browser. After the download is finished, you will see the Windows installation screen.
-
-  - Start the installation by clicking `Install now`. On the next screen, press 'OK' when prompted to `Load driver`.
-
-  -  Select the `VirtIO SCSI` driver from the list that matches your Windows version. So for Windows 11, select `D:\amd64\w11\vioscsi.inf` and click 'Next'.
-
-  - Accept the license agreement and select your preferred Windows edition, like Home or Pro.
-
-  - Choose `Custom: Install Windows only (advanced)`, and click `Load driver` on the next screen.
-
-  - Select 'Browse' and navigate to the `D:\NetKVM\w11\amd64` folder, and click 'OK'.
-
-  - Select the `VirtIO Ethernet Adapter` from the list and click 'Next'.
-
-  - Select `Drive 0` and click 'Next'.
-
-  - Wait until Windows finishes copying files and completes the installation.
-
-  - Once you see the desktop, open File Explorer and navigate to the CD-ROM drive (`E:\`).
-
-  - Double-click on `virtio-win-gt-x64.msi` and proceed to install the VirtIO drivers.
-
-  Enjoy your brand new machine, and don't forget to star this repo!
- 
 * ### How do I verify if my system supports KVM?
 
   To verify if your system supports KVM, run the following commands:
@@ -251,11 +212,11 @@ kubectl apply -f kubernetes.yml
 
   If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check the virtualization settings in the BIOS.
 
-* ### How do I increase the amount of CPU or RAM?
+* ### How do I change the amount of CPU or RAM?
 
-  By default, 2 CPU cores and 4 GB of RAM are allocated to the container, as those are the minimum requirements of Windows 11.
+  By default, the container will be allowed to use a maximum of 2 CPU cores and 4 GB of RAM.
 
-  If there arises a need to increase this, add the following environment variables:
+  If you want to adjust this, you can specify the desired amount using the following environment variables:
 
   ```yaml
   environment:
@@ -265,12 +226,14 @@ kubectl apply -f kubernetes.yml
 
 * ### 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:
+  By default, a user called `Docker` is created during the installation, with an empty password.
+
+  If you want to use different credentials, you can change them in your compose file:
 
   ```yaml
   environment:
-    USERNAME: "john"
-    PASSWORD: "secret"
+    USERNAME: "bill"
+    PASSWORD: "gates"
   ```
 
 * ### How do I connect using RDP?
@@ -279,7 +242,7 @@ kubectl apply -f kubernetes.yml
 
   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.
+  There is a 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.
 
 * ### How do I assign an individual IP address to the container?
 

+ 99 - 29
src/define.sh

@@ -105,6 +105,10 @@ parseVersion() {
       VERSION="win10x64-enterprise-iot-eval"
       [ -z "$DETECTED" ] && DETECTED="win10x64-iot"
       ;;
+    "ltsc11" | "11ltsc" | "win11-ltsc" | "win11x64-ltsc" | "win11x64-enterprise-ltsc-eval" )
+      VERSION="win11x64-enterprise-ltsc-eval"
+      [ -z "$DETECTED" ] && DETECTED="win11x64-iot"
+      ;;
     "ltsc10" | "10ltsc" | "win10-ltsc" | "win10x64-ltsc" | "win10x64-enterprise-ltsc-eval" )
       VERSION="win10x64-enterprise-ltsc-eval"
       [ -z "$DETECTED" ] && DETECTED="win10x64-ltsc"
@@ -358,6 +362,10 @@ printVersion() {
     "win10"* ) desc="Windows 10" ;;
     "win11"* ) desc="Windows 11" ;;
     "winxp"* ) desc="Windows XP" ;;
+    "win9x"* ) desc="Windows ME" ;;
+    "win98"* ) desc="Windows 98" ;;
+    "win95"* ) desc="Windows 95" ;;
+    "win2k"* ) desc="Windows 2000" ;;
     "winvista"* ) desc="Windows Vista" ;;
     "win2025"* ) desc="Windows Server 2025" ;;
     "win2022"* ) desc="Windows Server 2022" ;;
@@ -556,19 +564,10 @@ getVersion() {
           *" enterprise"* ) id="$id-enterprise" ;;
         esac
       ;;
-    "win10"* )
-        case "${name,,}" in
-          *" iot"* ) id="$id-iot" ;;
-          *" ltsc"* ) id="$id-ltsc" ;;
-          *" home"* ) id="$id-home" ;;
-          *" education"* ) id="$id-education" ;;
-          *" enterprise evaluation"* ) id="$id-enterprise-eval" ;;
-          *" enterprise"* ) id="$id-enterprise" ;;
-        esac
-      ;;
-    "win11"* )
+    "win10"* | "win11"* )
        case "${name,,}" in
           *" iot"* ) id="$id-iot" ;;
+          *" ltsc"* ) id="$id-ltsc" ;;
           *" home"* ) id="$id-home" ;;
           *" education"* ) id="$id-education" ;;
           *" enterprise evaluation"* ) id="$id-enterprise-eval" ;;
@@ -629,6 +628,14 @@ getMido() {
       size=6209064960
       sum="c8dbc96b61d04c8b01faf6ce0794fdf33965c7b350eaa3eb1e6697019902945c"
       ;;
+    "win11x64-enterprise-ltsc-eval" )
+      size=4428627968
+      sum="8abf91c9cd408368dc73aab3425d5e3c02dae74900742072eb5c750fc637c195"
+      ;;
+    "win11x64-enterprise-iot-eval" )
+      size=4428627968
+      sum="8abf91c9cd408368dc73aab3425d5e3c02dae74900742072eb5c750fc637c195"
+      ;;
     "win10x64" )
       size=6140975104
       sum="a6f470ca6d331eb353b815c043e327a347f594f37ff525f17764738fe812852e"
@@ -1034,6 +1041,18 @@ getLink4() {
         "zh" | "zh-"* ) url="zh-cn_windows_11_business_editions_version_23h2_updated_april_2024_x64_dvd_3db5a62b.iso" ;;
       esac
       ;;
+    "win11x64-iot" | "win11x64-enterprise-iot-eval" )
+      [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
+      size=4821989376
+      sum="e8f1431c4e6289b3997c20eadbb2576670300bb6e1cf8948b5d7af179010a962"
+      url="26100.1.240331-1435.ge_release_CLIENTENTERPRISE_OEM_x64FRE_en-us.iso"
+      ;;
+    "win11x64-ltsc" | "win11x64-enterprise-ltsc-eval" )
+      [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
+      size=4821989376
+      sum="e8f1431c4e6289b3997c20eadbb2576670300bb6e1cf8948b5d7af179010a962"
+      url="26100.1.240331-1435.ge_release_CLIENTENTERPRISE_OEM_x64FRE_en-us.iso"
+      ;;
     "win10x64" )
       case "${culture,,}" in
         "ar" | "ar-"* ) url="ar-sa_windows_10_consumer_editions_version_22h2_updated_april_2024_x64_dvd_9a92dc89.iso" ;;
@@ -1169,12 +1188,6 @@ getLink4() {
         "zh" | "zh-"* ) url="zh-cn_windows_10_enterprise_ltsc_2021_x64_dvd_033b7312.iso" ;;
       esac
       ;;
-    "win11x64-iot" | "win11x64-enterprise-iot-eval" )
-      [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
-      size=6248140800
-      sum="5d9b86ad467bc89f488d1651a6c5ad3656a7ea923f9f914510657a24c501bb86"
-      url="en-us_windows_11_iot_enterprise_version_23h2_x64_dvd_fb37549c.iso"
-      ;;
     "win10x64-iot" | "win10x64-enterprise-iot-eval" )
       [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-us" ]] && return 0
       size=4851668992
@@ -1918,6 +1931,32 @@ prepareLegacy() {
   return 1
 }
 
+prepare9x() {
+
+  local iso="$1"
+  local dir="$2"
+  local file="$dir/boot.img"
+
+  ETFS=$(basename "$file")
+  [ -f "$file" ] && [ -s "$file" ] && return 0
+  rm -f "$file"
+
+  local src="[BOOT]/Boot-1.44M.img"
+  [ ! -f "$dir/$src" ] && error "Boot floppy not found!" && return 1
+
+  cp "$dir/$src" "$file" && return 0
+
+  return 1
+}
+
+prepare2k() {
+
+  local dir="$2"
+  ETFS="[BOOT]/Boot-NoEmul.img"
+
+  return 0
+}
+
 prepareXP() {
 
   local dir="$2"
@@ -1932,10 +1971,13 @@ prepareXP() {
     target="$dir/AMD64"
   fi
 
-  rm -rf "$drivers"
+  local msg="Adding drivers to image..."
+  info "$msg" && html "$msg"
+
+  mkdir -p "$drivers"
 
-  if ! 7z x /run/drivers.iso -o"$drivers" > /dev/null; then
-    error "Failed to extract driver ISO file!" && return 1
+  if ! tar -xf /drivers.txz -C "$drivers" --warning=no-timestamp; then
+    error "Failed to extract driver!" && return 1
   fi
 
   cp "$drivers/viostor/xp/$arch/viostor.sys" "$target"
@@ -1978,14 +2020,16 @@ prepareXP() {
   sed -i '/^\[SCSI\]/s/$/\niaStor=\"Intel\(R\) SATA RAID\/AHCI Controller\"/' "$target/TXTSETUP.SIF"
   sed -i '/^\[HardwareIdsDatabase\]/s/$/\nPCI\\VEN_8086\&DEV_2922\&CC_0106=\"iaStor\"/' "$target/TXTSETUP.SIF"
 
-  local key pid setup
+  rm -rf "$drivers"
+
+  local key pid file setup
   setup=$(find "$target" -maxdepth 1 -type f -iname setupp.ini | head -n 1)
   pid=$(<"$setup")
   pid="${pid:(-4)}"
   pid="${pid:0:3}"
 
   if [[ "$pid" == "270" ]]; then
-    info "Warning: this XP version requires a volume license, it will reject the generic key during installation."
+    warn "this version of Windows XP requires a volume license key (VLK), it will ask for one during installation."
   fi
 
   if [[ "${arch,,}" == "x86" ]]; then
@@ -1998,10 +2042,28 @@ prepareXP() {
     key="B2RBK-7KPT9-4JP6X-QQFWM-PJD6G"
   fi
 
+  local oem=""
+  local folder="/oem"
+
+  [ ! -d "$folder" ] && folder="/OEM"
+  [ ! -d "$folder" ] && folder="$STORAGE/oem"
+  [ ! -d "$folder" ] && folder="$STORAGE/OEM"
+
+  if [ -d "$folder" ]; then
+
+    file=$(find "$folder" -maxdepth 1 -type f -iname install.bat | head -n 1)
+
+    if [ -f "$file" ]; then
+      unix2dos -q "$file"
+      oem="\"Script\"=\"cmd /C start \\\"Install\\\" \\\"cmd /C C:\\\\OEM\\\\install.bat\\\"\""
+    fi
+  fi
+
   local username="Docker"
   local password="*"
-  [ -n "$USERNAME" ] && username="$USERNAME"
+
   [ -n "$PASSWORD" ] && password="$PASSWORD"
+  [ -n "$USERNAME" ] && username=$(echo "$USERNAME" | sed 's/[^[:alnum:]@!._-]//g')
 
   find "$target" -maxdepth 1 -type f -iname winnt.sif -exec rm {} \;
 
@@ -2055,9 +2117,6 @@ prepareXP() {
           echo "    Home_Page = http://www.google.com"
           echo "    Search_Page = http://www.google.com"
           echo ""
-          echo "[RegionalSettings]"
-          echo "    Language=00000409"
-          echo ""
           echo "[TerminalServices]"
           echo "    AllowConnections=1"
           echo ""
@@ -2098,10 +2157,10 @@ prepareXP() {
           echo "\"DefaultSettings.XResolution\"=dword:00000780"
           echo "\"DefaultSettings.YResolution\"=dword:00000438"
           echo ""
-          echo "[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnceEx]"
+          echo "[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce]"
           echo "\"ScreenSaver\"=\"reg add \\\"HKCU\\\\Control Panel\\\\Desktop\\\" /f /v \\\"SCRNSAVE.EXE\\\" /t REG_SZ /d \\\"off\\\"\""
           echo "\"ScreenSaverOff\"=\"reg add \\\"HKCU\\\\Control Panel\\\\Desktop\\\" /f /v \\\"ScreenSaveActive\\\" /t REG_SZ /d \\\"0\\\"\""
-          echo ""
+          echo "$oem"
   } | unix2dos > "$dir/\$OEM\$/install.reg"
 
   {       echo "Set WshShell = WScript.CreateObject(\"WScript.Shell\")"
@@ -2118,7 +2177,18 @@ prepareXP() {
           echo ""
   } | unix2dos > "$dir/\$OEM\$/cmdlines.txt"
 
-  rm -rf "$drivers"
+  [ ! -d "$folder" ] && return 0
+
+  msg="Adding OEM folder to image..."
+  info "$msg" && html "$msg"
+
+  local dest="$dir/\$OEM\$/\$1/"
+  mkdir -p "$dest"
+
+  if ! cp -r "$folder" "$dest"; then
+    error "Failed to copy OEM folder!" && return 1
+  fi
+
   return 0
 }
 

+ 1 - 1
src/entry.sh

@@ -35,4 +35,4 @@ cat "$QEMU_TERM" 2> /dev/null | tee "$QEMU_PTY" &
 wait $! || :
 
 sleep 1 & wait $!
-finish 0
+[ ! -f "$QEMU_END" ] && finish 0

+ 276 - 111
src/install.sh

@@ -119,6 +119,7 @@ finishInstall() {
   fi
 
   rm -f "$STORAGE/windows.old"
+  rm -f "$STORAGE/windows.type"
   rm -f "$STORAGE/windows.base"
   rm -f "$STORAGE/windows.boot"
   rm -f "$STORAGE/windows.mode"
@@ -149,21 +150,30 @@ finishInstall() {
       # Enable secure boot on multi-socket systems to workaround freeze
       if [ -n "$SOCKETS" ] && [[ "$SOCKETS" != "1" ]]; then
         BOOT_MODE="windows_secure"
-        echo "$BOOT_MODE" > "$STORAGE/windows.mode"    
+        echo "$BOOT_MODE" > "$STORAGE/windows.mode"
       fi
     fi
   fi
 
+  if [ -n "${DISK_TYPE:-}" ] && [[ "${DISK_TYPE:-}" != "scsi" ]]; then
+    echo "$DISK_TYPE" > "$STORAGE/windows.type"
+  fi
+
   rm -rf "$TMP"
   return 0
 }
 
 abortInstall() {
 
-  local iso="$1"
+  local dir="$1"
+  local iso="$2"
 
   [[ "${iso,,}" == *".esd" ]] && exit 60
 
+  if [ ! -d "$dir/EFI" ]; then
+    [[ "${PLATFORM,,}" == "x64" ]] && BOOT_MODE="windows_legacy"
+  fi
+
   if [ -n "$CUSTOM" ]; then
     BOOT="$iso"
     REMOVE="N"
@@ -468,6 +478,7 @@ detectLanguage() {
 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"
@@ -478,11 +489,81 @@ setXML() {
   return 0
 }
 
+detectLegacy() {
+
+  local dir="$1"
+  local find find2 desc
+
+  find=$(find "$dir" -maxdepth 1 -type d -iname win95 | head -n 1)
+
+  if [ -n "$find" ]; then
+    DETECTED="win95"
+    desc=$(printEdition "$DETECTED" "Windows 95")
+    info "Detected: $desc" && return 0
+  fi
+
+  find=$(find "$dir" -maxdepth 1 -type d -iname win98 | head -n 1)
+
+  if [ -n "$find" ]; then
+    DETECTED="win98"
+    desc=$(printEdition "$DETECTED" "Windows 98")
+    info "Detected: $desc" && return 0
+  fi
+
+  find=$(find "$dir" -maxdepth 1 -type d -iname win9x | head -n 1)
+
+  if [ -n "$find" ]; then
+    DETECTED="win9x"
+    desc=$(printEdition "$DETECTED" "Windows ME")
+    info "Detected: $desc" && return 0
+  fi
+
+  find=$(find "$dir" -maxdepth 1 -type d -iname win51 | head -n 1)
+  find2=$(find "$dir" -maxdepth 1 -type f -iname setupxp.htm | head -n 1)
+
+  if [ -n "$find" ] || [ -n "$find2" ] || [ -f "$dir/WIN51AP" ] || [ -f "$dir/WIN51IC" ]; then
+    [ -d "$dir/AMD64" ] && DETECTED="winxpx64" || DETECTED="winxpx86"
+    desc=$(printEdition "$DETECTED" "Windows XP")
+    info "Detected: $desc" && return 0
+  fi
+
+  if [ -f "$dir/CDROM_NT.5" ]; then
+    DETECTED="win2kx86"
+    desc=$(printEdition "$DETECTED" "Windows 2000")
+    info "Detected: $desc" && return 0
+  fi
+
+  if [ -f "$dir/WIN51AA" ] || [ -f "$dir/WIN51AD" ] || [ -f "$dir/WIN51AS" ] || [ -f "$dir/WIN51MA" ] || [ -f "$dir/WIN51MD" ]; then
+    desc="Windows Server 2003"
+    info "Detected: $desc" && error "$desc is not supported yet!" && exit 54
+  fi
+
+  if [ -f "$dir/WIN51IA" ] || [ -f "$dir/WIN51IB" ] || [ -f "$dir/WIN51ID" ] || [ -f "$dir/WIN51IL" ] || [ -f "$dir/WIN51IS" ]; then
+    desc="Windows Server 2003"
+    info "Detected: $desc" && error "$desc is not supported yet!" && exit 54
+  fi
+
+  return 1
+}
+
+skipVersion() {
+
+  local version="$1"
+
+  case "${version,,}" in
+    "win2k"* | "winxp"* | "win9"* )
+      return 0
+      ;;
+  esac
+
+  return 1
+}
+
 detectImage() {
 
   local dir="$1"
   local version="$2"
-  local desc msg language
+  local desc msg find language
 
   XML=""
 
@@ -492,7 +573,7 @@ detectImage() {
 
   if [ -n "$DETECTED" ]; then
 
-    [[ "${DETECTED,,}" == "winxp"* ]] && return 0
+    skipVersion "${DETECTED,,}" && return 0
 
     if ! setXML "" && [[ "$MANUAL" != [Yy1]* ]]; then
       MANUAL="Y"
@@ -504,31 +585,23 @@ detectImage() {
   fi
 
   info "Detecting version from ISO image..."
+  detectLegacy "$dir" && return 0
 
-  if [ -f "$dir/WIN51" ] || [ -f "$dir/SETUPXP.HTM" ]; then
-    [ -d "$dir/AMD64" ] && DETECTED="winxpx64" || DETECTED="winxpx86"
-    desc=$(printEdition "$DETECTED" "Windows XP")
-    info "Detected: $desc"
-    return 0
-  fi
-
-  local src loc info
+  local src wim info
   src=$(find "$dir" -maxdepth 1 -type d -iname sources | head -n 1)
 
   if [ ! -d "$src" ]; then
-    [[ "${PLATFORM,,}" == "x64" ]] && BOOT_MODE="windows_legacy"
     warn "failed to locate 'sources' folder in ISO image, $FB" && return 1
   fi
 
-  loc=$(find "$src" -maxdepth 1 -type f -iname install.wim | head -n 1)
-  [ ! -f "$loc" ] && loc=$(find "$src" -maxdepth 1 -type f -iname install.esd | head -n 1)
+  wim=$(find "$src" -maxdepth 1 -type f -iname install.wim | head -n 1)
+  [ ! -f "$wim" ] && wim=$(find "$src" -maxdepth 1 -type f -iname install.esd | head -n 1)
 
-  if [ ! -f "$loc" ]; then
-    [[ "${PLATFORM,,}" == "x64" ]] && BOOT_MODE="windows_legacy"
+  if [ ! -f "$wim" ]; then
     warn "failed to locate 'install.wim' or 'install.esd' in ISO image, $FB" && return 1
   fi
 
-  info=$(wimlib-imagex info -xml "$loc" | tr -d '\000')
+  info=$(wimlib-imagex info -xml "$wim" | tr -d '\000')
   ! checkPlatform "$info" && exit 67
 
   DETECTED=$(detectVersion "$info")
@@ -575,31 +648,44 @@ prepareImage() {
   local missing
 
   case "${DETECTED,,}" in
+    "win9"* | "win2k"* )
+      MACHINE="pc-i440fx-2.4" ;;
     "winxp"* | "winvistax86"* |  "win7x86"* )
-      MACHINE="pc-q35-2.10"
-      ;;
+      MACHINE="pc-q35-2.10" ;;
   esac
 
   case "${DETECTED,,}" in
-    "winxp"* )
-      BOOT_MODE="windows_legacy"
-      prepareXP "$iso" "$dir" && return 0
-      error "Failed to prepare Windows XP ISO!" && return 1
-      ;;
+    "win9"* | "winxp"* | "win2k"* )
+      HV="N"
+      BOOT_MODE="windows_legacy" ;;
     "winvista"* | "win7"* | "win2008"* )
       BOOT_MODE="windows_legacy" ;;
   esac
 
+  case "${DETECTED,,}" in
+    "winxp"* )
+      DISK_TYPE="blk"
+      prepareXP "$iso" "$dir" && return 0
+      error "Failed to prepare Windows XP ISO!" && return 1 ;;
+    "win9"* )
+      DISK_TYPE="auto"
+      prepare9x "$iso" "$dir" && return 0
+      error "Failed to prepare Windows 9x ISO!" && return 1 ;;
+    "win2k"* )
+      DISK_TYPE="auto"
+      prepare2k "$iso" "$dir" && return 0
+      error "Failed to prepare Windows 2000 ISO!" && return 1 ;;
+  esac
+
   if [[ "${BOOT_MODE,,}" != "windows_legacy" ]]; then
 
     [ -f "$dir/$ETFS" ] && [ -f "$dir/$EFISYS" ] && return 0
 
     missing=$(basename "$dir/$EFISYS")
     [ ! -f "$dir/$ETFS" ] && missing=$(basename "$dir/$ETFS")
-    warn "failed to locate file '${missing,,}' in ISO image!"
 
-    [[ "${PLATFORM,,}" == "arm64" ]] && return 1
-    BOOT_MODE="windows_legacy"
+    error "failed to locate file '${missing,,}' in ISO image!"
+    return 1
   fi
 
   prepareLegacy "$iso" "$dir" && return 0
@@ -659,6 +745,118 @@ updateXML() {
   return 0
 }
 
+addDriver() {
+
+  local id="$1"
+  local path="$2"
+  local target="$3"
+  local driver="$4"
+  local folder=""
+
+  case "${id,,}" in
+    "win7x86"* ) folder="w7/x86" ;;
+    "win7x64"* ) folder="w7/amd64" ;;
+    "win81x64"* ) folder="w10/amd64" ;;
+    "win10x64"* ) folder="w10/amd64" ;;
+    "win11x64"* ) folder="w11/amd64" ;;
+    "win2022"* ) folder="2k22/amd64" ;;
+    "win2019"* ) folder="2k19/amd64" ;;
+    "win2016"* ) folder="2k16/amd64" ;;
+    "win2012"* ) folder="2k16/amd64" ;;
+    "win2008"* ) folder="2k8R2/amd64" ;;
+    "win10arm64"* ) folder="w10/ARM64" ;;
+    "win11arm64"* ) folder="w11/ARM64" ;;
+    "winvistax86"* ) folder="2k8/x86" ;;
+    "winvistax64"* ) folder="2k8/amd64" ;;
+  esac
+
+  if [ -z "$folder" ]; then
+    warn "no \"$driver\" driver found for \"$DETECTED\" !" && return 0
+  fi
+
+  [ ! -d "$path/$driver/$folder" ] && return 0
+
+  if [[ "${id,,}" == "winvista"* ]]; then
+    [[ "${driver,,}" == "viorng" ]] && return 0
+  fi
+
+  local dest="$path/$target/$driver"
+  mv "$path/$driver/$folder" "$dest"
+
+  return 0
+}
+
+addDrivers() {
+
+  local file="$1"
+  local index="$2"
+  local version="$3"
+
+  local msg="Adding drivers to image..."
+  info "$msg" && html "$msg"
+
+  local drivers="$TMP/drivers"
+  mkdir -p "$drivers"
+
+  if ! tar -xf /drivers.txz -C "$drivers" --warning=no-timestamp; then
+    error "Failed to extract driver!" && return 1
+  fi
+
+  local target="\$WinPEDriver\$"
+  local dest="$drivers/$target"
+  mkdir -p "$dest"
+
+  wimlib-imagex update "$file" "$index" --command "delete --force --recursive /$target" >/dev/null || true
+
+  addDriver "$version" "$drivers" "$target" "qxl"
+  addDriver "$version" "$drivers" "$target" "viofs"
+  addDriver "$version" "$drivers" "$target" "sriov"
+  addDriver "$version" "$drivers" "$target" "smbus"
+  addDriver "$version" "$drivers" "$target" "qxldod"
+  addDriver "$version" "$drivers" "$target" "viorng"
+  addDriver "$version" "$drivers" "$target" "viostor"
+  addDriver "$version" "$drivers" "$target" "NetKVM"
+  addDriver "$version" "$drivers" "$target" "Balloon"
+  addDriver "$version" "$drivers" "$target" "vioscsi"
+  addDriver "$version" "$drivers" "$target" "pvpanic"
+  addDriver "$version" "$drivers" "$target" "vioinput"
+  addDriver "$version" "$drivers" "$target" "viogpudo"
+  addDriver "$version" "$drivers" "$target" "vioserial"
+  addDriver "$version" "$drivers" "$target" "qemupciserial"
+
+  if ! wimlib-imagex update "$file" "$index" --command "add $dest /$target" >/dev/null; then
+    return 1
+  fi
+
+  rm -rf "$drivers"
+  return 0
+}
+
+addFolder() {
+
+  local src="$1"
+  local folder="/oem"
+
+  [ ! -d "$folder" ] && folder="/OEM"
+  [ ! -d "$folder" ] && folder="$STORAGE/oem"
+  [ ! -d "$folder" ] && folder="$STORAGE/OEM"
+  [ ! -d "$folder" ] && return 0
+
+  local msg="Adding OEM folder to image..."
+  info "$msg" && html "$msg"
+
+  local dest="$src/\$OEM\$/\$1/"
+  mkdir -p "$dest"
+
+  ! cp -r "$folder" "$dest" && return 1
+
+  local file
+  file=$(find "$dest" -maxdepth 1 -type f -iname install.bat | head -n 1)
+  [ -f "$file" ] && unix2dos -q "$file"
+
+  return 0
+}
+
 updateImage() {
 
   local dir="$1"
@@ -667,9 +865,9 @@ updateImage() {
   local file="autounattend.xml"
   local org="${file//.xml/.org}"
   local dat="${file//.xml/.dat}"
-  local desc path src loc xml index result
+  local desc path src wim xml index result
 
-  [[ "${DETECTED,,}" == "winxp"* ]] && return 0
+  skipVersion "${DETECTED,,}" && return 0
 
   if [ ! -s "$asset" ] || [ ! -f "$asset" ]; then
     asset=""
@@ -682,29 +880,35 @@ updateImage() {
   src=$(find "$dir" -maxdepth 1 -type d -iname sources | head -n 1)
 
   if [ ! -d "$src" ]; then
-    [[ "${PLATFORM,,}" == "x64" ]] && BOOT_MODE="windows_legacy"
-    warn "failed to locate 'sources' folder in ISO image, $FB" && return 1
+    error "failed to locate 'sources' folder in ISO image, $FB" && return 1
   fi
 
-  loc=$(find "$src" -maxdepth 1 -type f -iname boot.wim | head -n 1)
-  [ ! -f "$loc" ] && loc=$(find "$src" -maxdepth 1 -type f -iname boot.esd | head -n 1)
+  wim=$(find "$src" -maxdepth 1 -type f -iname boot.wim | head -n 1)
+  [ ! -f "$wim" ] && wim=$(find "$src" -maxdepth 1 -type f -iname boot.esd | head -n 1)
 
-  if [ ! -f "$loc" ]; then
-    [[ "${PLATFORM,,}" == "x64" ]] && BOOT_MODE="windows_legacy"
-    warn "failed to locate 'boot.wim' or 'boot.esd' in ISO image, $FB" && return 1
+  if [ ! -f "$wim" ]; then
+    error "failed to locate 'boot.wim' or 'boot.esd' in ISO image, $FB" && return 1
   fi
 
   index="1"
-  result=$(wimlib-imagex info -xml "$loc" | tr -d '\000')
+  result=$(wimlib-imagex info -xml "$wim" | tr -d '\000')
 
   if [[ "${result^^}" == *"<IMAGE INDEX=\"2\">"* ]]; then
     index="2"
   fi
 
-  if wimlib-imagex extract "$loc" "$index" "/$file" "--dest-dir=$TMP" >/dev/null 2>&1; then
-    if ! wimlib-imagex extract "$loc" "$index" "/$dat" "--dest-dir=$TMP" >/dev/null 2>&1; then
-      if ! wimlib-imagex extract "$loc" "$index" "/$org" "--dest-dir=$TMP" >/dev/null 2>&1; then
-        if ! wimlib-imagex update "$loc" "$index" --command "rename /$file /$org" > /dev/null; then
+  if ! addDrivers "$wim" "$index" "$DETECTED"; then
+    error "Failed to add drivers to image!" && return 1
+  fi
+
+  if ! addFolder "$src"; then
+    error "Failed to add OEM folder to image!" && return 1
+  fi
+
+  if wimlib-imagex extract "$wim" "$index" "/$file" "--dest-dir=$TMP" >/dev/null 2>&1; then
+    if ! wimlib-imagex extract "$wim" "$index" "/$dat" "--dest-dir=$TMP" >/dev/null 2>&1; then
+      if ! wimlib-imagex extract "$wim" "$index" "/$org" "--dest-dir=$TMP" >/dev/null 2>&1; then
+        if ! wimlib-imagex update "$wim" "$index" --command "rename /$file /$org" > /dev/null; then
           warn "failed to backup original answer file ($file)."
         fi
       fi
@@ -723,11 +927,11 @@ updateImage() {
     cp "$asset" "$answer"
     updateXML "$answer" "$language"
 
-    if ! wimlib-imagex update "$loc" "$index" --command "add $answer /$file" > /dev/null; then
+    if ! wimlib-imagex update "$wim" "$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 $answer /$dat" > /dev/null || true
+      wimlib-imagex update "$wim" "$index" --command "add $answer /$dat" > /dev/null || true
     fi
 
     rm -f "$answer"
@@ -736,10 +940,10 @@ updateImage() {
 
   if [[ "$MANUAL" == [Yy1]* ]]; then
 
-    wimlib-imagex update "$loc" "$index" --command "delete --force /$file" > /dev/null || true
+    wimlib-imagex update "$wim" "$index" --command "delete --force /$file" > /dev/null || true
 
-    if wimlib-imagex extract "$loc" "$index" "/$org" "--dest-dir=$TMP" >/dev/null 2>&1; then
-      if ! wimlib-imagex update "$loc" "$index" --command "add $TMP/$org /$file" > /dev/null; then
+    if wimlib-imagex extract "$wim" "$index" "/$org" "--dest-dir=$TMP" >/dev/null 2>&1; then
+      if ! wimlib-imagex update "$wim" "$index" --command "add $TMP/$org /$file" > /dev/null; then
         warn "failed to restore original answer file ($org)."
       fi
     fi
@@ -763,7 +967,7 @@ updateImage() {
   return 0
 }
 
-removeDownload() {
+removeImage() {
 
   local iso="$1"
 
@@ -774,43 +978,10 @@ removeDownload() {
   return 0
 }
 
-copyOEM() {
-
-  local dir="$1"
-  local folder="/oem"
-  local src dest file
-
-  [ ! -d "$folder" ] && folder="/OEM"
-  [ ! -d "$folder" ] && folder="$STORAGE/oem"
-  [ ! -d "$folder" ] && folder="$STORAGE/OEM"
-  [ ! -d "$folder" ] && return 0
-
-  local msg="Copying OEM folder to image..."
-  info "$msg" && html "$msg"
-
-  src=$(find "$dir" -maxdepth 1 -type d -iname sources | head -n 1)
-
-  if [ ! -d "$src" ]; then
-    error "failed to locate 'sources' folder in ISO image!" && return 1
-  fi
-
-  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
-}
-
 buildImage() {
 
   local dir="$1"
-  local failed="N"
+  local failed=""
   local cat="BOOT.CAT"
   local log="/run/shm/iso.log"
   local base size size_gb space space_gb desc
@@ -841,31 +1012,25 @@ buildImage() {
 
   if [[ "${BOOT_MODE,,}" != "windows_legacy" ]]; then
 
-    if ! genisoimage -o "$out" -b "$ETFS" -no-emul-boot -c "$cat" -iso-level 4 -J -l -D -N -joliet-long -relaxed-filenames -V "${LABEL::30}" \
-                     -udf -boot-info-table -eltorito-alt-boot -eltorito-boot "$EFISYS" -no-emul-boot -allow-limited-size -quiet "$dir" 2> "$log"; then
-      failed="Y"
-    fi
+    ! genisoimage -o "$out" -b "$ETFS" -no-emul-boot -c "$cat" -iso-level 4 -J -l -D -N -joliet-long -relaxed-filenames -V "${LABEL::30}" \
+                  -udf -boot-info-table -eltorito-alt-boot -eltorito-boot "$EFISYS" -no-emul-boot -allow-limited-size -quiet "$dir" 2> "$log" && failed="y"
 
   else
 
-    if [[ "${DETECTED,,}" != "winxp"* ]]; then
-
-      if ! genisoimage -o "$out" -b "$ETFS" -no-emul-boot -c "$cat" -iso-level 2 -J -l -D -N -joliet-long -relaxed-filenames -V "${LABEL::30}" \
-                       -udf -allow-limited-size -quiet "$dir" 2> "$log"; then
-        failed="Y"
-      fi
-
-    else
-
-      if ! genisoimage -o "$out" -b "$ETFS" -no-emul-boot -boot-load-seg 1984 -boot-load-size 4 -c "$cat" -iso-level 2 -J -l -D -N -joliet-long \
-                       -relaxed-filenames -V "${LABEL::30}" -quiet "$dir" 2> "$log"; then
-        failed="Y"
-      fi
+    case "${DETECTED,,}" in
+      "win2k"* | "winxp"* )
+        ! genisoimage -o "$out" -b "$ETFS" -no-emul-boot -boot-load-seg 1984 -boot-load-size 4 -c "$cat" -iso-level 2 -J -l -D -N -joliet-long \
+                      -relaxed-filenames -V "${LABEL::30}" -quiet "$dir" 2> "$log" && failed="y" ;;
+      "win9"* )
+        ! genisoimage -o "$out" -b "$ETFS" -J -r -V "${LABEL::30}" -quiet "$dir" 2> "$log" && failed="y" ;;
+      * )
+        ! genisoimage -o "$out" -b "$ETFS" -no-emul-boot -c "$cat" -iso-level 2 -J -l -D -N -joliet-long -relaxed-filenames -V "${LABEL::30}" \
+                      -udf -allow-limited-size -quiet "$dir" 2> "$log" && failed="y" ;;
+    esac
 
-    fi
   fi
 
-  if [[ "$failed" != "N" ]]; then
+  if [ -n "$failed" ]; then
     [ -s "$log" ] && echo "$(<"$log")"
     error "Failed to build image!" && return 1
   fi
@@ -886,6 +1051,10 @@ bootWindows() {
 
   [[ "${PLATFORM,,}" == "arm64" ]] && VGA="virtio-gpu"
 
+  if [ -s "$STORAGE/windows.type" ] && [ -f "$STORAGE/windows.type" ]; then
+    DISK_TYPE=$(<"$STORAGE/windows.type")
+  fi
+
   if [ -s "$STORAGE/windows.mode" ] && [ -f "$STORAGE/windows.mode" ]; then
     BOOT_MODE=$(<"$STORAGE/windows.mode")
     if [ -s "$STORAGE/windows.old" ] && [ -f "$STORAGE/windows.old" ]; then
@@ -955,28 +1124,24 @@ if ! extractImage "$ISO" "$DIR" "$VERSION"; then
 fi
 
 if ! detectImage "$DIR" "$VERSION"; then
-  abortInstall "$ISO" && return 0
+  abortInstall "$DIR" "$ISO" && return 0
   exit 60
 fi
 
 if ! prepareImage "$ISO" "$DIR"; then
-  abortInstall "$ISO" && return 0
-  exit 60
+  abortInstall "$DIR" "$ISO" && return 0
+  exit 66
 fi
 
 if ! updateImage "$DIR" "$XML" "$LANGUAGE"; then
-  abortInstall "$ISO" && return 0
-  exit 60
+  abortInstall "$DIR" "$ISO" && return 0
+  exit 63
 fi
 
-if ! removeDownload "$ISO"; then
+if ! removeImage "$ISO"; then
   exit 64
 fi
 
-if ! copyOEM "$DIR"; then
-  exit 63
-fi
-
 if ! buildImage "$DIR"; then
   exit 65
 fi

+ 120 - 65
src/mido.sh

@@ -6,12 +6,18 @@ handle_curl_error() {
   local error_code="$1"
 
   case "$error_code" in
+    1) error "Unsupported protocol!" ;;
+    2) error "Failed to initialize curl!" ;;
+    3) error "The URL format is malformed!" ;;
+    5) error "Failed to resolve address of proxy host!" ;;
     6) error "Failed to resolve Microsoft servers! Is there an Internet connection?" ;;
     7) error "Failed to contact Microsoft servers! Is there an Internet connection or is the server down?" ;;
     8) error "Microsoft servers returned a malformed HTTP response!" ;;
+    16) error "A problem was detected in the HTTP2 framing layer!" ;;
     22) error "Microsoft servers returned a failing HTTP status code!" ;;
     23) error "Failed at writing Windows media to disk! Out of disk space or permission error?" ;;
-    26) error "Ran out of memory during download!" ;;
+    26) error "Failed to read Windows media from disk!" ;;
+    27) error "Ran out of memory during download!" ;;
     28) error "Connection timed out to Microsoft server!" ;;
     35) error "SSL connection error from Microsoft server!" ;;
     36) error "Failed to continue earlier download!" ;;
@@ -32,22 +38,34 @@ handle_curl_error() {
         # https://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
         INT) error "Curl was interrupted!" ;;
         # There could be other signals but these are most common
-        SEGV | ABRT ) error "Curl crashed! Failed exploitation attempt? Please report any core dumps to curl developers." ;;
-        *) error "Curl terminated due to a fatal signal!" ;;
+        SEGV | ABRT ) error "Curl crashed! Please report any core dumps to curl developers." ;;
+        *) error "Curl terminated due to fatal signal $error_code !" ;;
       esac
   esac
 
   return 1
 }
 
+get_agent() {
+
+  local user_agent
+
+  # Determine approximate latest Firefox release
+  browser_version="$((124 + ($(date +%s) - 1710892800) / 2419200))"
+  echo "Mozilla/5.0 (X11; Linux x86_64; rv:${browser_version}.0) Gecko/20100101 Firefox/${browser_version}.0"
+
+  return 0
+}
+
 download_windows() {
 
   local id="$1"
   local lang="$2"
+  local desc="$3"
   local sku_id=""
   local language=""
   local session_id=""
-  local browser_version=""
+  local user_agent=""
   local windows_version=""
   local iso_download_link=""
   local product_edition_id=""
@@ -56,12 +74,13 @@ download_windows() {
   local language_skuid_table_html=""
 
   case "${id,,}" in
-    "win11${PLATFORM,,}" ) windows_version="11" ;;
-    "win10${PLATFORM,,}" ) windows_version="10" ;;
-    "win81${PLATFORM,,}" ) windows_version="8" ;;
+    "win11x64" ) windows_version="11" ;;
+    "win10x64" ) windows_version="10" ;;
+    "win81x64" ) windows_version="8" ;;
     * ) error "Invalid VERSION specified, value \"$id\" is not recognized!" && return 1 ;;
   esac
 
+  user_agent=$(get_agent)
   language=$(getLanguage "$lang" "name")
 
   local url="https://www.microsoft.com/en-us/software-download/windows$windows_version"
@@ -69,12 +88,8 @@ download_windows() {
     8 | 10) url="${url}ISO";;
   esac
 
-  # Determine approximate latest Firefox release
-  browser_version="$((124 + ($(date +%s) - 1710892800) / 2419200))"
-  local user_agent="Mozilla/5.0 (X11; Linux x86_64; rv:${browser_version}.0) Gecko/20100101 Firefox/${browser_version}.0"
-
   # uuidgen: For MacOS (installed by default) and other systems (e.g. with no /proc) that don't have a kernel interface for generating random UUIDs
-  session_id="$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random)"
+  session_id=$(cat /proc/sys/kernel/random/uuid 2> /dev/null || uuidgen --random)
 
   # Get product edition ID for latest release of given Windows version
   # Product edition ID: This specifies both the Windows release (e.g. 22H2) and edition ("multi-edition" is default, either Home/Pro/Edu/etc., we select "Pro" in the answer files) in one number
@@ -82,7 +97,7 @@ download_windows() {
   # Also, keeping a "$WindowsVersions" array like Fido does would be way too much of a maintenance burden
   # Remove "Accept" header that curl sends by default
   [[ "$DEBUG" == [Yy1]* ]] && echo " - Parsing download page: ${url}"
-  iso_download_page_html="$(curl --silent --max-time 30 --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
+  iso_download_page_html=$(curl --silent --max-time 30 --user-agent "$user_agent" --header "Accept:" --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url") || {
     handle_curl_error $?
     return $?
   }
@@ -90,7 +105,7 @@ download_windows() {
   [[ "$DEBUG" == [Yy1]* ]] && echo -n "Getting Product edition ID: "
   # tr: Filter for only numerics to prevent HTTP parameter injection
   # head -c was recently added to POSIX: https://austingroupbugs.net/view.php?id=407
-  product_edition_id="$(echo "$iso_download_page_html" | grep -Eo '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)"
+  product_edition_id=$(echo "$iso_download_page_html" | grep -Eo '<option value="[0-9]+">Windows' | cut -d '"' -f 2 | head -n 1 | tr -cd '0-9' | head -c 16)
   [[ "$DEBUG" == [Yy1]* ]] && echo "$product_edition_id"
 
   [[ "$DEBUG" == [Yy1]* ]] && echo "Permit Session ID: $session_id"
@@ -110,17 +125,17 @@ download_windows() {
   # SKU ID: This specifies the language of the ISO. We always use "English (United States)", however, the SKU for this changes with each Windows release
   # We must make this request so our next one will be allowed
   # --data "" is required otherwise no "Content-Length" header will be sent causing HTTP response "411 Length Required"
-  language_skuid_table_html="$(curl --silent --max-time 30 --request POST --user-agent "$user_agent" --data "" --header "Accept:" --max-filesize 10K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=a8f8f489-4c7f-463a-9ca6-5cff94d8d041&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=getskuinformationbyproductedition&sessionId=$session_id&productEditionId=$product_edition_id&sdVersion=2")" || {
+  language_skuid_table_html=$(curl --silent --max-time 30 --request POST --user-agent "$user_agent" --data "" --header "Accept:" --max-filesize 10K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=a8f8f489-4c7f-463a-9ca6-5cff94d8d041&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=getskuinformationbyproductedition&sessionId=$session_id&productEditionId=$product_edition_id&sdVersion=2") || {
     handle_curl_error $?
     return $?
   }
 
   # tr: Filter for only alphanumerics or "-" to prevent HTTP parameter injection
-  sku_id="$(echo "$language_skuid_table_html" | grep -m 1 ">${language}<" | sed 's/&quot;//g' | cut -d ',' -f 1  | cut -d ':' -f 2 | tr -cd '[:alnum:]-' | head -c 16)"
+  sku_id=$(echo "$language_skuid_table_html" | grep -m 1 ">${language}<" | sed 's/&quot;//g' | cut -d ',' -f 1  | cut -d ':' -f 2 | tr -cd '[:alnum:]-' | head -c 16)
 
   if [ -z "$sku_id" ]; then
     language=$(getLanguage "$lang" "desc")
-    error "No download for the $language language available!"
+    error "No download in the $language language available for $desc!"
     return 1
   fi
 
@@ -130,7 +145,7 @@ download_windows() {
   # Get ISO download link
   # If any request is going to be blocked by Microsoft it's always this last one (the previous requests always seem to succeed)
   # --referer: Required by Microsoft servers to allow request
-  iso_download_link_html="$(curl --silent --max-time 30 --request POST --user-agent "$user_agent" --data "" --referer "$url" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=6e2a1789-ef16-4f27-a296-74ef7ef5d96b&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=GetProductDownloadLinksBySku&sessionId=$session_id&skuId=$sku_id&language=English&sdVersion=2")"
+  iso_download_link_html=$(curl --silent --max-time 30 --request POST --user-agent "$user_agent" --data "" --referer "$url" --header "Accept:" --max-filesize 100K --fail --proto =https --tlsv1.2 --http1.1 -- "https://www.microsoft.com/en-US/api/controls/contentinclude/html?pageId=6e2a1789-ef16-4f27-a296-74ef7ef5d96b&host=www.microsoft.com&segments=software-download,$url_segment_parameter&query=&action=GetProductDownloadLinksBySku&sessionId=$session_id&skuId=$sku_id&language=English&sdVersion=2")
 
   if ! [ "$iso_download_link_html" ]; then
     # This should only happen if there's been some change to how this API works
@@ -146,7 +161,7 @@ download_windows() {
   # Filter for 64-bit ISO download URL
   # sed: HTML decode "&" character
   # tr: Filter for only alphanumerics or punctuation
-  iso_download_link="$(echo "$iso_download_link_html" | grep -o "https://software.download.prss.microsoft.com.*IsoX64" | cut -d '"' -f 1 | sed 's/&amp;/\&/g' | tr -cd '[:alnum:][:punct:]')"
+  iso_download_link=$(echo "$iso_download_link_html" | grep -o "https://software.download.prss.microsoft.com.*IsoX64" | cut -d '"' -f 1 | sed 's/&amp;/\&/g' | tr -cd '[:alnum:][:punct:]')
 
   if ! [ "$iso_download_link" ]; then
     # This should only happen if there's been some change to the download endpoint web address
@@ -162,37 +177,47 @@ download_windows_eval() {
 
   local id="$1"
   local lang="$2"
+  local desc="$3"
+  local filter=""
   local culture=""
   local language=""
-  local windows_version=""
+  local user_agent=""
   local enterprise_type=""
+  local windows_version=""
 
   case "${id,,}" in
     "win11${PLATFORM,,}-enterprise-eval" )
-      windows_version="windows-11-enterprise"
-      enterprise_type="enterprise" ;;
+      enterprise_type="enterprise"
+      windows_version="windows-11-enterprise" ;;
+    "win11${PLATFORM,,}-enterprise-iot-eval" )
+      enterprise_type="iot"
+      windows_version="windows-11-iot-enterprise-ltsc" ;;
+    "win11${PLATFORM,,}-enterprise-ltsc-eval" )
+      enterprise_type="iot"
+      windows_version="windows-11-iot-enterprise-ltsc" ;;
     "win10${PLATFORM,,}-enterprise-eval" )
-      windows_version="windows-10-enterprise"
-      enterprise_type="enterprise" ;;
+      enterprise_type="enterprise"
+      windows_version="windows-10-enterprise" ;;
     "win10${PLATFORM,,}-enterprise-ltsc-eval" )
-      windows_version="windows-10-enterprise"
-      enterprise_type="ltsc" ;;
+      enterprise_type="ltsc"
+      windows_version="windows-10-enterprise" ;;
     "win2022-eval" )
-      windows_version="windows-server-2022"
-      enterprise_type="server" ;;
+      enterprise_type="server"
+      windows_version="windows-server-2022" ;;
     "win2019-eval" )
-      windows_version="windows-server-2019"
-      enterprise_type="server" ;;
+      enterprise_type="server"
+      windows_version="windows-server-2019" ;;
     "win2016-eval" )
-      windows_version="windows-server-2016"
-      enterprise_type="server" ;;
+      enterprise_type="server"
+      windows_version="windows-server-2016" ;;
     "win2012r2-eval" )
-      windows_version="windows-server-2012-r2"
-      enterprise_type="server" ;;
+      enterprise_type="server"
+      windows_version="windows-server-2012-r2" ;;
     * )
       error "Invalid VERSION specified, value \"$id\" is not recognized!" && return 1 ;;
   esac
 
+  user_agent=$(get_agent)
   culture=$(getLanguage "$lang" "culture")
 
   local country="${culture#*-}"
@@ -200,7 +225,7 @@ download_windows_eval() {
   local url="https://www.microsoft.com/en-us/evalcenter/download-$windows_version"
 
   [[ "$DEBUG" == [Yy1]* ]] && echo "Parsing download page: ${url}"
-  iso_download_page_html="$(curl --silent --max-time 30 --location --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url")" || {
+  iso_download_page_html=$(curl --silent --max-time 30 --user-agent "$user_agent" --location --max-filesize 1M --fail --proto =https --tlsv1.2 --http1.1 -- "$url") || {
     handle_curl_error $?
     return $?
   }
@@ -212,32 +237,52 @@ download_windows_eval() {
   fi
 
   [[ "$DEBUG" == [Yy1]* ]] && echo "Getting download link.."
-  iso_download_links="$(echo "$iso_download_page_html" | grep -o "https://go.microsoft.com/fwlink/p/?LinkID=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country^^}")" || {
+
+  if [[ "$enterprise_type" == "iot" ]]; then
+    filter="https://go.microsoft.com/fwlink/?linkid=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country^^}"
+  else
+    filter="https://go.microsoft.com/fwlink/p/?LinkID=[0-9]\+&clcid=0x[0-9a-z]\+&culture=${culture,,}&country=${country^^}"
+  fi
+
+  iso_download_links=$(echo "$iso_download_page_html" | grep -io "$filter") || {
     # This should only happen if there's been some change to the download endpoint web address
     if [[ "${lang,,}" == "en" ]] || [[ "${lang,,}" == "en-"* ]]; then
       error "Windows server download page gave us no download link!"
     else
       language=$(getLanguage "$lang" "desc")
-      error "No download for the $language language available!"
+      error "No download in the $language language available for $desc!"
     fi
     return 1
   }
 
-  # Limit untrusted size for input validation
-  iso_download_links="$(echo "$iso_download_links" | head -c 1024)"
-
   case "$enterprise_type" in
-    # Select x64 download link
-    "enterprise") iso_download_link=$(echo "$iso_download_links" | head -n 2 | tail -n 1) ;;
-    # Select x64 LTSC download link
-    "ltsc") iso_download_link=$(echo "$iso_download_links" | head -n 4 | tail -n 1) ;;
-    *) iso_download_link="$iso_download_links" ;;
+    "enterprise" )
+      iso_download_link=$(echo "$iso_download_links" | head -n 2 | tail -n 1)
+      ;;
+    "iot" )
+      if [[ "${PLATFORM,,}" == "x64" ]]; then
+        iso_download_link=$(echo "$iso_download_links" | head -n 1)
+      fi
+      if [[ "${PLATFORM,,}" == "arm64" ]]; then
+        iso_download_link=$(echo "$iso_download_links" | head -n 2 | tail -n 1)
+      fi
+      ;;
+    "ltsc" )
+      iso_download_link=$(echo "$iso_download_links" | head -n 4 | tail -n 1)
+      ;;
+    "server" )
+      iso_download_link=$(echo "$iso_download_links" | head -n 1)
+      ;;
+    * )
+      error "Invalid type specified, value \"$enterprise_type\" is not recognized!" && return 1 ;;
   esac
 
+  [[ "$DEBUG" == [Yy1]* ]] && echo "Found download link: $iso_download_link"
+
   # Follow redirect so proceeding log message is useful
   # This is a request we make this Fido doesn't
   # We don't need to set "--max-filesize" here because this is a HEAD request and the output is to /dev/null anyway
-  iso_download_link="$(curl --silent --max-time 30 --location --output /dev/null --silent --write-out "%{url_effective}" --head --fail --proto =https --tlsv1.2 --http1.1 -- "$iso_download_link")" || {
+  iso_download_link=$(curl --silent --max-time 30 --user-agent "$user_agent" --location --output /dev/null --silent --write-out "%{url_effective}" --head --fail --proto =https --tlsv1.2 --http1.1 -- "$iso_download_link") || {
     # This should only happen if the Microsoft servers are down
     handle_curl_error $?
     return $?
@@ -252,37 +297,46 @@ getWindows() {
   local version="$1"
   local lang="$2"
   local desc="$3"
-  local language
+
+  local language edition
+  language=$(getLanguage "$lang" "desc")
+  edition=$(printEdition "$version" "$desc")
 
   local msg="Requesting $desc from Microsoft server..."
   info "$msg" && html "$msg"
 
+  case "${version,,}" in
+    "win2008r2" | "win81${PLATFORM,,}-enterprise-eval" | "win11${PLATFORM,,}-enterprise-iot-eval" )
+      if [[ "${lang,,}" != "en" ]] && [[ "${lang,,}" != "en-"* ]]; then
+        error "No download in the $language language available for $edition!"
+        MIDO_URL="" && return 1
+      fi ;;
+  esac
+
+  case "${version,,}" in
+    "win11${PLATFORM,,}-enterprise-iot-eval" ) ;;
+    * )
+      if [[ "${PLATFORM,,}" != "x64" ]]; then
+        error "No download for the ${PLATFORM^^} platform available for $edition!"
+        MIDO_URL="" && return 1
+      fi ;;
+  esac
+
   case "${version,,}" in
     "win81${PLATFORM,,}" | "win10${PLATFORM,,}" | "win11${PLATFORM,,}" )
-      download_windows "$version" "$lang" && return 0
-      ;;
-    "win11${PLATFORM,,}-enterprise-eval" )
-      download_windows_eval "$version" "$lang" && return 0
+      download_windows "$version" "$lang" "$edition" && return 0
       ;;
-    "win10${PLATFORM,,}-enterprise-eval" | "win10${PLATFORM,,}-enterprise-ltsc-eval" )
-      download_windows_eval "$version" "$lang" && return 0
+    "win11${PLATFORM,,}-enterprise"* | "win10${PLATFORM,,}-enterprise"* )
+      download_windows_eval "$version" "$lang" "$edition" && return 0
       ;;
     "win2022-eval" | "win2019-eval" | "win2016-eval" | "win2012r2-eval" )
-      download_windows_eval "$version" "$lang" && return 0
+      download_windows_eval "$version" "$lang" "$edition" && return 0
       ;;
     "win81${PLATFORM,,}-enterprise-eval" )
-      if [[ "${lang,,}" == "en" ]] || [[ "${lang,,}" == "en-"* ]]; then
-        MIDO_URL="https://download.microsoft.com/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X64FREE_EN-US_DV9.ISO" && return 0
-      fi
-      language=$(getLanguage "$lang" "desc")
-      error "No download for the $language language available!"
+      MIDO_URL="https://download.microsoft.com/download/B/9/9/B999286E-0A47-406D-8B3D-5B5AD7373A4A/9600.17050.WINBLUE_REFRESH.140317-1640_X64FRE_ENTERPRISE_EVAL_EN-US-IR3_CENA_X64FREE_EN-US_DV9.ISO" && return 0
       ;;
     "win2008r2" )
-      if [[ "${lang,,}" == "en" ]] || [[ "${lang,,}" == "en-"* ]]; then
-        MIDO_URL="https://download.microsoft.com/download/4/1/D/41DEA7E0-B30D-4012-A1E3-F24DC03BA1BB/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso" && return 0
-      fi
-      language=$(getLanguage "$lang" "desc")
-      error "No download for the $language language available!"
+      MIDO_URL="https://download.microsoft.com/download/4/1/D/41DEA7E0-B30D-4012-A1E3-F24DC03BA1BB/7601.17514.101119-1850_x64fre_server_eval_en-us-GRMSXEVAL_EN_DVD.iso" && return 0
       ;;
     * ) error "Invalid VERSION specified, value \"$version\" is not recognized!" ;;
   esac
@@ -385,8 +439,9 @@ getESD() {
 
   size=$(stat -c%s "$dir/$eFile")
   if ((size<20)); then
+    desc=$(printEdition "$version" "$desc")
     language=$(getLanguage "$lang" "desc")
-    error "the $language language is not supported by this download method!" && return 1
+    error "No download in the $language language available for $desc!" && return 1
   fi
 
   local tag="FilePath"

+ 16 - 10
src/power.sh

@@ -29,7 +29,12 @@ boot() {
 
   if [ -s "$QEMU_PTY" ]; then
     if [ "$(stat -c%s "$QEMU_PTY")" -gt 7 ]; then
-      if ! grep -Fq "BOOTMGR is missing" "$QEMU_PTY"; then
+      local fail=""
+      if [[ "${BOOT_MODE,,}" == "windows_legacy" ]]; then
+        grep -Fq "No bootable device." "$QEMU_PTY" && fail="y"
+        grep -Fq "BOOTMGR is missing" "$QEMU_PTY" && fail="y"
+      fi
+      if [ -z "$fail" ]; then
         info "Windows started succesfully, visit http://localhost:8006/ to view the screen..."
         return 0
       fi
@@ -37,6 +42,11 @@ boot() {
   fi
 
   error "Timeout while waiting for QEMU to boot the machine!"
+
+  local pid
+  pid=$(<"$QEMU_PID")
+  { kill -15 "$pid" || true; } 2>/dev/null
+
   return 0
 }
 
@@ -49,18 +59,14 @@ ready() {
     local last
     local bios="Booting from Hard"
     last=$(grep "^Booting.*" "$QEMU_PTY" | tail -1)
-    if [[ "${last,,}" == "${bios,,}"* ]]; then
-      if ! grep -Fq "BOOTMGR is missing" "$QEMU_PTY"; then
-        return 0
-      fi
-    fi
-    return 1
+    [[ "${last,,}" != "${bios,,}"* ]] && return 1
+    grep -Fq "No bootable device." "$QEMU_PTY" && return 1
+    grep -Fq "BOOTMGR is missing" "$QEMU_PTY" && return 1
+    return 0
   fi
 
   local line="\"Windows Boot Manager\""
-  if grep -Fq "$line" "$QEMU_PTY"; then
-    return 0
-  fi
+  grep -Fq "$line" "$QEMU_PTY" && return 0
 
   return 1
 }