I remember these old Czech Čtyřlístek games from my childhood, when they showed up as part of Nesquik cereal promotions. My brother and I used to play them together, and years later I wanted to see whether my son could play them too. That quickly turned into a practical problem: my own machines run Linux or macOS, and even a Windows 11 laptop from work did not run the games reliably.
If you are not Czech, Čtyřlístek is a long-running Czech comics series for kids. It also produced a line of PC CD-ROM games. The whole series has roughly twenty titles, but I focused on three CDs I still had at home:
- Čtyřlístek: Zítra se bude tapetovat (
tapetovat, 2001) - Čtyřlístek: Sami doma (
sami, 2003) - Čtyřlístek: Silák Bobík (
silak, 2005)
These are not DOS games. They are Win32-era 2D point-and-click adventures built on Centauri Production's CPAL2 engine, with DirectDraw, DirectSound, DirectInput, DirectShow/AMStream, old codecs, and a lot of assumptions from the Windows 98/XP period. My goal was simple: launch all three games reliably, avoid a dedicated old Windows machine, and make the result usable from a browser so I could share and test it quickly.
The short names above are the labels I use in scripts, logs, and runtime tooling. They are much easier to work with than full Czech titles when building images, patching files, and switching between test runs.
What Did Not Work
The obvious first attempt was running the original media directly on Windows 11. Installation worked, but the games crashed on startup even after trying different compatibility-mode settings. That was not a setup I wanted to debug or depend on.
The next attempt was Windows XP in UTM. On paper, that sounded right: a period-correct operating system for period-correct software. After roughly an hour and a half of setting up UTM and installing XP, the blocker was simple and disappointing: this path did not give the games the DirectX support they needed.
Then I tried a more ambitious browser-first direction with v86: boot a Linux i386 guest in the browser, run Wine inside the emulated x86 system, and expose a web UI for launching the games. That experiment was still useful because it proved the shape of the experience. Browser-based launch controls made sense, diagnostics could be surfaced in the page, and the whole flow could be shared remotely.
But it was the wrong runtime. The build-test loop was painfully slow, and the final blocker was not just performance. Wine startup inside the v86 guest became unstable, with failures such as WINE_EXITED_EARLY and low-level memory-mapping problems around virtual_map_user_shared_data. At that point, continuing to tune the emulated guest looked less useful than changing the architecture.
The Runtime That Worked
The practical solution was to keep the browser UX and drop the full CPU emulation layer. The final runtime uses a linux/386 Docker image with Wine, TigerVNC's Xvnc, noVNC, websockify, PulseAudio, and a small Python control server.
In simplified form, the stack looks like this:
Browser -> noVNC/websockify -> Xvnc -> Wine -> cdromek.exe
Browser -> Audio WebSocket -> control server -> PulseAudio TCP stream
Browser -> control API -> launch / stop / status / logs
Xvnc is the X server and VNC server at the same time, which avoids an extra framebuffer-polling layer. The browser receives the desktop through noVNC, while audio is handled separately: PulseAudio writes to a null sink, a TCP stream exposes the monitor output, and the browser plays PCM through an AudioWorklet with a small low-latency buffer.
The control server became more important than I expected. It owns game selection, cleanup of stale Wine processes, launch modes, log tailing, status metrics, and the small differences between the three titles. tapetovat can use a native-first ddraw wrapper path, while sami and silak behave better with Wine's built-in ddraw in the current setup.
The Legacy Details
The most useful lesson was that the hard parts were not always "emulation" problems. Some were plain old runtime assumptions.
tapetovat already had extracted runtime files, but sami and silak wanted to go through an installer path. Their media contains Centauri CPack archives: cdromek.pak for resources and cdromek2.pak for the executable. Extracting those into cdromek.res and cdromek.exe, then launching from the extracted runtime directory, made the games behave much more like tapetovat.
There were also path assumptions. The games expected variants of C:\runtime-* and C:\Program Files\Ctyrlistek\..., so the launcher prepares writable runtime copies and creates compatibility aliases where needed. Old Windows programs also expect TEMP and TMP to be Windows-style paths, not just Unix environment variables.
Fonts and media registration needed their own hardening. The container installs core fonts, adds legacy font substitutes like Arial CE, registers DirectShow-related COM classes, sets DirectX-era registry values, and maps old codec names. Without this, some failures looked like game bugs even though the actual problem was the Wine environment not looking enough like an old Windows install.
The nastiest issue was menu AVI playback in sami and silak. The failing path went through CPSE and AMStream, with errors like CPSE: Stream open file failed, Can't create multimedia stream instance, and RenderEx -> 0x80040218. sami ships Intel Indeo-era AVI files, while silak includes WMV3-era video. I tried normalizing clips and simplifying the media path, but the DirectShow graph still failed in places.
The pragmatic fix was to patch known runtime executable builds and blank the menu_*.avi references. That bypasses the crashing menu-video path while leaving the games themselves playable. It is not perfect preservation, but it is a reasonable tradeoff for a reproducible "works now" setup.
Current State
All three games now launch from the browser. The installer path is bypassed, stale Wine processes are cleaned up between launches, audio latency is noticeably better than in the earlier versions, and the menu AVI crash blocker is worked around.
The remaining weak spot is video delivery. The setup is functional and usable, but the stream can still feel choppy under load, currently around the low-20 FPS range in the cases where it struggles. There is probably more performance to recover by tuning ddraw modes per game, cutting render/encode overhead, or eventually replacing the VNC transport with something more efficient.
That is no longer the main milestone, though. These old Czech games are playable again in a modern browser workflow, and they are no longer just a preservation experiment sitting on my machine. My son and I actually play them now.
At this point, the hardest obstacle is not Wine, DirectShow, Docker, or streaming latency. It is teaching him how to control a game with a mouse, because until now he has mostly played on a mobile phone. That is a much better problem to have.