ARTICLE 4 - Compiling without Microsoft Visual C++ (part I)
This article is aimed at the Quake3 Arena mod developer who
can't or won't work with Microsoft Visual C++. In addition it helps
lift the restriction that all coders working on a project use the same
tools in the same environment.
You'll need the Quake3 Source and the tools that go with it to get started.
Summarized here is the experience I had in building the Q3Source
for the Borland compilers. Hopefully you can use this to create
the tools required to develop Q3Source using the compiler you're
I'd encouraged you to base your compiler solution in a new directory under
Examples are provided based on the work I did. There's no substitute
for real world experience.
Code3Arena will act as a focus for providing compiler
solutions for building Q3Source. Mod makers can then choose the
platform and compiler they want to work on.
Contact me if you do make a successful port to another compiler. If you have
any questions about porting to another compiler then I'll
try to help. No binaries or attachments over 50K without my permission
Sit back, kick off your shoes, and try not to think of cheese!
The Quake3 Source released by id Software builds part
of the game code, allowing dedicated enthusiasts to add to and improve
3 for the gaming community at large. As the game already ships on several
platforms it's clear that the Q3Source needed to be as platform
independent as possible. It was written using portable ANSI C and compiled
to a bytecode that runs on any machine with a Q3 executable.
Mod makers can finally develop game enhancements: on a single platform,
for multiple platforms.
One beneficial side effect of using portable ANSI C is that a large
number of programmers are already familiar with C as a programming language.
The other main benefit is the ability to build binaries that only work
on the one platform for development and debugging.
2. The objectives
The objective can be split into three parts:
When you release your work for others to use it should make the minimal
number of changes to the Q3Source installation. Preferably you should
provide your own batch files or scripts, making sure they don't overwrite
the ones supplied with Q3Source. Id Software might release
an updated source, overwriting your files. Ideally a mod developer should
only need to re-apply your necessary changes to the Q3Source
Building the bytecode for use with Quake3 (and redistribution)
Building binaries that can be tested and debugged on your system
Releasing your work for others to use
Read 'Distribute your project' - in
the second part of the article -
for ideas on how to do this so you can start as you mean to go on.
In the first two cases you'll be using the header files supplied with
your compiler. The main issue for the bytecode is making your headers look
like ANSI C. See '4. Getting started on the bytecode'
for more information.
Building binaries for your system needs an understanding of how to modify
portable code in a way that keeps it portable. In other words, another
compiler should be able to use the Q3Source code you've modifed
without running into problems of its own. See 'Compiling the binaries'
in part 2 of this article for details.
3. Getting started on the bytecode
The bytecode that runs on the Quake3 Virtual Machine (QVM) is platform
independent. It's compiled using lcc.exe, a tool supplied by id
Software, and will use the header files from your compiler. Each of
the compiled files is then assembled and linked using q3asm.exe.
There are three separate QVM files you'll be compiling:
||Contains the code needed to
run a game server. In single player this also controls the bot AI.
||Handles the events and
screen display on your local (client) machine.
||Provides the User Interface
and menu front end to the single player game.
To get the bytecode to compile you'll need to work out how to make your
header files appear as platform independent ANSI C.
4. Automating compilation using scripts
Provided as part of Q3Source are 4 batch files that run from the DOS prompt.
Three of the files are concerned with building each of bytecode modules
qagame, cgame,and ui, called game.bat, cgame.bat and ui.bat.
Each calls the fourth batch file compile.bat with the location
of a source file needed to build that QVM module.
Once compiled, the last job of each script is to assemble and link the
files to make the distributable. q3asm.exe uses a response file
for each module: game.q3asm, cgame.q3asm and ui.q3asm.
Copy these files to your compiler directory under Quake3\source.
Noticing that they actually do their work in a subdirectory called vm,
modify them in the following way:
If you run each of there files they should now *try* to compile the source,
bombing out with an error about not finding some header files.
Adapt the batch file to work using the script language on your system,
making sure that you still use the compile script to call lcc.exe.
change the relative paths to each of the source files
change the relative paths to the executables lcc.exe and q3asm.exe,
add them to your executable path (document this!)
change the relative paths to ..\cgame, ..\game, and ..\ui
in each of the .q3asm files: modify the path to cg_syscalls, ui_syscalls or g_syscalls
only, so it uses the right .asm file in the Q3Source subdirectories
cgame, ui, and game repectively.
5. Understanding your header files
From now on the changes you need to make are in the compile script
The first modification to compile is to tell it where your header files
are. Make sure the following argument is passed to lcc.exe:
-I<path to header files>
where <path to header files> is an absolute path correct
for your system.
Now that your header files can be found you'll start running into platform
and compiler specific issues. Most of these should be solved by passing
the equivalent of a #define xxxxxx to lcc.exe. You can
do this by adding the argument -Dxxxxxx or -Dxxxxxx=""
in the compile script.
If your header files can be used on more than one compilation model
then you need to work out a path through them that gives a "pure" ANSI
C definition of all functions. One way to do this is find the header file
that defines the compiler specific information, bypass it, and supply your
own definitions to lcc.exe.
You may also have to define some compiler specific values to help control
the route through the header files. Check that these aren't used in the
Q3Source already, and if they are that they won't cause problems.
Borland C++ uses header files that can build
for executables or DLLs in Win32, Win16, and executables for DOS in 6 different
I chose to force the Win32 executable path onto the Borland
header files by defining __FLAT__. I had to avoid the Win32 references
in Q3Source (we're not compiling for that platform!) so I didn't
define WIN32, _WIN32 or __WIN32__. These variables
are defined and used by either Borland or Microsoft tools.
I also defined the compiler specific value __BORLANDC__, further
controlling the route through the headers.
With these defined I started getting errors from lcc.exe about
and similar constants, so I had to remove the header file that supplied
these definitions <_defs.h> (defining ___DEFS_H did
this as the header was protected from repeated inclusion by this value).
A typical Borland definition looks like:
int _RTLENTRYF atoi(const char _FAR *__s);
and applying these definitions reduced it to:
int atoi(const char *__s);
6. Keeping the code portable (and how to make necessary changes)
Avoid making modifications to the Q3Source, unless you can absolutely
have to. Try and make changes through the command line options in the compile
When you have make modifications to Q3Source, do so after you've
determined that the use of -Dxxxxxx can't solve your problem.
Make the changes so that they are controlled by a constant defined only
by your compiler, make sure this constant is defined in the compile
script as well.
Make your necessary changes to files that already have a compiler specific
component in them (game\q_shared.h for example). You should only
need to touch a few header files.
If you need to edit a C source file, think again! Look at
'Expected errors', there should be no need to fix these.
If a route through the Q3Source header files is already available
for your platform, but you're using a different compiler, then take advantage
Document your changes and allow the recipient of your work to incorporate
them, understanding the benefit themselves. Remember: you're helping people
who are already exerienced C coders.
Using the Borland header files there was a clash with the definition
of random(). As the Q3Source definition needed to take
precedence, the following code was inserted:
#if (defined __BORLANDC__ && defined random)
and was added after game\q_shared.h line 424.
Notice that this also works when building binaries, as the same problem
The only other change I had to make to a source file was in a Borland
header file. lcc.exe was generating an error while parsing a #error
directive (even though it wasn't executed). The change made to the Borland
header put the error message into quotes.
#error "Can't include both STDARG.H and VARARGS.H"
in <stdarg.h> line 20.
7. Expected errors
Despite the portablilty of the Q3Source, warnings are generated
by the source. You might also get a few warnings from your header files.
Work out why and decide whether any change is needed in your header files.
"Warning: Conversion of 'pointer to void'... ...is compiler
Not a problem for the QVM, though it might be for your compiler.
You might also find that your bytecode modules are of a different size
to those released with Quake3. This is probably caused by differences
between how the header files use static data for their implementation.
The Borland header files generate another warning:
limits.h:31 Character constant taken as not signed
Paradoxically this is warning about the method employed by Borland
to find out if a char type is signed or unsigned. It can be ignored.
8. Testing the bytecode
Follow the instructions in Tutorial#3 for modifying
the source code to produce the "Slow rocket mod". Don't forget to change
Try playing back the demos using "timedemo 1". This is not
a performance test, but ensures that the same frames are drawn each time.
Play a few games against bots by compiling the bytecode into a directory
other than baseq3. With or without the slow rocket mod!
Go on! You've earned it!
9. Continuing the good work
With the compiled QVM's under your belt the next thing to look at is getting
the code compiled to produce binaries. This is covered in the second part to the article,
as well as some suggestions on how to organize your work if you want to distribute it.