commit 04398840c04c06818d9bbbe1b74fb50f839ffb51 Author: Gregory Bednov Date: Wed Sep 2 11:08:59 2026 +0300 новый файл: flake.lock новый файл: flake.nix новый файл: pkgs/codesys2/default.nix новый файл: pkgs/codesys2/setup.iss новый файл: pkgs/codesys3/default.nix новый файл: pkgs/mfc42/default.nix новый файл: pkgs/mspaintxp/default.nix новый файл: pkgs/vcredist2019/default.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..76e7ff9 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1787814960, + "narHash": "sha256-PYZq1qzCJXC2zGI0mH07vrZBsw6DRBAOX0jN1pPtqOQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "c27cdad491a991b11ed731760aa2ef8db0cb0410", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..921c10a --- /dev/null +++ b/flake.nix @@ -0,0 +1,43 @@ +{ + description = "MS Paint Windows XP via Wine"; + + inputs.nixpkgs.url = "github:nixos/nixpkgs?ref=nixpkgs-unstable"; + + outputs = + { self, nixpkgs }: + let + system = "x86_64-linux"; + pkgs = nixpkgs.legacyPackages.${system}; + in + { + packages.${system} = rec { + default = mspaintxp; + + mfc42 = pkgs.callPackage ./pkgs/mfc42 { }; + + mspaintxp = pkgs.callPackage ./pkgs/mspaintxp { + inherit mfc42; + exe = ./XP_mspaintxp.exe; + }; + + codesys2 = pkgs.callPackage ./pkgs/codesys2 { + src = ./pkgs/codesys2/installed.tar.xz; + }; + + vcredist2019 = pkgs.callPackage ./pkgs/vcredist2019 { }; + + codesys3 = pkgs.callPackage ./pkgs/codesys3 { + inherit vcredist2019; + src = ./pkgs/codesys3/prefix.tar.xz; + }; + }; + + devShells.${system}.default = pkgs.mkShell { + packages = [ + pkgs.wine + pkgs.winetricks + self.packages.${system}.mspaintxp + ]; + }; + }; +} diff --git a/pkgs/codesys2/default.nix b/pkgs/codesys2/default.nix new file mode 100644 index 0000000..f060131 --- /dev/null +++ b/pkgs/codesys2/default.nix @@ -0,0 +1,47 @@ +{ + stdenv, + makeWrapper, + makeDesktopItem, + copyDesktopItems, + wine, + src, +}: + +stdenv.mkDerivation { + pname = "codesys2"; + version = "2.3.8.1"; + + inherit src; + sourceRoot = "."; + + nativeBuildInputs = [ + makeWrapper + copyDesktopItems + ]; + + desktopItems = [ + (makeDesktopItem { + name = "codesys2"; + desktopName = "CoDeSys 2"; + genericName = "PLC Programming"; + exec = "codesys2"; + categories = [ "Development" ]; + startupWMClass = "codesys.exe"; + }) + ]; + + installPhase = '' + runHook preInstall + + app=$out/share/codesys2 + install -d $app + cp -r . $app/ + + makeWrapper ${wine}/bin/wine $out/bin/codesys2 \ + --add-flags "$app/Codesys.exe" + + runHook postInstall + ''; + + meta.mainProgram = "codesys2"; +} diff --git a/pkgs/codesys2/setup.iss b/pkgs/codesys2/setup.iss new file mode 100644 index 0000000..d2cc12d --- /dev/null +++ b/pkgs/codesys2/setup.iss @@ -0,0 +1,70 @@ +[InstallShield Silent] +Version=v6.00.000 +File=Response File +[File Transfer] +OverwrittenReadOnly=NoToAll +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-DlgOrder] +Dlg0={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-MessageBox-0 +Count=8 +Dlg1={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdWelcome-0 +Dlg2={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdAskDestPath-0 +Dlg3={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdComponentTree-0 +Dlg4={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdSelectFolder-0 +Dlg5={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdShowInfoList-0 +Dlg6={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-MessageBox-1 +Dlg7={07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdFinish-0 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-MessageBox-0] +Result=1 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdWelcome-0] +Result=1 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdAskDestPath-0] +szDir=C:\Program Files\3S Software +Result=1 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdComponentTree-0] +szDir=C:\Program Files\3S Software +Ko_CoDeSys-type=string +Ko_CoDeSys-count=2 +Ko_CoDeSys-0=Ko_CoDeSys\Neu InstallDialogue Object 1 +Ko_CoDeSys-1=Ko_CoDeSys\Neu MediaRead 1 +Ko_Communication\Ko_Gateway-type=string +Ko_Communication\Ko_Gateway-count=1 +Ko_Communication\Ko_Gateway-0=Ko_Communication\Ko_Gateway\Ko_Gateway_ProgSys +Ko_Communication-type=string +Ko_Communication-count=2 +Ko_Communication-0=Ko_Communication\Ko_Gateway +Ko_Communication-1=Ko_Communication\Ko_OPC +Component-type=string +Component-count=12 +Component-0=Ko_CoDeSys +Component-1=Ko_CoDeSys Base +Component-2=Ko_LicenseManager +Component-3=Ko_SoftMotion +Component-4=Ko_CoDeSysHMI +Component-5=Ko_Communication +Component-6=Ko_InstallTarget +Component-7=Ko_Link +Component-8=Ko_PLCWinNT +Component-9=Ko_WebServer +Component-10=Ko_CoDeSysINI +Component-11=Ko_VersionInfo +Result=1 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdSelectFolder-0] +szFolder=3S Software +Result=1 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdShowInfoList-0] +Result=1 +[Application] +Name=CoDeSys for Automation Alliance +Version=2.3.000 +Company=3S-Smart Software Solutions GmbH +Lang=0009 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-MessageBox-1] +Result=1 +[{07976ABB-1EBD-4A65-A7C7-155A0DC17173}-SdFinish-0] +Result=1 +bOpt1=0 +bOpt2=0 +[{533A955C-332B-484A-8F78-82FD1E718D04}-DlgOrder] +Count=0 +[{D254075A-A1C7-4E4C-8DCE-8F4B08A8F463}-DlgOrder] +Count=0 diff --git a/pkgs/codesys3/default.nix b/pkgs/codesys3/default.nix new file mode 100644 index 0000000..05fa99d --- /dev/null +++ b/pkgs/codesys3/default.nix @@ -0,0 +1,72 @@ +{ + stdenv, + makeWrapper, + makeDesktopItem, + copyDesktopItems, + wineWowPackages, + vcredist2019, + # Full win64 Wine prefix produced by installing CODESYS 3.5.15.20 interactively + # (with dotnet462, msxml4/6 via winetricks). See pkgs/codesys3/README for steps. + src, +}: + +let + wine = wineWowPackages.stable; +in + +stdenv.mkDerivation { + pname = "codesys3"; + version = "3.5.15.20"; + + inherit src; + sourceRoot = "."; + + nativeBuildInputs = [ + makeWrapper + copyDesktopItems + ]; + + desktopItems = [ + (makeDesktopItem { + name = "codesys3"; + desktopName = "CODESYS 3.5"; + genericName = "PLC Programming"; + exec = "codesys3"; + categories = [ "Development" ]; + startupWMClass = "CODESYS.exe"; + }) + ]; + + installPhase = '' + runHook preInstall + + # The src is a full wine prefix (win64). Copy it to the store as a read-only + # template; the wrapper inflates it to ~/.local/share/codesys3 on first run. + install -d $out/share/codesys3-prefix + cp -r . $out/share/codesys3-prefix/ + + # Also expose the vcredist2019 DLLs so the wrapper can drop them in on demand. + install -d $out/share/codesys3-windll + cp ${vcredist2019}/windll/*.dll $out/share/codesys3-windll/ + + makeWrapper ${wine}/bin/wine $out/bin/codesys3 \ + --run ' + PREFIX="$HOME/.local/share/codesys3-prefix" + if [ ! -f "$PREFIX/.codesys3-ready" ]; then + echo "Setting up CODESYS 3 prefix (first run, may take a moment)..." + rm -rf "$PREFIX" + cp -r '"$out/share/codesys3-prefix"' "$PREFIX" + chmod -R u+w "$PREFIX" + touch "$PREFIX/.codesys3-ready" + fi + export WINEPREFIX="$PREFIX" + export WINEARCH=win64 + export WINEDEBUG=-all + ' \ + --add-flags '"C:\\Program Files\\CODESYS 3.5.15.20\\CODESYS\\Common\\CODESYS.exe"' + + runHook postInstall + ''; + + meta.mainProgram = "codesys3"; +} diff --git a/pkgs/mfc42/default.nix b/pkgs/mfc42/default.nix new file mode 100644 index 0000000..5c2fdbc --- /dev/null +++ b/pkgs/mfc42/default.nix @@ -0,0 +1,31 @@ +# mfc42.dll / mfc42u.dll, taken from the Visual C++ 6.0 redistributable. +{ + runCommand, + fetchurl, + p7zip, + cabextract, +}: + +let + # To get the hash: nix-prefetch-url --type sha256 + redist = fetchurl { + url = "https://download.microsoft.com/download/vc60pro/Update/2/W9XNT4/EN-US/VC6RedistSetup_deu.exe"; + hash = "sha256-wuuR2cREjVDkajL+y8w7QYcG0AK+q5tfSYHeVSCYzuc="; + }; +in + +runCommand "mfc42" + { + nativeBuildInputs = [ + p7zip + cabextract + ]; + } + '' + # VC6RedistSetup_deu.exe is a self-extracting archive that 7z can unpack; + # the vcredist.exe inside it is a CAB holding the actual DLLs. + 7z e ${redist} vcredist.exe -otmp -y + + mkdir -p $out/windll + cabextract tmp/vcredist.exe -d $out/windll -F "mfc42*.dll" + '' diff --git a/pkgs/mspaintxp/default.nix b/pkgs/mspaintxp/default.nix new file mode 100644 index 0000000..cf7825d --- /dev/null +++ b/pkgs/mspaintxp/default.nix @@ -0,0 +1,86 @@ +{ + stdenv, + makeWrapper, + makeDesktopItem, + copyDesktopItems, + icoutils, + wine, + mfc42, + # The Paint binary lifted out of a Windows XP install. + exe, +}: + +stdenv.mkDerivation { + pname = "mspaintxp"; + version = "xp"; + + src = exe; + dontUnpack = true; + + nativeBuildInputs = [ + makeWrapper + copyDesktopItems + icoutils + ]; + + desktopItems = [ + (makeDesktopItem { + name = "mspaintxp"; + desktopName = "Paint (Windows XP)"; + genericName = "Image Editor"; + exec = "mspaintxp %f"; + icon = "mspaintxp"; + categories = [ + "Graphics" + "2DGraphics" + "RasterGraphics" + ]; + mimeTypes = [ + "image/bmp" + "image/gif" + "image/jpeg" + "image/png" + "image/tiff" + ]; + # Wine names the window class after the .exe; this lets the taskbar + # match the running window back to this entry. + startupWMClass = "mspaint.exe"; + }) + ]; + + # Windows DLLs this .exe needs at runtime. Works like buildInputs: Nix hands + # the list to the builder as a space-separated string of store paths, and + # installPhase pulls $dep/windll out of each one. Add a dependency by adding + # a package here. + winDlls = [ mfc42 ]; + + installPhase = '' + runHook preInstall + + app=$out/share/mspaintxp + install -Dm644 $src $app/mspaint.exe + + # Wine resolves imports from the .exe's own directory first, so the + # dependencies only have to land next to it. + for dep in $winDlls; do + install -Dm644 $dep/windll/*.dll -t $app + done + + makeWrapper ${wine}/bin/wine $out/bin/mspaintxp \ + --add-flags $app/mspaint.exe + + # Resource group 2 is Paint's own application icon (the pencil cup); + # it ships 16/32/48px variants, of which the 32-bit ones look best. + wrestool -x -t 14 -n 2 $src -o app.ico + for size in 16 32 48; do + icons=$out/share/icons/hicolor/''${size}x''${size}/apps + mkdir -p $icons + icotool -x --bit-depth=32 --width=$size --height=$size \ + -o $icons/mspaintxp.png app.ico + done + + runHook postInstall + ''; + + meta.mainProgram = "mspaintxp"; +} diff --git a/pkgs/vcredist2019/default.nix b/pkgs/vcredist2019/default.nix new file mode 100644 index 0000000..43b6f83 --- /dev/null +++ b/pkgs/vcredist2019/default.nix @@ -0,0 +1,35 @@ +# Visual C++ 2019 (MSVC 14.29) x64 runtime DLLs. +# msvcp140.dll, vcruntime140.dll, concrt140.dll, mfc140.dll, and friends. +{ + runCommand, + fetchurl, + cabextract, + p7zip, +}: + +let + redist = fetchurl { + url = "https://aka.ms/vs/16/release/vc_redist.x64.exe"; + hash = "sha256-avrming/ESkhSRdYRK7Q4s4/JHvAJQ9ssYyTEpWz85k="; + }; +in + +runCommand "vcredist2019" + { nativeBuildInputs = [ cabextract p7zip ]; } + '' + # The vcredist bundle is a WiX burn exe. Its structure: + # - PE stub (bootstrapper) + # - First CAB at offset 479744: bootstrapper UI resources + # - Second CAB at offset 684808: payload packages (MSIs + their CABs) + # + # We skip straight to the second CAB which contains the DLL payloads + # in its a11/a12/a13 inner CABs. + dd if=${redist} bs=1 skip=684808 of=container.cab 2>/dev/null + cabextract container.cab -d pkgs 2>/dev/null + + mkdir -p $out/windll + # a11 = vcRuntimeMinimum_x64 payload, a12 = additional, a13 = MFC + for cab in pkgs/a11 pkgs/a12 pkgs/a13; do + [ -f "$cab" ] && cabextract "$cab" -F '*.dll' -d $out/windll 2>/dev/null || true + done + ''