I’m using “DEX” to refer to everything that isn’t the core C++ app, which includes the Python compiler drivers and the (mixed C++/C#) compatibility layers that borrow heavily from Emscripten. It’s an even cooler name than DExTr (and makes even less sense), and it’s only three letters which means I can map my compiler commands in a way very similar to how Emscripten does it; ‘emmake’ -> ‘dexmake’, ‘emcc’ -> ‘dexcc’, etc.
Basically DEX includes all the things you would typically expect your compiler to include: musl libc, libcxx, and a C# layer that binds ‘syscalls’ to the s&box API under the hood. So stdout maps to Log.Info, stderr maps to Log.Error, fopen maps to FileSystem.Data.Open*, et cetera.
I also made an effort to mimic a lot of Emscripten functionality, so instead of an HTML5 header that you include, we have some s&box specific headers to set up a main loop that gets run in OnUpdate, alongside some other quality of life things.
After a few barebones experiments to make sure the filesystem etc. was all working how I expected, my first field test of DEX was the obvious choice; the rite of passage of all rites of passage, the quintessential DOOM port.
And holy shit, it was so much easier than I thought it would be. Like, too easy. I had it working in the span of like, two days. And the syntax for the transitions between C# and C++ were so beautifully intuitive.
In Emscripten, you can do this cool thing where you weave in and out from C++ mode to JavaScript and back to C++ and then do a little more JavaScript, passing variables between the two like it’s nothing. The DEX syntax for this is very similar:This is a snippet from my DOOM port, everything outside DEX_ASM_ARGS is C++ and everything inside is C#, and it just works. Like… come to think of it, this is actually easier than the normal way of passing pointers from C++ to C# when you’re working with unsafe code. The memory span is just, right there… you just grab it and everything’s gravy.
So then I had DOOM working (you can check that project out now, it’s available on sbox.game as DoomBox!) and I figured “let’s take it one step further and port Quake!”At which point I’m starting to realize, okay this is actually running a LOT faster than I gave it credit. Obviously it’s not anywhere as near as fast as native code, but the fact that it was able to manage software rendering at 20-25 fps ENTIRELY in safe C# was another one of those “what the fuck” moments.
Then I realized, hey, interop is really easy, why don’t I just tailor GLQuake to call S&Box’s Graphics.* APIs instead of calling OpenGL?
And bam, it worked, Quake in the scene system like it was nothing.At this point I was having so much fun I figured… I’ll just go all out on this, I’ll find out how to make multiplayer work.
So I made a pretend socket layer that sits on top of your s&box connections, and once again, everything worked pretty much how I expected it to.Then I was like - “this is really fast, I wonder if I could run emulators at a decent speed?” The answer was yes. This was getting too fun. Time to give myself a challenge.