Main
- Homepage
- Sitemap

Shareware
- ProgramBar

Games
- Quake 3 Arena
- Theme Hospital
Quake 3
- What's new
- Compiler porting
- Source+Borland
- Downloads

E-mail
Last updated:
09:53 04-Feb-2000

Copyright 2000
Ian Jefferies
All rights reserved
E-mail webmaster
 

HypoThermia's Q3 Source tools for Borland compilers

 

So what is it?

A collection of files that allow you to build the Quake 3 Source as a Windows DLL using the Borland compiler tools.

I don't have access to a Microsoft compiler so I tried to compile the source code using Borland C++ v5.01. The problems that I ran into were "frustrating", but once identified were easily fixed. I'm releasing those fixes so others can use Borland tools to produce mods, without having to repeat what I had to do.

These tools have only been tested and used on Borland C++ v5.01. Information on whether they work on Borland C++ Builder would be appreciated.
 
  • Some questions answered
  • What changes were required
  • Download the tools and IDE (35K)
  • Read the readme.txt file

  •  

    Some questions answered

    These questions and answers are taken from the readme.txt file in the archive. They provide a quick answer to most things in an easily digestible form.

    They overlap somewhat with the explanation of the changes that were required.

  • When compiling the .qvm files using the batch files I get warning messages. What gives?
  • Can I build the DLLs into another directory?
  • Why create the \borland directory?
  • What is borland_hack.c for?
  • What are the DEF files for?
  • Can I get some more performance out of these DLLs?
  • Why are the QVM files a different size to those distributed by id?
  • How do I change the directory the QVM files are built to?
  • What's with all those -Dxxxxx things in compile.bat?
  • How do I debug using these DLLs?
  • Top of page
     

    Q: When compiling the .qvm files using the batch files I get warning messages. What gives?

    These error messages are caused by "issues" within either the Borland header files or the Q3 source.

    limits.h:31 Character constant taken as not signed

    The header file is intended to detect whether the data type char is signed
    or unsigned.

    ui/ui_ingame.c:103 warning: Conversion of 'pointer to void'...
    ui/ui_ingame.c:107
    ui/ui_atoms.c:739
    ui/ui_atoms.c:742

    Compiler is warning of a non-portable pointer conversion. Not a problem with BC++ or the Quake VM.

    Anything else is something you've introduced!

    Back to Some Questions answered
    Top of Page
     

    Q: Can I build the DLLs into another directory?

    Yes! Open the project options dialog Options|Project... and select Directories. Change the final output directory to the absolute/relative path you want. e.g. changing to ..\ compiles directly to the Quake3 directory. Remember: paths are relative to \Quake3\source.

    Back to Some Questions answered
    Top of Page
     

    Q: Why create the \borland directory?

    It keeps the modifications that I've had to make to the supplied files away from the source distribution as much as possible.

    It also invites others to work in the same way; and for Borland, Microsoft, and other compiler tools to co-exist side by side and work with the same code source.

    Back to Some Questions answered
    Top of Page
     

    Q: What is borland_hack.c for?

    When running cgamex86.dll there was the occasional hang that wase cleared by pressing ESC twice. It was always reproducable in the same places by running a timedemo of demo001.dm3.

    What was happening was that the math library was generating an error dialog. borland_hack.c traps the error before the dialog is shown and "fixes" it by, effectively, ignoring the problem.

    The problem was created after a call into trap_CM_BoxTrace() returned a null vector when a unit vector was expected. It only appears to occur in the CG_PlayerShadow() function.

    A bug report has been sent to id.

    It appears that the Microsoft based builds of this .dll pass over this error silently.

    Back to Some Questions answered
    Top of Page
     

    Q: What are the DEF files for?

    The Borland compiler places an underscore (_) in front of every name generated that uses the C calling convention. The new .def files rename these exported functions to remove the underscore so Q3 can bind to the DLLs.

    Back to Some Questions answered
    Top of Page
     

    Q: Can I get some more performance out of these DLLs?

    If you have TASM try removing the C_ONLY define in the Compiler|Defines of the project options. I don't have TASM so this is untested.

    Compile using the Intel optimizing compiler, and use Pentium scheduling. Turbo Debugger doesn't work too well on these builds because of the way the Intel compiler moves code around.

    Back to Some Questions answered
    Top of Page
     

    Q: Why are the QVM files a different size to those distributed by id?

    I suspect that this is caused by differences in the implementation of the header files by Microsoft and Borland. Presumably static data is included in the QVM files, and if the Microsoft headers use more static data then
    this will be reflected in the size of the QVM. 

    Back to Some Questions answered
    Top of Page
     

    Q: How do I change the directory the QVM files are built to?

    Modify the cgame.q3asm, game.q3asm and ui.q3asm files. In each case the first line (beginning with -o) contains the destination directory of the QVM files.

    Back to Some Questions answered
    Top of Page
     

    Q: What's with all those -Dxxxxx things in compile.bat

    They pass defined constants to lcc.exe, the equivalent to #define xxxxx in C source code or header files.

    Borland header files use predefined constants that match the requirements of the compiling model. These are all defined in <_defs.h>, so that file is bypassed by defining ___DEFS_H and appropriate definitions are supplied instead.

    Back to Some Questions answered
    Top of Page
     

    Q: How do I debug using these DLLs?

    This is not straight forward.

    Quake3 is a demanding executable, any number of problems can arise depending on the implementation and stability of your sound drivers, GL drivers etc.

    I have found that the following works most of the time:

    1) Compile your DLL's and move them to the \Quake3 directory.

    2) Open a windowed DOS box in your Quake3 directory.

    3) Run Turbo Debugger, TD32.EXE QUAKE3.EXE

    4) Quake 3 should start OK, if not jump to step 7.

    5) If your .dll's are crashing then generate the event that causes the crash.  The machine should appear to lock solid. Press Alt-Enter to get to the debugger.

    6) If Q3 locks during startup then try Alt-Enter to get to the debugger. Quit the debugger.

    7) Do a full reboot of the machine to avoid any instability.

    Any enhancements/improvements to this method are invited, particularly on how to attach to DLL's loaded using LoadLibrary().

    To generate your own crash deliberately insert the code:

    *(long*)(-4)=0;

    This is a one time only crash, and can't be recovered from.

    Back to Some Questions answered
    Top of Page
     


     

    What changes were required?

    Overview

    The changes fell into several areas: in the Q3 Source itself, the compiling of the bytecode for the virtual machine, Borland header files, the building of the DLL using Borland TLINK, and handling a bug in the Q3 engine.

    Id Software wrote the Q3 Source using portable C, restricting the machine specific changes to only a few header files. This is a Good Thing(tm): it makes so it much easier to port the code to another compiler.

    The changes listed here are dry and of a techincal nature. They'll probably only interest those porting to other compilers... or masochists.
     
  • Changes to the Q3 Source
  • Compiling the bytecode for the virtual machine
  • Building the DLL using Borland TLINK
  • Handling a bug in the Q3 engine

  • Top of page
     
     

    Changes to Q3 Source

    Q3 Source required only one change: actually caused by the use of Borland header files.

    Open the header file game\q_shared.h and move to line 424, it should be:

    float Q_crandom(int* seed);

    Insert after it the following:

    #ifdef __BORLANDC__
    #ifdef random
    #undef random
    #endif
    #endif

    The Borland header files have defined the macro random() already. To remove the possibility of a clash with the definition of random() that follows it is undef'd here. The error is also flagged when compiling lcc.exe, and a clear, unambiguous, and correct definition of random() is required.

    The file game\q_shared.h contains most or all of the compiler specific flags so I have no qualms in modifying it.

    If a new Q3 source is added or reinstalled then this modification will have to be applied again.

    Back to changes required
    Top of page
     
     

    Compiling the bytecode for the virtual machine

    These changes were driven by two things: putting these tools in their own directory, and getting the compiler lcc.exe working with Borland header files.

    The changes required to get the Borland headers accepted by lcc.exe are aimed at making the header files look like ANSI C.

    The bytecode is compiled using game.bat, cgame.bat, ui.bat, and compile.bat. The batch file compile.bat is called by the others to convert each C source file into intermediate assembler. Changes were made so that all batch files and executables were accessed by relative path only.

    Changes were made within compile.bat so that the Borland header files could be parsed by lcc.exe. A typical header file contains a definition like:

    int _RTLENTRYF atoi(const char _FAR *__s);

    The file declaring _RTLENTRYF et. al. is excluded (all definitions of this type occur in <_defs.h>, so ___DEFS_H was defined). Substitute definitions were then provided in the form -Dxxxxx.

    The compiler constants __FLAT__ and __BORLANDC__ were also defined to control the precompilers path through the headers.

    The include directory for the header files was also added, this avoided setting an environment variable.

    Back to changes required
    Top of page
     
     

    Building the DLL using Borland TLINK

    By default the Borland compiler prepends an underscore ("_") to all functions built using the C calling convention. New .DEF files are provided to map the exported function names _vmMain and _dllentry to those called by quake3.exe and the naming convention used by Microsoft compilers.

    Back to changes required
    Top of page
     
     

    Handling a bug in the Q3 engine

    Once I'd built the DLLs I tested them and found that the game halted when playing demo001.dm3 with "timedemo 1". Pressing ESC twice cleared the error, but it repeated again several times before the demo had finished. On each occasion the halt occurred in the same place. The halt was caused by a dialog box being created to report a math error (sqrt:DOMAIN), although it wasn't visible on screen. 

    Borland libraries generate an error dialog if no math error handler is installed. Installing a math error handler and ignoring sqrt:DOMAIN errors makes the problem go away. The fix can be found in borland_hack.c.

    Tracking the error back in the source code, the cause lay in VectorNormalize() trying to process {-NAN,-NAN,-NAN}. It appears that, when trying to draw an entities shadow through CG_PlayerShadow(), the direction of a surface normal had been set to {0,0,0}.

    As this vector is obtained by a syscall() into quake3.exe through trap_CM_BoxTrace() I can't take this any further. A tentative diagnosis is that the co-ordinate of the shadow has been found to fall onto the boundary between two surfaces separated by a vertical surface (e.g. between an upper and lower step). This would be consistent with the observed position at which the error dialog was generated, halting the game.

    I've reported this bug to Id. Microsoft libraries appear to ignore it quietly judging by the builds of these DLLs I've seen!

    Back to changes required
    Top of page