Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement makeWinXPImage #13

Draft
wants to merge 28 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c8a1586
Implement makeWin2kImage
io12 Dec 17, 2023
3236b51
Stash changes
io12 Dec 31, 2023
7f6a960
win2k: switch to unattended install
io12 Dec 31, 2023
6d19d3a
win2k: fix winnt init check
io12 Dec 31, 2023
d837404
win2k: disable turbo, move answers to separate file, and set dosbox to
io12 Dec 31, 2023
1a617b4
win2k: add comments
io12 Dec 31, 2023
17b503a
win2k: add win2k-repeatability-test
io12 Dec 31, 2023
ea4d865
win2k: add unattend.doc web link to comment
io12 Dec 31, 2023
f953930
win2k: add vnc and tesseract for output logs
io12 Jan 2, 2024
6129b0d
win2k: disable rate limit and enable turbo in stage 1
io12 Jan 5, 2024
fa13e70
Start makeWinXPImage
io12 Dec 17, 2023
4327cf1
Stash changes
io12 Jan 1, 2024
ef97a06
winxp: fix win2k bootstrap
io12 Jan 2, 2024
2158295
winxp: actually installs successfully now!
io12 Jan 5, 2024
98ee0a6
winxp: remove unneeded dosbox options
io12 Jan 5, 2024
578aeb6
winxp: remove unneeeded winnt32.exe flags
io12 Jan 5, 2024
8782d4c
winxp: set number of stages
io12 Jan 5, 2024
2c9ab82
winxp: remove unneeded machine key in config
io12 Jan 5, 2024
a2b4979
winxp: remove unneeded serial disables
io12 Jan 5, 2024
fa6474e
winxp: remove unneeded parallel disables
io12 Jan 5, 2024
e69eaae
winxp: disable drive data rate limit
io12 Jan 6, 2024
7116ed7
win2k: remove dos.ver
io12 Jan 6, 2024
350b0b3
winxp: add tesseract and use smaller image
io12 Jan 6, 2024
89f23b3
winxp: add expect script
io12 Jan 6, 2024
5425c9d
winxp: remove __impure
io12 Jan 7, 2024
177ba30
winxp: don't put output in directory
io12 Jan 7, 2024
f9a0414
winxp: run: upgrade memsize and cputype
io12 Jan 7, 2024
bdf934a
winxp: enable turbo in middle part
io12 Jan 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ Each of the outputs in this flake have their own image builders and `runScript`.
- `makeWin30Image`
- `makeWfwg311Image`
- `makeWin98Image`
- `makeWin2kImage`

They can each be passed the `dosPostInstall` argument arbitrary **dos
commands** to be ran after Windows has been installed, for example here's how
Expand Down
13 changes: 13 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@
makeWin30Image = pkgs.callPackage ./makeWin30Image {};
makeWfwg311Image = pkgs.callPackage ./makeWfwg311Image {};
makeWin98Image = pkgs.callPackage ./makeWin98Image {};
makeWin2kImage = pkgs.callPackage ./makeWin2kImage {};
makeWinXPImage = pkgs.callPackage ./makeWinXPImage {};
# makeSystem7Image = pkgs.callPackage ./makeSystem7Image {};
};
apps = {
Expand All @@ -83,15 +85,26 @@
type = "app";
program = config.packages.win98-image.runScript;
};
win2k = {
type = "app";
program = config.packages.win2k-image.runScript;
};
winxp = {
type = "app";
program = config.packages.winxp-image.runScript;
};
};
packages = rec {
macos-ventura-image = config.legacyPackages.makeDarwinImage {};
msdos622-image = config.legacyPackages.makeMsDos622Image {};
win30-image = config.legacyPackages.makeWin30Image {};
wfwg311-image = config.legacyPackages.makeWfwg311Image {};
win98-image = config.legacyPackages.makeWin98Image {};
win2k-image = config.legacyPackages.makeWin2kImage {};
winxp-image = config.legacyPackages.makeWinXPImage {};
#system7-image = config.legacyPackages.makeSystem7Image {};
#macos-repeatability-test = genOverridenDrvLinkFarm (macos-ventura-image.overrideAttrs { repeatabilityTest = true; }) 3;
win2k-repeatability-test = genOverridenDrvLinkFarm win2k-image 100;
win98-repeatability-test = genOverridenDrvLinkFarm win98-image 100;
wfwg311-repeatability-test = genOverridenDrvLinkFarm wfwg311-image 100;
win30-repeatability-test = genOverridenDrvLinkFarm win30-image 100;
Expand Down
43 changes: 43 additions & 0 deletions makeWin2kImage/answers.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Windows 2000's unattended installation feature is a bit tricky to figure out.
# The SUPPORT/TOOLS/SETUP.EXE in the install disk ISO crashes DOSBox-X, but
# SUPPORT/TOOLS/SREADME.DOC says that it doesn't install the relevant deployment
# tools in DEPLOY.CAB anyway. If you extract SUPPORT/TOOLS/DEPLOY.CAB with
# cabextract, there is setupmgr.exe inside that has a GUI for creating the
# answer files. It must be run in the same directory as setupmgx.dll, also
# included in DEPLOY.CAB. There is also documentation in DEPLOY.CAB in the
# deptool.chm, readme.txt, and unattend.doc files. The setupmgr.exe tool
# doesn't ask for a product key, so that has to be added to UserData.ProductID
# separately. Otherwise, during the install there will be an error asking the
# user to input it. The unattend.doc file also contains documentation of the
# different answer file options. A web version of unattend.doc is at
# https://web.archive.org/web/20040314065512/https://www.microsoft.com/technet/prodtechnol/Windows2000Pro/deploy/unattend/default.mspx

{
Data = {
AutoPartition = 1;
MsDosInitiated = "0";
UnattendedInstall = "Yes";
};
Unattended = {
UnattendMode = "FullUnattended";
OemSkipEula = "Yes";
OemPreinstall = "No";
TargetPath = "WINDOWS";
};
GuiUnattended = {
AdminPassword = "*";
AutoLogon = "Yes";
OEMSkipRegional = 1;
TimeZone = 4;
OemSkipWelcome = 1;
};
UserData = {
FullName = "user";
OrgName = "NixThePlanet";
ComputerName = "*";
ProductID = "RBDC9-VTRC8-D7972-J97JY-PRVMG";
};
RegionalSettings.LanguageGroup = 1;
Identification.JoinWorkgroup = "WORKGROUP";
Networking.InstallDefaultComponents = "Yes";
}
106 changes: 106 additions & 0 deletions makeWin2kImage/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# Fabulous.systems demonstrated that installing Windows 2000 in DOSBox-X is possible in
# https://fabulous.systems/posts/2023/07/installing-windows-2000-in-dosbox-x/
# but this package uses a different approach, installing from scratch instead of
# from Windows 98 and using an answer file for unattended installation.

{ lib, fetchurl, runCommand, p7zip, dosbox-x, x11vnc, tesseract, vncdo, xvfb-run
, writeText, writeShellScript, callPackage }:
{ dosPostInstall ? "", imageType ? "hd_1gig", answerFile ?
writeText "answers.ini" (lib.generators.toINI { } (import ./answers.nix)) }:
let
win2k-installer = fetchurl {
name = "win2k.7z";
urls = [
"https://winworldpc.com/download/413638c2-8d18-c39a-11c3-a4e284a2c3a5/from/c39ac2af-c381-c2bf-1b25-11c3a4e284a2"
"https://winworldpc.com/download/413638c2-8d18-c39a-11c3-a4e284a2c3a5/from/c3ae6ee2-8099-713d-3411-c3a6e280947e"
"https://cloudflare-ipfs.com/ipfs/QmT7rGKU4WzQxwpfZqFgGwGSCrBbBm7SejUSPgDzxAgPye/Microsoft%20Windows%202000%20Professional%20(5.00.2195).7z"
];
sha512 =
"9cb026d8eaa3933d7ca0447c7e1b05fd1504a6063b16a86d5cca4dc04ef5d598bd8ae95dac6f10671422ece192b1aff94ecd505d2cd7a15981eaa4fd691f1489";
};
dosboxConf = stage:
writeText "dosbox.conf" ''
[dosbox]
memsize = 32

[dos]
hard drive data rate limit = 0
floppy drive data rate limit = 0

[cpu]
# Turbo prevents the boot screen from progressing in stage 2
turbo = ${if stage == 2 then "off" else "on"}

[autoexec]
mount a .
if not exist a:\win2k.img imgmake win2k.img -t ${imageType}
imgmount c win2k.img -t hdd
imgmount d win2k.iso
if not exist c:\ntldr d:\i386\winnt /s:d: /u:a:answers.ini
boot -l c
'';
iso = runCommand "win2k.iso" { } ''
echo "win2k-installer src: ${win2k-installer}"
mkdir win2k
${p7zip}/bin/7z x -owin2k ${win2k-installer}
ls -lah win2k
mv win2k/*/*.iso $out
'';
tesseractScript = writeShellScript "tesseractScript" ''
export OMP_THREAD_LIMIT=1
cd $(mktemp -d)
TEXT=""
while true
do
sleep 3
${vncdo}/bin/vncdo -s 127.0.0.1::5900 capture cap.png
NEW_TEXT="$(${tesseract}/bin/tesseract cap.png stdout 2>/dev/null)"
if [ "$TEXT" != "$NEW_TEXT" ]; then
echo "$NEW_TEXT"
TEXT="$NEW_TEXT"
fi
done
'';
installedImage = runCommand "win2k.img" {
# set __impure = true; for debugging
# __impure = true;
buildInputs = [ dosbox-x xvfb-run x11vnc ];
passthru = rec {
makeRunScript = callPackage ./run.nix;
runScript = makeRunScript { };
};
} ''
echo "iso src: ${iso}"
cp --no-preserve=mode ${iso} win2k.iso
cp --no-preserve=mode ${answerFile} answers.ini
# This install is fully unattended, but a VNC server and tesseract script are still started for log output and debugging
(
while true; do
DISPLAY=:99 XAUTHORITY=/tmp/xvfb.auth x11vnc -many -shared -display :99 >/dev/null 2>&1 || true
echo RESTARTING VNC
done
) &
${tesseractScript} &
${lib.strings.concatMapStrings (stage: ''
echo STAGE ${toString stage}
xvfb-run -l -s ":99 -auth /tmp/xvfb.auth -ac -screen 0 800x600x24" \
dosbox-x -conf ${dosboxConf stage} || true
'') [ 1 2 ]}
cp win2k.img $out
'';
postInstalledImage = let
dosboxConf-postInstall = writeText "dosbox.conf" ''
[dosbox]
memsize = 32

[autoexec]
imgmount c win2k.img
${dosPostInstall}
exit
'';
in runCommand "win2k.img" { inherit (installedImage) passthru; } ''
cp --no-preserve=mode ${installedImage} ./win2k.img
SDL_VIDEODRIVER=dummy ${lib.getExe dosbox-x} -conf ${dosboxConf-postInstall}
mv win2k.img $out
'';
in if (dosPostInstall != "") then postInstalledImage else installedImage
38 changes: 38 additions & 0 deletions makeWin2kImage/run.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{ writeShellScriptBin, writeText, lib, dosbox-x, makeWin2kImage
, extraDosboxFlags ? [ ], diskImage ? makeWin2kImage { } }:
let
dosboxConf = writeText "dosbox.conf" ''
[dosbox]
memsize = 32

[sdl]
autolock = true

[autoexec]
imgmount C win2k.img
boot -l C
'';
in writeShellScriptBin "run-win2k.sh" ''
args=(
-conf ${dosboxConf}
${lib.concatStringsSep " " extraDosboxFlags}
"$@"
)

if [ ! -f win2k.img ]; then
echo "win2k.img not found, making disk image ./win2k.img"
cp --no-preserve=mode ${diskImage} ./win2k.img
fi

run_dosbox() {
${dosbox-x}/bin/dosbox-x "''${args[@]}"
}

run_dosbox

if [ $? -ne 0 ]; then
echo "Dosbox crashed. Re-running with SDL_VIDEODRIVER=x11."
SDL_VIDEODRIVER=x11 run_dosbox
fi
''

28 changes: 28 additions & 0 deletions makeWinXPImage/answers.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
Data = {
AutoPartition = 1;
MsDosInitiated = "0";
UnattendedInstall = "Yes";
};
Unattended = {
UnattendMode = "FullUnattended";
OemSkipEula = "Yes";
OemPreinstall = "No";
TargetPath = "WINDOWS";
};
GuiUnattended = {
AdminPassword = "*";
EncryptedAdminPassword = "NO";
OEMSkipRegional = 1;
TimeZone = 4;
OemSkipWelcome = 1;
};
UserData = {
ProductKey = "MRX3F-47B9T-2487J-KWKMF-RPWBY";
FullName = "user";
OrgName = "NixThePlanet";
ComputerName = "*";
};
Identification.JoinWorkgroup = "WORKGROUP";
Networking.InstallDefaultComponents = "Yes";
}
132 changes: 132 additions & 0 deletions makeWinXPImage/default.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# The installer from DOS (winnt.exe) doesn't work, so use winnt32.exe from Windows 2000

{ lib, fetchtorrent, runCommand, dosbox-x, xvfb-run, x11vnc, vncdo, tesseract
, expect, writeText, writeShellScript, writeScript, makeWin2kImage, callPackage
}:
{ dosPostInstall ? "", answerFile ?
writeText "answers.ini" (lib.generators.toINI { } (import ./answers.nix)) }:
let
win2k = makeWin2kImage { imageType = "hd_2gig"; };
winxp-installer = fetchtorrent {
url =
"https://archive.org/download/WinXPProSP3x86/WinXPProSP3x86_archive.torrent";
hash = "sha256-NDCPO4gT4rgfB76HrF/HtaRNzSfpXJUSHbqLqECvkpU=";
};
dosboxConf = stage:
writeText "dosbox.conf" ''
[dosbox]
memsize = 128

[dos]
ver = 7.0 # Need long filenames support to edit the C drive in autoexec
hard drive data rate limit = 0
floppy drive data rate limit = 0

[cpu]
cputype = ppro_slow
# Turbo breaks win2k boot and final stage
turbo = ${if builtins.elem stage [ 1 3 ] then "off" else "on"}

[autoexec]
imgmount c win2k.img
imgmount d winxp.iso
${lib.optionalString (stage == 2) ''
# After the XP install is bootstrapped, remove the old Windows 2000 files to make space for XP
deltree /y c:\WINDOWS
deltree /y "c:\Documents and Settings"
deltree /y "c:\Program Files"
''}
boot -l c
'';
tesseractScript = writeShellScript "tesseractScript" ''
export OMP_THREAD_LIMIT=1
cd $(mktemp -d)
TEXT=""
while true
do
sleep 3
${vncdo}/bin/vncdo -s 127.0.0.1::5900 capture cap.png
NEW_TEXT="$(${tesseract}/bin/tesseract cap.png stdout 2>/dev/null)"
if [ "$TEXT" != "$NEW_TEXT" ]; then
echo "$NEW_TEXT"
TEXT="$NEW_TEXT"
fi
done
'';
expectScript = let
vncdoWrapper = writeScript "vncdoWrapper" ''
sleep 3
${vncdo}/bin/vncdo --force-caps -s 127.0.0.1::5900 "$@"
'';
in writeScript "expect.sh" ''
#!${expect}/bin/expect -f
set debug 5
set timeout -1
spawn ${tesseractScript}
expect "ENTER-Install"
exec ${vncdoWrapper} key enter
# Keep running until killed so the entire build gets tesseract log output
while { 1 } { sleep 10000 }
'';
installedImage = runCommand "winxp.img" {
# set __impure = true; for debugging
# __impure = true;
buildInputs = [ dosbox-x xvfb-run x11vnc ];
passthru = rec {
makeRunScript = callPackage ./run.nix;
runScript = makeRunScript { };
};
} ''
ln -s ${winxp-installer}/*.iso winxp.iso
cp --no-preserve=mode ${win2k} win2k.img
cp --no-preserve=mode ${answerFile} answers.ini
# Copy answer file to win2k.img and add autostart script that runs the XP installer
(
SDL_VIDEODRIVER=dummy dosbox-x -conf ${
writeText "dosbox.conf" ''
[dos]
ver = 7.0 # Need long filenames support to edit the C drive in autoexec

[autoexec]
mount a .
imgmount c win2k.img
copy a:\answers.ini c:\
echo d:\i386\winnt32.exe /unattend:c:\answers.ini > "c:\Documents and Settings\All Users\Start Menu\Programs\Startup\start-xp-install.bat"
exit
''
}
)
(
while true; do
DISPLAY=:99 XAUTHORITY=/tmp/xvfb.auth x11vnc -many -shared -display :99 >/dev/null 2>&1 || true
echo RESTARTING VNC
done
) &
${expectScript} &
${lib.strings.concatMapStrings (stage: ''
echo STAGE ${toString stage}
xvfb-run -l -s ":99 -auth /tmp/xvfb.auth -ac -screen 0 800x600x24" \
dosbox-x -conf ${dosboxConf stage} || true
'') (lib.range 1 3)}
cp win2k.img $out
'';
postInstalledImage = let
dosboxConf-postInstall = writeText "dosbox.conf" ''
[cpu]
turbo = on
stop turbo on key = false

[autoexec]
imgmount c winxp.img
${dosPostInstall}
exit
'';
in runCommand "winxp.img" {
buildInputs = [ dosbox-x ];
inherit (installedImage) passthru;
} ''
cp --no-preserve=mode ${installedImage} ./winxp.img
SDL_VIDEODRIVER=dummy dosbox-x -conf ${dosboxConf-postInstall}
mv winxp.img $out
'';
in if (dosPostInstall != "") then postInstalledImage else installedImage
Loading