Porting Quake II to MS-DOS pt3

Well it mostly works now.  But did we ever have the biggest fun with the sound and SVGA.

So [HCI]Mara’akate integrated all the Q1 sound code, got it to compile, but nothing not a peep from the sound card.  While I was busy trashing the video code, he spent way too much time on the audio, and then for the heck of it I thought I’d look at the code, although I’m really stabbing in the dark when it comes to audio.  Imagine my surprise when I compiled the code, and ran it and got the sound blasting at full volume!  It turns out that I had my audio set to ‘high quality’ in the client, while he had his set to ‘low quality’.  What it does is govern the frequency between 11Khz and 22Khz.  And if you get this wrong you get *NO* audio. OOPS.  At least it was one of those feeling vindicated moments that his efforts for sound really did work.

Now with audio, it was my time to hurry up and get the video going.  I had basic VGA working so I figured I didn’t want to spend a lot of time on this, so I was going to go with the VESA 2.0 linear frame buffer.  Well this once more again proved to be a bit more involved as the only way I really have to test is emulation via DOSBox and Qemu.  And naturally both of them worked fine, but when [HCI]Mara’akate ran it on his real DOS Box (with GUS of course) it crashed wonderfully.

Now I had taken the VESA init code from Quake 1 to build a table of what 8bit modes are supported, and used the VESA code to switch, along with drawing to the buffer with a simple memcpy.  And we got nothing but crashes.

After looking more around, I found that you had to add 0x4000 to the VESA mode you want if you wanted to access it’s LFB.  Did that work? No still crashed.

Later in the adventure we noticed to get proper access you had enable ‘near pointer acess’ with a call to :

__djgpp_nearptr_enable();

So naturally I would disable it when I’m done, making the call look like this:

__djgpp_nearptr_enable();
memcpy(vid_resolutions[whatmodearewe].address+__djgpp_conventional_base,vid.buffer,(vid.height*vid.width));
__djgpp_nearptr_disable();

Right?

WRONG. Oh so WRONG.  Well OK technically it did work, but if sound is enabled (and why wouldn’t it be?) it would immediately crash with an error in the DMA code.  We ended up wasting over a day trying to figure this one out until I just said screw it, let’s never dsiable the nearpointer, leave it unprotected.  Naturally that actually worked.  The hint is in map_in_conventional_memory, where __djgpp_nearptr_enable(); is called, but of course there is no calls to __djgpp_nearptr_disable();  I’ve thought about going back through the source to ‘clean’ it up to make it lock and unlock memory as needed, but this is 2015 not 1997 so good enough is well, good enough.

So now with VESA and Sound, I thought this would be a great time to tackle the dynamic loading of gamex86.  We will have to re-compile whatever gamex86 DLLs are out there as of course DOS is DOS.  The HX DOS guy Japheth seems to have died, as his site and all the info on that extender seems to have mostly vanished.  I have an old copy, but I never could make it call DOS stuff if you had WIN32 stuff going on.  And if he’s really dead it’s too late to ask him.

Ages ago I remembered this DLL support for DJGPP called DLM, so I thought I’d give that a try.  I was able to take our ‘null’ version of Quake II, and get it to where it can load and unload the gamex86.dlm at will.  So I figured this was going to be an easy win, right?

Wrong again.  Once I started to put in the MS-DOS specific code I got this fun crash:

DLM -> Exiting due to signal SIGSEGV at
0x0014f59d SYM: _dos_lockmem+0x9 DLM: q2.exe [0xf5000]
possibly because of undefined reference to symbol ‘___djgpp_base_address’

DLM call frame traceback :
0x0014dcaf SYM: _Sys_PageInProgram+0x37 DLM: q2.exe [0xf5000]
0x0014e1c4 SYM: _main+0x18 DLM: q2.exe [0xf5000]
0x0000774e
0x00008b6e

So apparently it doesn’t export (or import) djgpp stuff like the base address calculations so we can’t do direct memory access, which means no video and no sound.  Ouch.  I’ll have to hit the lists to see about support.  I don’t like the DXE’s because they cannot be unloaded, so that isn’t much good.  And of course things like the JVM inside of gamex86.dll is right out as JAVA inside of MS-DOS? in 64MB of RAM? Dream on.

So for now, the gamex86.dll is statically linked into the executable.  You are restricted to vanilla gameplay for now.

As an added bonus, I used Rufus 2.2, to setup a bootable MS-DOS USB stick, slapped everything onto there, and booted up on my Xeon, and it works!

Quake II running on MS-DOS
Quake II running on MS-DOS

Now as a super bonus,  [HCI]Mara’akate went above and beyond by adding in a bunch of fixes, and updates from 3.24 and varous stuff from Knightmare.  And then the best part is integrating libogg, so it can now play the ogg sound track!  Really, just place the ogg files in the baseq2\music directory and away you go!

For those who care the bitbucket of the project is here, and binaries are here.

**UPDATE**

For anyone who is interested, I’ve updated the binaries, to include the latest version with built in quakespy technology!  Run an /slist2 and get a list of servers!

/slist2 in action!
/slist2 in action!

For the more adventurous trying to build from source, we are using GCC 2.95.3 and DJGPP 2.04.  All of the sub libraries that you need to build are already pre-compiled in source drop.

At this point the ‘alpha 2’ version contains:

  • VGA
  • SVGA
  • Mouse
  • Keyboard
  • SoundBlaster and Gravis UltraSound Family
  • CD-ROM music
  • OGG music
  • Networking (You need a packet driver)

Installing the packet driver will require a driver from the crynwr project.  You can find a description of some of the drivers here.  Sadly for new cards I think we are left in the dark.

I’ve also compiled a ‘server’ for Linux based on the code, and put it online @ 87.117.247.11:27910

Continued in pt4, and part 5.

Porting Quake II to MS-DOS pt2

Continuing in this series on porting Quake II to MS-DOS, we get to touch some of the fun stuff.  The first big ‘fun’ thing is networking.

Now in my prior work with the MS-DOS version of Quake, I had used the WATTCP library to bring networking to the otherwise Windows/UNIX specific fun of network deathmatch back to MS-DOS.  Quake by default had support for the Beame & Whiteside’s TCP/IP stack which for all intents and purposes has vanished from the face of the Earth (does anyone have a copy?!).  So at the time, I thought it’d be cool to try to interface WATTCP with Quake, and it worked on the first attempt as WATTCP is a very competent TCP/IP stack.

So I took the Linux networking file net_udp, and compiled it, and I got an executable!

When it comes to testing WATTCP though, I prefer to use Qemu instead of DOSBox as it can not only emulate various network cards to which I can find packet drivers (yes even the evil PCI NE2000!) but it has a built in SLiRP network stack that let’s me NAT on my desktop without any crazy network configs.

And for the sake of testing, I setup a ‘null’ text mode server, figured out some flags, and I was able to connect!

Quake II for MS-DOS running on Qemu connected to a dedicated OS X server.
Quake II for MS-DOS running on Qemu connected to a dedicated OS X server.

Very exciting stuff indeed!

Now for some interesting stuff.  First I noticed that MS-DOS 5.00 with himem.sys is almost unplayable because it is so slow.  MS-DOS 4.01 without himem.sys is actually faster.  No, I’m not kidding.

Next is that some levels LOVE to gobble up RAM.  Maps like city1 will need at least 192MB of ram.  I haven’t even tried playing with the virtual memory of DJGPP, and I really don’t want to.  And let’s face if, if you even try to load Quake II on a MS-DOS machine, it better be a ‘big’ one.  This means you should be using the ‘dos’ from Windows 98, or perhaps FreeDOS, although I haven’t tested that at all.

So far from our limited testing the networking seems to be pretty good.   And at least that is one function we didn’t have to really pour a lot of effort into.  Although the payoff of being able to connect to servers on the LAN and even the internet is a good thing.

Continued in pt3, pt4, and part 5.

Porting Quake II to MS-DOS pt1

(In this series, I’m going to go over the process of porting Quake II to MS-DOS.  Now of course the question will pop up why? And the answer is simple enough, after [HCI]Mara’akate added in gamespy support, something was noticeably off, and that is fewer and fewer people are playing Quake 1 these days.  So what to do?

Well apparently Quake II has an active following, so it’s time to move our creeky but favorite OS, MS-DOS into the Quake II Arena!

So the first thing to do is to grab a copy of the GPL source to Quake 2, along with a copy of the game, and get started on making a ‘null’ version of the game.  Null versions of the game have no graphical output, no sound, and ‘function’ at a very basic level.  It’s something to strive for as a first base in hitting that home run of a functional port.  Thankfully iD wrote really portable and modular software.  Unfortunately they tended to let their ports drift as they were writing the engine so the null code doesn’t actually work out of the box.  And the project makfiles leave a little to be desired for me, as they have a bunch of i386/Dec Alpha magic which doesn’t leave much in the room for weird ports like MS-DOS.

Now I should mention that before I’d gotten started the first thing I decided that like QuakeWorld for MS-DOS (and OS/2) I would use GCC as it is a known working compiler out of the box.  If you can, don’t fight so many battles of unknowns at once.  Another thing is that I am going to cross compile from OS X and test with DOSBox.  Of course you may want to use something else, and I know my tastes drift, but for now this is what I’m using. I’m using my old OSX to DJGPP cross compiler with GCC 2.95.3, which serves me well.

So the first thing was to compile and run the null version with native tools on OS X.  After a bit of struggle I got here:

$ ./q2
Added packfile ./baseq2/pak0.pak (3307 files)
Added packfile ./baseq2/pak1.pak (279 files)
Added packfile ./baseq2/pak2.pak (2 files)
Console initialized.
Sys_Error: ref_soft::R_BeginFrame() – catastrophic mode change failure

Well that’s great, how to figure this one out?

One thing I did to make it easier to work with the flow of Quake is to make the Sys_Error procedure contain a divide by zero.  Now why would I purposely put a divide by zero in the code?  Simple it lets me back trace the code when Quake catches it’s own faults so I can see what went wrong where, vs what would look like a clean exit.  From my example:

(lldb) bt
* thread #1: tid = 0xf14f9, 0x000c0853 q2`Sys_Error(error=0x000c17d4) + 98 at sys_null.c:28, queue = ‘com.apple.main-thread’, stop reason = EXC_ARITHMETIC (code=EXC_I386_DIV, subcode=0x0)
* frame #0: 0x000c0853 q2`Sys_Error(error=0x000c17d4) + 98 at sys_null.c:28
frame #1: 0x0003a0cb q2`Com_Error(code=0, fmt=0x000c17d4) + 368 at common.c:215
frame #2: 0x000c0523 q2`VID_Error(err_level=0, fmt=0x000cc8a8) + 81 at vid_null.c:45
frame #3: 0x000a6d38 q2`R_BeginFrame(camera_separation=0) + 592 at r_main.c:1145
frame #4: 0x000a4cfc q2`R_Init(hInstance=0x00000000, wndProc=0x00000000) + 276 at r_main.c:333
frame #5: 0x000c0740 q2`VID_Init + 362 at vid_null.c:122
frame #6: 0x00007900 q2`CL_Init + 55 at cl_main.c:1795
frame #7: 0x0003c2cb q2`Qcommon_Init(argc=1, argv=0xbffffc40) + 754 at common.c:1469
frame #8: 0x000c09ac q2`main(argc=1, argv=0xbffffc40) + 24 at sys_null.c:128
frame #9: 0x000019ad q2`_start + 212
frame #10: 0x000018d8 q2`start + 40
(lldb)

Now I know that R_BeginFrame was where the error was occurring, and looking at the code:

if ( ( err = SWimp_SetMode( &vid.width, &vid.height, sw_mode->value, vid_fullscreen->value ) ) == rserr_ok )
{

else
{
ri.Sys_Error( ERR_FATAL, “ref_soft::R_BeginFrame() – catastrophic mode change failure\n” );
}

We can see that the vid.width/vid.height aren’t being setup correctly.  Turns out there was a bunch more work to be done setting up vid_null!

 

After looking closer at the files, I notice as I’m stitching them together is that Quake II relies on DLL’s as part of it’s base functionality.  I drifted out of Quake after Quake 1, so I never played II before.  So I didn’t know this.  Obviously DJGPP doesn’t support DLL’s that can be loaded and un-loaded at will (Yes I know about DXE’s, but as the FAQ states, they cannot be un-loaded.  And I’m not going to fight DJGPP’s LIBC).  So looking further in the source, I saw these fun defines:

-DREF_HARD_LINKED -DGAME_HARD_LINKED

So at one point there was support for ‘hard linking’ in the ‘REF’ video driver, and the ‘game’ logic driver.  But it did kind of drift out of the code.  But looking at the Win32 version I could see that putting this functionality back in should be easy.  And to be honest if I learned any lesson from this, is that I should have been pulling the Win32 version apart by injecting null into it until it ran as a null platform, then use that as the basis.  Lesson learned.  Always start with a known good!  Quake II was built with Visual C++ 6, but I only have Visual C++ 4.2 installed on Crossover.  Yes I know again this is me being difficult.  But it didn’t take much time to get a simple project that has two DLL’s and a Win32 exe running.  Then I took on the ‘ref’ video driver and got that linking inside of the main EXE.  Now with one DLL ‘eliminated’ it was time to work on the game dll.

The game DLL posed the biggest challenge because it passes a reference to internal functions to it, and exports various functions back to the game engine.  So I ended up altering the engine to not call the game import/export directly but setting it up myself.  The hardest thing was that I couldn’t pull in the game header file, but rather I had to copy the prototypes myself.  Another interesting thing with the way Quake II works is that the game dll has to be able to be unloaded and loaded at will.  It wasn’t hard to simulate this, but I wasn’t expecting it.  Again this is probably because I never really played Quake II.

Now that I had Quake II building without DLL’s I could then take the next step of removing all the IO and re-replacing it with the null code, and now I had something that looked like it was doing something.

Added packfile ./baseq2/pak0.pak (3307 files)
Added packfile ./baseq2/pak1.pak (279 files)
Added packfile ./baseq2/pak2.pak (2 files)
execing default.cfg
execing config.cfg
NULLsock Initialized
Console initialized.
768k surface cache
ref_soft version: SOFT 0.01
——- sound initialization ——-
Sys_FindFirst [./baseq2/autoexec.cfg]: UNIMPLEMENTED!
==== InitGame ====
——- Server Initialization ——-
0 entities inhibited
0 teams with 0 entities
————————————-
====== Quake2 Initialized ======
loopback: client_connect
==== ShutdownGame ====
Sys_Error: Draw_Pic: bad coordinates

This turns out because I didn’t allocate the screen properly.  Looking at the code:

if ((x < 0) || (x + w > vid.width) ||
(y + h > vid.height))
{
ri.Sys_Error (ERR_FATAL,”Draw_Pic: bad coordinates”);
}

We can see it pretty plainly.

Now since we were going somewhere I started to write some MS-DOS code, and switch out of the null set of mind!

First a simple VGA mode 13 setup which gives us 320×200 resolution with 256 available colours.  And for good measure I did a simple VGA palette setup that I knew worked from a prior program.  Next we just blit our buffer onto the screen, and we get this:

Quake II on MS-DOS first screen
Quake II on MS-DOS first screen

Which is exciting and disappointing at the same time.  I then took the palette code from DOS Quake, and got something just as ugly.  I tried the code from OS/2. Same thing.  I tried all kinds of things and was going nowhere.

At this point Mara’akate added in the Linux clock code, and now we had this!

Quake II with a bad palette
Quake II with a bad palette

It wasn’t until much more digging around I saw some 320×240 screen setups that I realized there was something wrong there, and then I saw this gem in the linux port’s code:

/*
** SWimp_SetPalette
**
** System specific palette setting routine. A NULL palette means
** to use the existing palette. The palette is expected to be in
** a padded 4-byte xRGB format.
*/

In traditional VGA palette setups it’s 768 bytes that needs to be read/and pushed to the card.  I even checked Quake 1 is 768 bytes, but now in Quake II, it’s 1024 bytes!  OOPS!  Sometimes (ok a lot of times) you really need to check other ports or a ‘known good’ to see how they did things.

Quake II running on MS-DOS!
Quake II running on MS-DOS!

Pretty awesome!

So where to go from here? Obviously things like better keyboard input, the mouse, sound and networking need to be done.

Continued in pt2, pt3pt4, and part5.

Quake One blocked

Is this happening to everyone else?

Screen Shot 2015-05-04 at 11.40.58 AM
Google blockinig
Screen Shot 2015-05-04 at 12.02.16 PM
Sophos warning

Apparently, according to Sophos, it’s Mal/HTMLGen-A, which is just some generic placeholder thing.

Safari blocking Quake One now
Safari blocking Quake One now

The generic blocked by Safari message.

Which is a shame, they just had an interesting interview with John Romero.  Oh well when this gets sorted out, for those who are brave (lol) the link is right here.

Screen Shot 2015-05-04 at 10.44.56 PM

It’s short, but an interesting read none the less.

Setting up a Co-op QuakeWorld Server

I went with MVDsv, which I could just unpack the i386 executable on my VPS, allowing me to go ahead with my lovingly stock id1 pak files.  MVDsv requires  just paste this into the console, or add it to the server.cfg

sv_progsname spprogs
deathmatch 0
coop 1
map e1m1
samelevel “0”

MVDsv also requires spprogs.dat.  I forget where I got mine, but if you need it, you can use mine.

And firing it up..

Added packfile ./id1/pak0.pak (339 files)
Added packfile ./id1/pak1.pak (85 files)
IP address 62.113.213.65:27500
UDP Initialized
Initialized default mod messages.
Total: 60 messages.
16.0 megabyte heap
MVDSV 0.27 rev.643 (l), build 2772
Build date: Jul 24 2007, 19:53:52

MVDSV Project home page: http://mvdsv.sourceforge.net

======== QuakeWorld Initialized ========
execing server.cfg
couldn’t open accounts
VM_Load: “spprogs”
Client MS-dos connected
Couldn’t download skins/base.pcx to MS-dos
MS-dos entered the game

Now when I jump in I get monsters!

Co-Op QuakeWorld!
Co-Op QuakeWorld!

One thing I had screwed up was the samelevel command on my server, setting it to two would kill the player if they tried to exit the level.  Ugh.  There is a guide here, but of course co-op isn’t the same as death match!

QuakeWorld Clients

I thought Id’ take a few hours to update some builds based on the latest QWDOS source.

So I spent a few hours updating some QuakeWorld clients.. for reproducibility sakes, I built out the MS-DOS one and that worked fine.  The OS/2 one ‘works’ but the keyboard isn’t working.  Not so sure why not.  The Win32 version works fine, although I’m building with Visual C++ 4.0, and no assembly or DirectX.  Just straight GDI.

Next will be Linux with svgalib.

So my work is here.  I could go more into how to build, but I don’t know if anyone cares.

Building a super stock QuakeWorld server for Linux

I know this may sound silly, but I’m a silly person.  And yes, QuakeWorld used to build cleanly and fine on Linux.  However it doesn’t anymore, things have changed a *LOT* in the world of Linux, since the birth of QuakeWorld in 1996.  A different LIBC standard or two, and all kinds of other changes in the compiler.  Not to mention I have a x86_64 machine, and I want a pure 32bit binary, so the best way to go about that is to setup a 32bit virtual machine, and build from there.  I know I could cross compile, but on the otherhand I don’t want to install all this kind of crap on my server if I don’t have to.  So there is all kinds of reasons why you may want to build your own.  Not to mention if you are running Linux, but on a non x86 platform.  I guess the Raspberry Pi would be a good choice now that it is over two million units sold.

The first thing you need of course is the source code to Quake, from iD (my mirror).  Next you’ll need a more up to date Makefile.  I used the one from the LinuxGL-QuakeWorld-mini-HOWTO.  Don’t worry about the client stuff, it doesn’t matter the first thing it does anyways is build the server.

For the sake of preserving it, here is the makefile:

#
# QuakeWorld Makefile for Linux 2.0
#
# Apr '98 by Zoid &lt;[email protected]&gt;
#
# GNU Make required
#
# ELF only
#

MAINDIR=.

BUILD_RELEASE_DIR=bin
CLIENT_DIR=$(MAINDIR)/client
SERVER_DIR=$(MAINDIR)/server

CC=gcc
BASE_CFLAGS=-Wall -Dstricmp=strcasecmp -I$(CLIENT_DIR) -I$(SERVER_DIR)
RELEASE_CFLAGS=$(BASE_CFLAGS) -m486 -ffast-math -fexpensive-optimizations
GLCFLAGS=-DGLQUAKE -DGL_EXT_SHARED

LDFLAGS=-lm
XLDFLAGS=-L/usr/X11R6/lib -lX11 -lXext
GL_X11_LDFLAGS=-L/usr/X11R6/lib -lm -lGL -lX11 -lXext

DO_CC=$(CC) $(CFLAGS) -o [email protected] -c $&lt;
DO_O_CC=$(CC) -O $(CFLAGS) -o [email protected] -c $&lt;
DO_GL_CC=$(CC) $(CFLAGS) $(GLCFLAGS) -o [email protected] -c $&lt;
DO_SERVER_CC=$(CC) -DSERVERONLY $(CFLAGS) -o [email protected] -c $&lt;

DO_AS=$(CC) $(CFLAGS) -DELF -x assembler-with-cpp -o [email protected] -c $&lt;
DO_GL_AS=$(CC) $(CFLAGS) $(GLCFLAGS) -DELF -x assembler-with-cpp -o [email protected] -c $&lt;

#############################################################################
# SETUP AND BUILD
#############################################################################

TARGETS=$(BUILDDIR)/qwsv #$(BUILDDIR)/glqwcl.glx $(BUILDDIR)/qwcl.x11

build_release:
  @-mkdir $(BUILD_RELEASE_DIR) \
    $(BUILD_RELEASE_DIR)/client \
    $(BUILD_RELEASE_DIR)/glclient \
    $(BUILD_RELEASE_DIR)/server
  $(MAKE) targets BUILDDIR=$(BUILD_RELEASE_DIR) CFLAGS="$(RELEASE_CFLAGS)"

all: build_release

targets: $(TARGETS)

#############################################################################
# SERVER
#############################################################################

QWSV_OBJS = \
   $(BUILDDIR)/server/pr_cmds.o \
   $(BUILDDIR)/server/pr_edict.o \
   $(BUILDDIR)/server/pr_exec.o \
   $(BUILDDIR)/server/sv_init.o \
   $(BUILDDIR)/server/sv_main.o \
   $(BUILDDIR)/server/sv_nchan.o \
   $(BUILDDIR)/server/sv_ents.o \
   $(BUILDDIR)/server/sv_send.o \
   $(BUILDDIR)/server/sv_move.o \
   $(BUILDDIR)/server/sv_phys.o \
   $(BUILDDIR)/server/sv_user.o \
   $(BUILDDIR)/server/sv_ccmds.o \
   $(BUILDDIR)/server/world.o \
   $(BUILDDIR)/server/sys_unix.o \
   $(BUILDDIR)/server/model.o \
   $(BUILDDIR)/server/cmd.o \
   $(BUILDDIR)/server/common.o \
   $(BUILDDIR)/server/crc.o \
   $(BUILDDIR)/server/cvar.o \
   $(BUILDDIR)/server/mathlib.o \
   $(BUILDDIR)/server/md4.o \
   $(BUILDDIR)/server/zone.o \
   $(BUILDDIR)/server/pmove.o \
   $(BUILDDIR)/server/pmovetst.o \
   $(BUILDDIR)/server/net_chan.o \
   $(BUILDDIR)/server/net_udp.o

$(BUILDDIR)/qwsv : $(QWSV_OBJS)
  $(CC) $(CFLAGS) -o [email protected] $(QWSV_OBJS) $(LDFLAGS)

$(BUILDDIR)/server/pr_cmds.o :   $(SERVER_DIR)/pr_cmds.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/pr_edict.o :  $(SERVER_DIR)/pr_edict.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/pr_exec.o :   $(SERVER_DIR)/pr_exec.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_init.o :   $(SERVER_DIR)/sv_init.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_main.o :   $(SERVER_DIR)/sv_main.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_nchan.o :  $(SERVER_DIR)/sv_nchan.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_ents.o :   $(SERVER_DIR)/sv_ents.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_send.o :   $(SERVER_DIR)/sv_send.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_move.o :   $(SERVER_DIR)/sv_move.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_phys.o :   $(SERVER_DIR)/sv_phys.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_user.o :   $(SERVER_DIR)/sv_user.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sv_ccmds.o :  $(SERVER_DIR)/sv_ccmds.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/world.o :     $(SERVER_DIR)/world.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/sys_unix.o :  $(SERVER_DIR)/sys_unix.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/model.o :     $(SERVER_DIR)/model.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/cmd.o :       $(CLIENT_DIR)/cmd.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/common.o :    $(CLIENT_DIR)/common.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/crc.o :       $(CLIENT_DIR)/crc.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/cvar.o :      $(CLIENT_DIR)/cvar.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/mathlib.o :   $(CLIENT_DIR)/mathlib.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/md4.o :       $(CLIENT_DIR)/md4.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/zone.o :      $(CLIENT_DIR)/zone.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/pmove.o :     $(CLIENT_DIR)/pmove.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/pmovetst.o :  $(CLIENT_DIR)/pmovetst.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/net_chan.o :  $(CLIENT_DIR)/net_chan.c
  $(DO_SERVER_CC)

$(BUILDDIR)/server/net_udp.o :   $(CLIENT_DIR)/net_udp.c
  $(DO_SERVER_CC)

#############################################################################
# CLIENT
#############################################################################

QWCL_OBJS = \
  $(BUILDDIR)/client/cl_demo.o \
  $(BUILDDIR)/client/cl_ents.o \
  $(BUILDDIR)/client/cl_input.o \
  $(BUILDDIR)/client/cl_main.o \
  $(BUILDDIR)/client/cl_parse.o \
  $(BUILDDIR)/client/cl_pred.o \
  $(BUILDDIR)/client/cl_tent.o \
  $(BUILDDIR)/client/cl_cam.o \
  $(BUILDDIR)/client/cmd.o \
  $(BUILDDIR)/client/common.o \
  $(BUILDDIR)/client/console.o \
  $(BUILDDIR)/client/crc.o \
  $(BUILDDIR)/client/cvar.o \
  $(BUILDDIR)/client/d_edge.o \
  $(BUILDDIR)/client/d_fill.o \
  $(BUILDDIR)/client/d_init.o \
  $(BUILDDIR)/client/d_modech.o \
  $(BUILDDIR)/client/d_part.o \
  $(BUILDDIR)/client/d_polyse.o \
  $(BUILDDIR)/client/d_scan.o \
  $(BUILDDIR)/client/d_sky.o \
  $(BUILDDIR)/client/d_sprite.o \
  $(BUILDDIR)/client/d_surf.o \
  $(BUILDDIR)/client/d_vars.o \
  $(BUILDDIR)/client/d_zpoint.o \
  $(BUILDDIR)/client/draw.o \
  $(BUILDDIR)/client/keys.o \
  $(BUILDDIR)/client/mathlib.o \
  $(BUILDDIR)/client/md4.o \
  $(BUILDDIR)/client/menu.o \
  $(BUILDDIR)/client/model.o \
  $(BUILDDIR)/client/net_chan.o \
  $(BUILDDIR)/client/net_udp.o \
  $(BUILDDIR)/client/nonintel.o \
  $(BUILDDIR)/client/pmove.o \
  $(BUILDDIR)/client/pmovetst.o \
  $(BUILDDIR)/client/r_aclip.o \
  $(BUILDDIR)/client/r_alias.o \
  $(BUILDDIR)/client/r_bsp.o \
  $(BUILDDIR)/client/r_draw.o \
  $(BUILDDIR)/client/r_edge.o \
  $(BUILDDIR)/client/r_efrag.o \
  $(BUILDDIR)/client/r_light.o \
  $(BUILDDIR)/client/r_main.o \
  $(BUILDDIR)/client/r_misc.o \
  $(BUILDDIR)/client/r_part.o \
  $(BUILDDIR)/client/r_sky.o \
  $(BUILDDIR)/client/r_sprite.o \
  $(BUILDDIR)/client/r_surf.o \
  $(BUILDDIR)/client/r_vars.o \
  $(BUILDDIR)/client/sbar.o \
  $(BUILDDIR)/client/screen.o \
  $(BUILDDIR)/client/skin.o \
  $(BUILDDIR)/client/snd_dma.o \
  $(BUILDDIR)/client/snd_mem.o \
  $(BUILDDIR)/client/snd_mix.o \
  $(BUILDDIR)/client/view.o \
  $(BUILDDIR)/client/wad.o \
  $(BUILDDIR)/client/zone.o \
  $(BUILDDIR)/client/cd_linux.o \
  $(BUILDDIR)/client/sys_linux.o \
  $(BUILDDIR)/client/snd_linux.o \

  QWCL_AS_OBJS = \
  $(BUILDDIR)/client/d_copy.o \
  $(BUILDDIR)/client/d_draw.o \
  $(BUILDDIR)/client/d_draw16.o \
  $(BUILDDIR)/client/d_parta.o \
  $(BUILDDIR)/client/d_polysa.o \
  $(BUILDDIR)/client/d_scana.o \
  $(BUILDDIR)/client/d_spr8.o \
  $(BUILDDIR)/client/d_varsa.o \
  $(BUILDDIR)/client/math.o \
  $(BUILDDIR)/client/r_aclipa.o \
  $(BUILDDIR)/client/r_aliasa.o \
  $(BUILDDIR)/client/r_drawa.o \
  $(BUILDDIR)/client/r_edgea.o \
  $(BUILDDIR)/client/r_varsa.o \
  $(BUILDDIR)/client/snd_mixa.o \
  $(BUILDDIR)/client/surf16.o \
  $(BUILDDIR)/client/surf8.o \
  $(BUILDDIR)/client/sys_dosa.o

QWCL_X11_OBJS = $(BUILDDIR)/client/vid_x.o

$(BUILDDIR)/qwcl.x11 : $(QWCL_OBJS) $(QWCL_AS_OBJS) $(QWCL_X11_OBJS)
  $(CC) $(CFLAGS) -o [email protected] $(QWCL_OBJS) $(QWCL_AS_OBJS) $(QWCL_X11_OBJS) \
    $(LDFLAGS) $(XLDFLAGS)

$(BUILDDIR)/client/cl_demo.o :        $(CLIENT_DIR)/cl_demo.c
  $(DO_CC)

$(BUILDDIR)/client/cl_ents.o :        $(CLIENT_DIR)/cl_ents.c
  $(DO_CC)

$(BUILDDIR)/client/cl_input.o :       $(CLIENT_DIR)/cl_input.c
  $(DO_CC)

$(BUILDDIR)/client/cl_main.o :        $(CLIENT_DIR)/cl_main.c
  $(DO_CC)

$(BUILDDIR)/client/cl_parse.o :       $(CLIENT_DIR)/cl_parse.c
  $(DO_CC)

$(BUILDDIR)/client/cl_pred.o :        $(CLIENT_DIR)/cl_pred.c
  $(DO_CC)

$(BUILDDIR)/client/cl_tent.o :        $(CLIENT_DIR)/cl_tent.c
  $(DO_CC)

$(BUILDDIR)/client/cl_cam.o :         $(CLIENT_DIR)/cl_cam.c
  $(DO_CC)

$(BUILDDIR)/client/cmd.o :            $(CLIENT_DIR)/cmd.c
  $(DO_CC)

$(BUILDDIR)/client/common.o :         $(CLIENT_DIR)/common.c
  $(DO_CC)

$(BUILDDIR)/client/console.o :        $(CLIENT_DIR)/console.c
  $(DO_CC)

$(BUILDDIR)/client/crc.o :            $(CLIENT_DIR)/crc.c
  $(DO_CC)

$(BUILDDIR)/client/cvar.o :           $(CLIENT_DIR)/cvar.c
  $(DO_CC)

$(BUILDDIR)/client/d_edge.o :         $(CLIENT_DIR)/d_edge.c
  $(DO_CC)

$(BUILDDIR)/client/d_fill.o :         $(CLIENT_DIR)/d_fill.c
  $(DO_CC)

$(BUILDDIR)/client/d_init.o :         $(CLIENT_DIR)/d_init.c
  $(DO_CC)

$(BUILDDIR)/client/d_modech.o :       $(CLIENT_DIR)/d_modech.c
  $(DO_CC)

$(BUILDDIR)/client/d_part.o :         $(CLIENT_DIR)/d_part.c
  $(DO_CC)

$(BUILDDIR)/client/d_polyse.o :       $(CLIENT_DIR)/d_polyse.c
  $(DO_CC)

$(BUILDDIR)/client/d_scan.o :         $(CLIENT_DIR)/d_scan.c
  $(DO_CC)

$(BUILDDIR)/client/d_sky.o :          $(CLIENT_DIR)/d_sky.c
  $(DO_CC)

$(BUILDDIR)/client/d_sprite.o :       $(CLIENT_DIR)/d_sprite.c
  $(DO_CC)

$(BUILDDIR)/client/d_surf.o :         $(CLIENT_DIR)/d_surf.c
  $(DO_CC)

$(BUILDDIR)/client/d_vars.o :         $(CLIENT_DIR)/d_vars.c
  $(DO_CC)

$(BUILDDIR)/client/d_zpoint.o :       $(CLIENT_DIR)/d_zpoint.c
  $(DO_CC)

$(BUILDDIR)/client/draw.o :           $(CLIENT_DIR)/draw.c
  $(DO_CC)

$(BUILDDIR)/client/keys.o :           $(CLIENT_DIR)/keys.c
  $(DO_CC)

$(BUILDDIR)/client/mathlib.o :        $(CLIENT_DIR)/mathlib.c
  $(DO_CC)

$(BUILDDIR)/client/md4.o :            $(CLIENT_DIR)/md4.c
  $(DO_CC)

$(BUILDDIR)/client/menu.o :           $(CLIENT_DIR)/menu.c
  $(DO_CC)

$(BUILDDIR)/client/model.o :          $(CLIENT_DIR)/model.c
  $(DO_CC)

$(BUILDDIR)/client/net_chan.o :       $(CLIENT_DIR)/net_chan.c
  $(DO_CC)

$(BUILDDIR)/client/net_udp.o :        $(CLIENT_DIR)/net_udp.c
  $(DO_CC)

$(BUILDDIR)/client/nonintel.o :       $(CLIENT_DIR)/nonintel.c
  $(DO_CC)

$(BUILDDIR)/client/pmove.o :          $(CLIENT_DIR)/pmove.c
  $(DO_CC)

$(BUILDDIR)/client/pmovetst.o :       $(CLIENT_DIR)/pmovetst.c
  $(DO_CC)

$(BUILDDIR)/client/r_aclip.o :        $(CLIENT_DIR)/r_aclip.c
  $(DO_CC)

$(BUILDDIR)/client/r_alias.o :        $(CLIENT_DIR)/r_alias.c
  $(DO_CC)

$(BUILDDIR)/client/r_bsp.o :          $(CLIENT_DIR)/r_bsp.c
  $(DO_CC)

$(BUILDDIR)/client/r_draw.o :         $(CLIENT_DIR)/r_draw.c
  $(DO_CC)

$(BUILDDIR)/client/r_edge.o :         $(CLIENT_DIR)/r_edge.c
  $(DO_CC)

$(BUILDDIR)/client/r_efrag.o :        $(CLIENT_DIR)/r_efrag.c
  $(DO_CC)

$(BUILDDIR)/client/r_light.o :        $(CLIENT_DIR)/r_light.c
  $(DO_CC)

$(BUILDDIR)/client/r_main.o :         $(CLIENT_DIR)/r_main.c
  $(DO_CC)

$(BUILDDIR)/client/r_misc.o :         $(CLIENT_DIR)/r_misc.c
  $(DO_CC)

$(BUILDDIR)/client/r_part.o :         $(CLIENT_DIR)/r_part.c
  $(DO_CC)

$(BUILDDIR)/client/r_sky.o :          $(CLIENT_DIR)/r_sky.c
  $(DO_CC)

$(BUILDDIR)/client/r_sprite.o :       $(CLIENT_DIR)/r_sprite.c
  $(DO_CC)

$(BUILDDIR)/client/r_surf.o :         $(CLIENT_DIR)/r_surf.c
  $(DO_CC)

$(BUILDDIR)/client/r_vars.o :         $(CLIENT_DIR)/r_vars.c
  $(DO_CC)

$(BUILDDIR)/client/sbar.o :           $(CLIENT_DIR)/sbar.c
  $(DO_CC)

$(BUILDDIR)/client/screen.o :         $(CLIENT_DIR)/screen.c
  $(DO_CC)

$(BUILDDIR)/client/skin.o :           $(CLIENT_DIR)/skin.c
  $(DO_CC)

$(BUILDDIR)/client/snd_dma.o :        $(CLIENT_DIR)/snd_dma.c
  $(DO_CC)

$(BUILDDIR)/client/snd_mem.o :        $(CLIENT_DIR)/snd_mem.c
  $(DO_CC)

$(BUILDDIR)/client/snd_mix.o :        $(CLIENT_DIR)/snd_mix.c
  $(DO_CC)

$(BUILDDIR)/client/view.o :           $(CLIENT_DIR)/view.c
  $(DO_CC)

$(BUILDDIR)/client/wad.o :            $(CLIENT_DIR)/wad.c
  $(DO_CC)

$(BUILDDIR)/client/zone.o :           $(CLIENT_DIR)/zone.c
  $(DO_CC)

$(BUILDDIR)/client/cd_linux.o :       $(CLIENT_DIR)/cd_linux.c
  $(DO_CC)

$(BUILDDIR)/client/sys_linux.o :      $(CLIENT_DIR)/sys_linux.c
  $(DO_CC)

$(BUILDDIR)/client/snd_linux.o :      $(CLIENT_DIR)/snd_linux.c
  $(DO_CC)

$(BUILDDIR)/client/d_copy.o :         $(CLIENT_DIR)/d_copy.s
  $(DO_AS)

$(BUILDDIR)/client/d_draw.o :         $(CLIENT_DIR)/d_draw.s
  $(DO_AS)

$(BUILDDIR)/client/d_draw16.o :       $(CLIENT_DIR)/d_draw16.s
  $(DO_AS)

$(BUILDDIR)/client/d_parta.o :        $(CLIENT_DIR)/d_parta.s
  $(DO_AS)

$(BUILDDIR)/client/d_polysa.o :       $(CLIENT_DIR)/d_polysa.s
  $(DO_AS)

$(BUILDDIR)/client/d_scana.o :        $(CLIENT_DIR)/d_scana.s
  $(DO_AS)

$(BUILDDIR)/client/d_spr8.o :         $(CLIENT_DIR)/d_spr8.s
  $(DO_AS)

$(BUILDDIR)/client/d_varsa.o :        $(CLIENT_DIR)/d_varsa.s
  $(DO_AS)

$(BUILDDIR)/client/math.o :           $(CLIENT_DIR)/math.s
  $(DO_AS)

$(BUILDDIR)/client/r_aclipa.o :       $(CLIENT_DIR)/r_aclipa.s
  $(DO_AS)

$(BUILDDIR)/client/r_aliasa.o :       $(CLIENT_DIR)/r_aliasa.s
  $(DO_AS)

$(BUILDDIR)/client/r_drawa.o :        $(CLIENT_DIR)/r_drawa.s
  $(DO_AS)

$(BUILDDIR)/client/r_edgea.o :        $(CLIENT_DIR)/r_edgea.s
  $(DO_AS)

$(BUILDDIR)/client/r_varsa.o :        $(CLIENT_DIR)/r_varsa.s
  $(DO_AS)

$(BUILDDIR)/client/snd_mixa.o :       $(CLIENT_DIR)/snd_mixa.s
  $(DO_AS)

$(BUILDDIR)/client/surf16.o :         $(CLIENT_DIR)/surf16.s
  $(DO_AS)

$(BUILDDIR)/client/surf8.o :          $(CLIENT_DIR)/surf8.s
  $(DO_AS)

$(BUILDDIR)/client/sys_dosa.o :       $(CLIENT_DIR)/sys_dosa.s
  $(DO_AS)

$(BUILDDIR)/client/vid_x.o : $(CLIENT_DIR)/vid_x.c
  $(DO_CC)

#############################################################################
# GL CLIENT
#############################################################################

GLQWCL_OBJS = \
  $(BUILDDIR)/glclient/cl_demo.o \
  $(BUILDDIR)/glclient/cl_ents.o \
  $(BUILDDIR)/glclient/cl_input.o \
  $(BUILDDIR)/glclient/cl_main.o \
  $(BUILDDIR)/glclient/cl_parse.o \
  $(BUILDDIR)/glclient/cl_pred.o \
  $(BUILDDIR)/glclient/cl_tent.o \
  $(BUILDDIR)/glclient/cl_cam.o \
  $(BUILDDIR)/glclient/cmd.o \
  $(BUILDDIR)/glclient/common.o \
  $(BUILDDIR)/glclient/console.o \
  $(BUILDDIR)/glclient/crc.o \
  $(BUILDDIR)/glclient/cvar.o \
  $(BUILDDIR)/glclient/keys.o \
  $(BUILDDIR)/glclient/mathlib.o \
  $(BUILDDIR)/glclient/md4.o \
  $(BUILDDIR)/glclient/menu.o \
  $(BUILDDIR)/glclient/net_chan.o \
  $(BUILDDIR)/glclient/net_udp.o \
  $(BUILDDIR)/glclient/nonintel.o \
  $(BUILDDIR)/glclient/pmove.o \
  $(BUILDDIR)/glclient/pmovetst.o \
  $(BUILDDIR)/glclient/r_part.o \
  $(BUILDDIR)/glclient/sbar.o \
  $(BUILDDIR)/glclient/skin.o \
  $(BUILDDIR)/glclient/snd_dma.o \
  $(BUILDDIR)/glclient/snd_mem.o \
  $(BUILDDIR)/glclient/snd_mix.o \
  $(BUILDDIR)/glclient/view.o \
  $(BUILDDIR)/glclient/wad.o \
  $(BUILDDIR)/glclient/zone.o \
  $(BUILDDIR)/glclient/cd_linux.o \
  $(BUILDDIR)/glclient/sys_linux.o \
  $(BUILDDIR)/glclient/snd_linux.o \
  \
  $(BUILDDIR)/glclient/gl_draw.o \
  $(BUILDDIR)/glclient/gl_mesh.o \
  $(BUILDDIR)/glclient/gl_model.o \
  $(BUILDDIR)/glclient/gl_ngraph.o \
  $(BUILDDIR)/glclient/gl_refrag.o \
  $(BUILDDIR)/glclient/gl_rlight.o \
  $(BUILDDIR)/glclient/gl_rmain.o \
  $(BUILDDIR)/glclient/gl_rmisc.o \
  $(BUILDDIR)/glclient/gl_rsurf.o \
  $(BUILDDIR)/glclient/gl_screen.o \
  $(BUILDDIR)/glclient/gl_warp.o \
  \
  $(BUILDDIR)/glclient/math.o \
  $(BUILDDIR)/glclient/snd_mixa.o \
  $(BUILDDIR)/glclient/sys_dosa.o

GLQWCL_X11_OBJS = $(BUILDDIR)/glclient/gl_vidlinuxglx.o

$(BUILDDIR)/glqwcl.glx : $(GLQWCL_OBJS) $(GLQWCL_X11_OBJS)
  $(CC) $(CFLAGS) -o [email protected] $(GLQWCL_OBJS) $(GLQWCL_X11_OBJS) $(LDFLAGS) $(GL_X11_LDFLAGS)

$(BUILDDIR)/glclient/cl_demo.o :       $(CLIENT_DIR)/cl_demo.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_ents.o :       $(CLIENT_DIR)/cl_ents.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_input.o :      $(CLIENT_DIR)/cl_input.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_main.o :       $(CLIENT_DIR)/cl_main.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_parse.o :      $(CLIENT_DIR)/cl_parse.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_pred.o :       $(CLIENT_DIR)/cl_pred.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_tent.o :       $(CLIENT_DIR)/cl_tent.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cl_cam.o :        $(CLIENT_DIR)/cl_cam.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cmd.o :           $(CLIENT_DIR)/cmd.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/common.o :        $(CLIENT_DIR)/common.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/console.o :       $(CLIENT_DIR)/console.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/crc.o :           $(CLIENT_DIR)/crc.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cvar.o :          $(CLIENT_DIR)/cvar.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/keys.o :          $(CLIENT_DIR)/keys.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/mathlib.o :       $(CLIENT_DIR)/mathlib.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/md4.o :           $(CLIENT_DIR)/md4.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/menu.o :          $(CLIENT_DIR)/menu.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/net_chan.o :      $(CLIENT_DIR)/net_chan.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/net_udp.o :       $(CLIENT_DIR)/net_udp.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/nonintel.o :      $(CLIENT_DIR)/nonintel.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/pmove.o :         $(CLIENT_DIR)/pmove.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/pmovetst.o :      $(CLIENT_DIR)/pmovetst.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/r_part.o :        $(CLIENT_DIR)/r_part.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/sbar.o :          $(CLIENT_DIR)/sbar.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/screen.o :        $(CLIENT_DIR)/screen.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/skin.o :          $(CLIENT_DIR)/skin.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/snd_dma.o :       $(CLIENT_DIR)/snd_dma.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/snd_mem.o :       $(CLIENT_DIR)/snd_mem.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/snd_mix.o :       $(CLIENT_DIR)/snd_mix.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/view.o :          $(CLIENT_DIR)/view.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/wad.o :           $(CLIENT_DIR)/wad.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/zone.o :          $(CLIENT_DIR)/zone.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/cd_linux.o :      $(CLIENT_DIR)/cd_linux.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/sys_linux.o :     $(CLIENT_DIR)/sys_linux.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/snd_linux.o :     $(CLIENT_DIR)/snd_linux.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_draw.o :       $(CLIENT_DIR)/gl_draw.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_mesh.o :       $(CLIENT_DIR)/gl_mesh.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_model.o :      $(CLIENT_DIR)/gl_model.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_ngraph.o :     $(CLIENT_DIR)/gl_ngraph.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_refrag.o :     $(CLIENT_DIR)/gl_refrag.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_rlight.o :     $(CLIENT_DIR)/gl_rlight.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_rmain.o :      $(CLIENT_DIR)/gl_rmain.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_rmisc.o :      $(CLIENT_DIR)/gl_rmisc.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_rsurf.o :      $(CLIENT_DIR)/gl_rsurf.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_screen.o :     $(CLIENT_DIR)/gl_screen.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_vidlinux.o :   $(CLIENT_DIR)/gl_vidlinux.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_vidlinuxglx.o :   $(CLIENT_DIR)/gl_vidlinuxglx.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/gl_warp.o :       $(CLIENT_DIR)/gl_warp.c
  $(DO_GL_CC)

$(BUILDDIR)/glclient/math.o :          $(CLIENT_DIR)/math.s
  $(DO_GL_AS)

$(BUILDDIR)/glclient/snd_mixa.o :      $(CLIENT_DIR)/snd_mixa.s
  $(DO_GL_AS)

$(BUILDDIR)/glclient/sys_dosa.o :      $(CLIENT_DIR)/sys_dosa.s
  $(DO_GL_AS)

#############################################################################
# MISC
#############################################################################

clean:
  rm -fr bin/client
  rm -fr bin/glclient
  rm -fr bin/server

With that saved into a file, it is time to build.  Unzip the source code into a directory, and then from the QW directory run the new Makefile.  If your GCC is new enough it’ll complain about the -m486 directive.  Just remove that from the Makefile.  On my bare build system it compiles the server in a few seconds, but then fails to build the GLQuake client because I don’t have any OpenGL installed.  But again this is fine, I just want the server.

gcc -Wall -Dstricmp=strcasecmp -I./client -I./server -ffast-math -fexpensive-optimizations -o bin/qwsv bin/server/pr_cmds.o bin/server/pr_edict.o bin/server/pr_exec.o bin/server/sv_init.o bin/server/sv_main.o bin/server/sv_nchan.o bin/server/sv_ents.o bin/server/sv_send.o bin/server/sv_move.o bin/server/sv_phys.o bin/server/sv_user.o bin/server/sv_ccmds.o bin/server/world.o bin/server/sys_unix.o bin/server/model.o bin/server/cmd.o bin/server/common.o bin/server/crc.o bin/server/cvar.o bin/server/mathlib.o bin/server/md4.o bin/server/zone.o bin/server/pmove.o bin/server/pmovetst.o bin/server/net_chan.o bin/server/net_udp.o -lm
gcc -Wall -Dstricmp=strcasecmp -I./client -I./server -ffast-math -fexpensive-optimizations -DGLQUAKE -DGL_EXT_SHARED -o bin/glclient/cl_demo.o -c client/cl_demo.c
In file included from client/quakedef.h:76:0,
from client/cl_demo.c:21:
client/glquake.h:22:0: warning: ignoring #pragma warning [-Wunknown-pragmas]
client/glquake.h:23:0: warning: ignoring #pragma warning [-Wunknown-pragmas]
client/glquake.h:24:0: warning: ignoring #pragma warning [-Wunknown-pragmas]
client/glquake.h:30:19: fatal error: GL/gl.h: No such file or directory
compilation terminated.
make[1]: *** [bin/glclient/cl_demo.o] Error 1
make[1]: Leaving directory `/root/QW’
make: *** [build_release] Error 2
[email protected]:~/QW#

And to verify the server:

[email protected]:~/QW# file bin/qwsv
bin/qwsv: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.26, BuildID[sha1]=0xa074d1a1c7b941da5e3a774eebc70f969c3347c7, not stripped

Ok, all good!

Now for running the server, I place my qwsv binary into somewhere more ‘server’ like say /usr/local/quake

[email protected]:~/QW# mkdir /usr/local/quake
[email protected]:~/QW# cp bin/qwsv /usr/local/quake/

The next part is to copy in the pak0.pak & pak1.pak from your registered copy of Quake 1 into /usr/local/quake/id1.  You can always buy it on steam, although it is so old I’m sure you’ve already acquired a copy or two in the last few years.  Note that pak0.pak is the shareware portion of the datafile!  To be a server you require pak1.pak from the registered copy of Quake.  Although the source to the maps has been released, I’m pretty sure you will be missing all kinds of otherthings from pak1.pak.

Ok now with the pak’s in place, you need the QuakeWorld scripts in place.  Copy the ‘progs’ directory into the qw directory

[email protected]:/usr/local/quake# mkdir qw
[email protected]:/usr/local/quake# cp $HOME/QW/progs/* qw

Now with all of this in place it is enough to run the server!

[email protected]:/usr/local/quake# ./qwsv
Added packfile ./id1/pak0.pak (339 files)
Added packfile ./id1/pak1.pak (85 files)
PackFile: ./id1/pak1.pak : gfx/pop.lmp
Playing registered version.
IP address 127.0.1.1:27500
UDP Initialized
Exe: 20:05:14 Nov 18 2013
16.0 megabyte heap

Server Version 2.40 (Build 5451)

======== QuakeWorld Initialized ========
FindFile: can’t find server.cfg
couldn’t exec server.cfg
PackFile: ./id1/pak0.pak : maps/start.bsp
FindFile: ./qw/qwprogs.dat
PackFile: ./id1/pak0.pak : maps/start.bsp
Building PHS…
Average leafs visible / hearable / total: 111 / 239 / 1128
PackFile: ./id1/pak0.pak : progs/player.mdl
PackFile: ./id1/pak0.pak : progs/eyes.mdl
Updated needpass.

However you probably will want a server.cfg.  And incase the page goes offline here is a pretty basic config:

sv_gamedir qw
deathmatch 1
hostname  ”Your Quake DM Server”
serverinfo admin  ”[email protected]
serverinfo url “http://www.yourwebsite.com”
rcon_password change_me
timelimit 10
fraglimit 20
pausable 0
samelevel 2
maxclients 16
map dm1
floodprot 4 8 30
floodprotmsg “You have activated the flood protection and will be silenced for 30 seconds”
maxspectators 2
allow_download 1
allow_download_skins 1
allow_download_models 1
allow_download_sounds 1
allow_download_maps 1

Naturally you may want to change things.  Save the config file into the id1 directory.

Now when it comes to running the server, the console will want to be interactive to the user.  If you nohup the process it’ll go bezerk. The best way to do this is with screen.  In my /etc/rc.local I add the following line:

screen -dmS quake /usr/local/quake/quake.sh

And of course the shell script is:

#!/bin/bash
cd /usr/local/quake
while true
do true
./qwsv
done

Very simple stuff.  If the server crashes or stops for any reason it’ll restart.

By default QuakeWorld will listen on UDP port 27500.  The next thing you’ll need is a client to test.  I’m kind of partial to the MS-DOS (qemu) and OS/2 clients.

QuakeWorld in action!
QuakeWorld in action!

And there we go, now you too can host your own QuakeWorld server.  And for those who can’t compiler, you can try my x86 binary here.

You may want to add your server to gametracker.com.  Or just join mine.

Yet another update for QuakeWorld/Quake for MS-DOS

You can download the source/binaries here.

The big change is that you can not play larger maps, like the dreaded Warp Spasm (which of course relies on QUOTH).

Warp Spasam under OS/2

I’ve also built it for OS/2! You can download Quake, Quake World, and the source code. This all builds with EMX 0.9d and I’m using OS/2 2.0 (patched to x06100).

Also if it matters the newer your MS-DOS the better… 4.01 works, sure but its SLOW…

The final version of Flash for PPC OS X

It’s version 10.1.102.64 … And I’ve stashed a copy here.  The OS X version is already up to 10.3 while Windows is at 11.1.102.62… Oh well.

But the best part is that this being flash 10, is that it can run C++ programs like say quake!

xxx
Flash Quake on PPC OS X

Sure its all obsolete but it’s still pretty snazzy.