That’s cool! Windows’ backwards compatibility is amazing. It also allows for feature detection so a program can work on 3.11 but also look and feel native on 10.
It’s a simple utility but it works on Windows 3.11 (with Win32s) and up, but supporting high DPI, font scaling, and theming on Windows versions that support it.
That's really cool, and yes, Windows' BC is really something. The engineering effort to keep everything working so well across versions must be staggering.
Favorited on GitHub for future reference (manually when needing to make a point). Awesome work!
I publish a universal msvcrt.lib that lets you use “standard C functions” without dependencies or static linking, but I’m quite sure it wouldn’t support Windows 3.x!
Thanks! Note that I don’t have much experience writing Win32 apps so the code might not be top quality.
The universal lib is interesting for future work (the compiler inlined the few things I needed). For Win32s support generating a position-independent executable was the only problematic bit, but that's not an issue for libraries.
> Note: It is recommended to use the versioned C runtime when and where possible; this script is only provided for the convenience of developers familiar with the pitfalls and caveats that come with using the unversioned copy of msvcrt.dll and the minimal APIs it exposes!
Wow, I’m super happy to see folks interested in my little project! I was inspired to do this thanks to a challenge posted to Twitter. It was fun putting together this binary and learning more than I thought I was going to learn about DOS extenders and the PE format. Happy to answer any questions!
Do you know of a good way (if any) to shrink the size of the DOS header as much as possible on MSVC? (Like to remove the "This program cannot be run in DOS mode" notice, and other things you don't need on Windows.)
Yeah I tried that at some point but I didn't figure out what to feed it to make a smaller executable that would still run. Not sure what I might've done wrong unfortunately, it's been a few years.
Most file formats have a magic number at offset 0. The magic numbers for EXE ("MZ"), OS X (0xfeedface/0xfeedfacf) and Linux ("\x7fELF") are all different, so they aren't compatible.
Not on windows, but that depends on the OS. On Linux the handlers are pluggable via binfmt (https://en.m.wikipedia.org/wiki/Binfmt_misc) and this allows for example wine and .net binaries to work transparently.
(I'm not aware of it, but maybe there a similar system in windows)
The PE magic number doesn't need to be at offset zero, which is why you can do this. Mach and ELF do need to be at offset zero, so you can't have something that's Mach and ELF at the same time. In theory, this is possible, if you can recover from COM executing the header.
I've tried going down that rabbit hole before. The primary issue that stopped me was the different executable file formats.
I never took it anywhere, but I think the way to go would be a "polyglot" script that could be interpreted by different script engines (sh/bat) that would then pick the correct executable for the given OS. This would require the user to change the file extension as well as download multiple executable files so I don't necessarily like this solution from an elegance stand point.
I've had good success with shebangs combined with zip archives containing python code. Zip files use a footer rather than a header, and python natively supports executing scripts out of them. The beginning of the file can have a shebang, allowing it to specify the interpreter to use. The only trick is that windows doesn't support shebangs, and so you need to associate your chosen file extension with python on every system. Also, you need python installed obviously.
What that gives you is a stable entry point into python code. From there, you can run whatever platform specific code you want.
I hadn't quite put together how much desktop computing changed between 1994 (DOS, Windows 3.1, System 7) and 2001 (OS X, Windows XP), and how relatively little it's changed since then.
"Universal" Mach-O binaries are specific to the Mach-O binary format, which wasn't supported on classic Mac OS. (I'm not even sure that multiplatform executables were even supported by Mac OS X before 10.4 or so, when they started using them for Intel cross-compatibitility.)
"Fat" CFM/PEF binaries existed on classic Mac OS, but used a completely different mechanism that only supported 68k and PowerPC code. And if you wanted to support a Classic II, that rules out a Carbon application bundle too.
About the widest range I can imagine for a single executable is a hybrid CFM/PEF/Carbon executable, which might be able to run on anything from a classic 68k mac up to Mac OS 10.6 (through Rosetta).
For the specific request here (Classic II and current MacBook), you might be able to build an executable with Mach-O data fork and the 68k code in the resource fork. I’m not sure offhand how to make the bundle format work for current macOS, though.
But if you can make that work, you can also add classic PowerPC support, by jumping to it via a 68K CODE stub and perhaps from a separate PEF fragment stored in a section in the Mach-O that would be ignored on current macOS.
Shameless self-promotion: https://github.com/saagarjha/dummy_thicc. That reminds me that I have left PRs for the NeXTSTEP slices hanging around for quite a while…
Macintosh Runtime for Java up to version 1.5.1 requires an ‘030 and System 7.1, so that would be technically possible on a Classic Ⅱ. You would be limited to Java 1.0 API with no JIT, because MRJ 2.0 requires an ‘040 or PPC for that.
But that's emulated. Emulating a popular video game on a major platform from 1993 isn't all that crazy (not to understate how tricking writing an emulator can be). Having a binary that works on modern hardware and 25+ year old hardware is what's impressive, and it's small enough to fit on a floppy.
I am confused on why the polglot program is required, especially PE files are designed to contain MZ files (the MSDOS format), and Doom is not greater than 2GB (half the limit of MZ+PE file). Is there an alignment issue on the DOS version of Doom that requires this trickery? Possible licensing issues?
At the very start I actually built the binary using the usual strategy of including a big DOS stub, but it wouldn’t load on Windows 10. I’m not entirely sure why, but I believe it’s because the next_offset value in the MZ header was too big - >= 4096 would make it fail. So I resorted to stuffing the PE header inside the MZ header after suitably expanding it.
Mind that the Windows side is using a very old version of Chocolate Doom, presumably because the versions using SDL 1.x at least had a fighting chance of running on Windows 95 (even though it was never actually supported)[0]. SDL 1.x has the other side effect of not running on Windows 10 very well, if at all.
Windows 95 compatibility might be neat, but it's probably better if it used the SDL2-based Chocolate Doom 3.0.1.
FWIW, it does work on Windows 95 (at least in QEMU). Old builds of Windows 95 might need msvcrt.DLL, but that’s it as far as dependencies go. It did require a custom build of libSDL with a few tweaks.
Meanwhile most apple dev (like myself dabbling it in) have a hard time keeping up with the changes of swift, macos, xcode, security requirements moving sands, developer fee, nanny state declarations of eternal faith and jumping into a dark tunnel full of venomous spikes. And for $0 for 98% of us. Because we still believe in some dead guy's ideology.
Isn't this possible by design? All Windows executables at least used to be also valid DOS programs that just output "This program cannot be run in DOS mode".
Yes, and this is not the new trick either. AFAIK regedit.exe in Windows 9x had a DOS "stub" that is actually a DOS version of the application.
On the other hand, for some small-sized demos DOS stubs were completely discarded to make room. I recall many Farbrausch demos had a hard-coded "stub" that simply reads "farbrausch".
(Update: yes, by design, the paragraph I quoted was meant to confirm that, but guess I should’ve been explicit)
From the intro...
> DOS and Windows .exe files both start with a common header (the DOS "MZ" header), which might suggest that you can run programs for one OS on the other one. However, this is complicated by a few facts: DOS support was dropped in Windows a long time ago (DOS binaries don't load on Windows 10, for example), and DOS programs use a completely different execution environment.
> This repo builds a single .exe file with a "polyglot" MZ header which allows it to load one program (the original DOOM) when running inside DOS, and a different program (a custom static build of Chocolate DOOM) when running on Windows.
tl;dr: Yes, it was originally by design, but very rarely used in practice (most use the default text you mentioned), and isn’t exactly straightforward in some tooling (but not hard either, as evidenced by the simple Python script here).
If I had to guess, the scanners are picking up on the fact that it’s UPX-packed with funny headers. I don’t know how much my word is worth, but I give you my word that I compiled the binary from source and there’s nothing malicious in there. You’re welcome to upx -d the binary to check this yourself, or build your own copy with the instructions in the repo if you’re in doubt :)
Raising kids and battling the demon hordes are such obvious good practice for each other, I’m surprised all pregancy sticks don’t already come with DOOM on them as standard.
I played around with that here: https://github.com/sjmulder/netwake
It’s a simple utility but it works on Windows 3.11 (with Win32s) and up, but supporting high DPI, font scaling, and theming on Windows versions that support it.