Hacker News new | past | comments | ask | show | jobs | submit login
Universal DOOM – a single EXE for DOS 6, and Windows 95 to Windows 10 (github.com/nneonneo)
247 points by _Microft on Nov 14, 2020 | hide | past | favorite | 68 comments



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.

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.


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.


It must be powered by an enormous library of automated tests.


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!

https://github.com/neosmart/msvcrt.lib


Oh, this is great! Wish I'd seen this when I was compiling Chocolate Doom (on old Windows 95 builds my DOOM.EXE requires msvcrt.dll).


Thanks. I needed it enough times to warrant making it easily accessible for everyone. Great work yourself!


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!

I'm worried that people won't heed this warning.


I’m amazed they didn’t add a single game screenshot to the github page. You have to invoke the nastalgia


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.)


It’s just a MSVC linker directive. Use /STUB


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.


How hard would it be to add support for other operating systems like OS X as well?


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.


My guess is that the headers would be incompatible.


Imagine something that could run on the latest Macbook Pro and a Mac Classic II.


It should be very possible to build something that runs on any version of OS X/macOS, so about 20 years.


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.


As someone born in 2001… yeah. Wow.


I don't think the PPC emulator ships with osx anymore.

Did the initial versions of osx recognize fat binaries or was that explicitly introduced later?


XNU has had support for fat binaries since NeXTSTEP.


I think fat/universal binary trickery might be able to do it?


"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.


I believe multi-platform executables date back to NeXTStep, which supported 68K/x86/Sparc/HPPA binaries.


...Perhaps we’re targeting the wrong product lines. I wonder if you could make a binary that runs across all OS X but also NextStep...


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…


Neat. I have a NeXT Station Turbo (68040/33, I think?) and am tempted to try this out.


Oh, this is perfect! Thanks for sharing!


Java?


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.


<3 that you left the (Motorola) 68k implicit so that those not in the loop will have a hard time figuring out what is actually being discussed


Hard to break old habits :)


I mean, it's doom. You can literally do it in javascript on the browser.


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.

[0] https://www.chocolate-doom.org/wiki/index.php/FAQ#Can_Chocol...


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.


And for DOS, there is FastDoom (https://github.com/viti95/FastDoom )!


What's the problem with SDL 1.2 on Win10? The latter should still DirectDraw.


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.

/s


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".


Yes, that's the default stub or you can use the linker's "/STUB" flag to specify alternative DOS code to link into your windows exe.


(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.


If the article had answered my question, I wouldn't have asked.


More details here then if you are still unclear...

https://wiki.osdev.org/PE

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).


the bottom paragraph of the readme seems to agree with basically what you said, by design.


This is really cool. One of the software [0] that I am using haven't been update since 1999 but it still work perfectly on Windows 10.

[0]: http://pmdevigne.free.fr/TaskbarActivate.html


Meanwhile there are android games I bought like 3 years ago that don't run anymore.



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 :)


Mostly just pointing it out. I think you're probably right as to the "why". UPX/packers often flag this sort of thing.

I'm assuming the Registry keys and the like are from the statically linked in libs.


Ah, but can it run on a pregnancy test?

https://twitter.com/Foone/status/1302820468819288066


Congratulations, you are pregnant with demon spawn.


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.


Nice! I'll bookmark this in case I need to change the stub later


Could it run on Windows 3.11 (using win32s/winG) too?


If it runs on DOS then it will run on Win 3.11 too, but using the DOS version.


But could it run on Windows 3.11 using win32s/winG?


Does it run with wine ? :)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: