Installing LCC-Win32 and compiling

This section will explain how to install and set up LCC-Win32, and how to compile ID software's gamex86.dll source code for use with Quake2.

Initial steps

Important: Make sure you're using the FULL version of Quake2! You cannot use custom DLL's with the Q2Test release!

First make sure you have the latest version of LCC-Win32, released on the 4th of December. If you're not sure you have the latest version, download it again just to make sure, as previous versions are unable to successfully compile a Quake2 dll, and it can be difficult to determine what version of LCC you have. You can download the latest version from here.

Unzip the LCC archive, making sure use folder names is ticked in Winzip. Select the root directory as the destination directory, and LCC will unzip into a directory called \lccpub. Look in the \lccpub directory, and you will see program called setuplcc.exe. You need to run this before using the compiler for the first time. It will ask you for the include and library directories. By default these are c:\lccpub\include, and c:\lccpub\lib respectively.

You also need to add LCC's bin directory to the path. By default this will be c:\lccpub\bin. The easiest way to do this is to add the following line as the LAST line of your autoexec.bat file.

PATH=C:\LCCPUB\BIN;%PATH%

You will need to reboot for the change to take effect. If you use Windows NT and don't have an autoexec.bat file, you can alter the path in the environment tab on the system properties. (System in control panel, or properties for My Computer.)

Creating a new patch directory

Create a new subdirectory in your Quake2 directory to hold your compiled patch. In this tutorial I will assume the directory name is PATCH. Next you need to unzip the ID source code into the new patch directory. If you don't already have it, download it here. The zip file contains a lot of other stuff related to map building, what you need is what's contained in the directory called GAME. Because there is a directory structure inside the zip file, the easiest way is to unzip the whole thing to a temporary directory, and then copy the contents of the GAME directory straight into your patch directory. It's also very important that you unzip the source code with a program that is capable of dealing with long filenames, otherwise you will mangle the filenames of the source code, causing the make program to be unable to find them.

Due to a quirk of LCC which causes the GetGameAPI export to be misnamed as _GetGameAPI, you will need to edit the game.def file to trick LCC into using the correct export name. Open game.def into a suitable editor such as Wedit, and you will see the following:

EXPORTS
	GetGameAPI
You need to
change this to:
EXPORTS
	GetGameAPI = GetGameAPI

To ensure your patch runs under both Windows 95 and Windows NT as well as future operating systems, you need to add a new function that provides a standard DLL entry point. Apparently MSVC++ does this automatically if it doesn't exist explicitly in the code, however LCC does not, so we have to do this ourselves. Using GetGameAPI as the entry point (as used by nearly everyone up until now) is INCORRECT, and while it works under 95, it crashes under NT. Grab lcchack.zip and unzip it into your patch directory.

Until ID release an updated version of the game dll source code, you'll need to make a small modification for your code to run in Quake2 version 3.09 or later. Open the file game.h, right near the top you'll see a line saying:

#define GAME_API_VERSION 1

change the 1 to a 2.

To automate the compiling process, you'll need a makefile. Get it here, and unzip it to your patch directory.

You are now ready to compile!

Open an MS-DOS prompt, change to your patch directory, and type MAKE. If all goes well you will see the results go by on the screen. Don't be alarmed at warnings, as long as there are no errors, everything should be fine. Once the compiling is finished you should find a gamex86.dll file in your patch directory. You'll find that this version is bigger than the original ID one, (421K or bigger, compared to 349K) but this is normal as the code produced by LCC is not as highly optimised for size as MSVC++5, and we are also using the C_ONLY #define to avoid the assembly code in ID's source. This is done because LCC is not compatible with the in-line assembly code ID used. The down side is that this will slow the patch down slightly and make the DLL file slightly bigger, but there is no other choice with LCC.

Testing your patch in Quake2.

Running a patch in Quake2 is similar to Quake, but the command line option has changed slightly. It is now Quake2.exe +set game patch where patch is the name of your patch directory. One way to launch a patch, is to make a copy of the icon you normally run Quake2 with, right click it, go into the properties, go to the shortcut tab, and in the target box, enter your command line options just after Quake2.exe. An alternative is to use a frontend program like QuakeOn.

If all goes well, when you run your patch you should notice NO difference in Quake2's behaviour! The reason of course, is that you have just compiled ID's code without making any changes. As long as a gamex86.dll file exists in your patch directory, you can be fairly sure that your patch is running, however if the dll file is missing, the game will load the normal one instead and you wont know about it. The only way to be REALLY sure, is once you start making changes to the code.

Editing your patch.

Most text editors are suitable for editing the source code, some examples are Wedit, which is supplied with LCC itself, PFE, (programmers file editor) and Wordpad. Examples of editors not suitable are Microsoft Word or Works, and Notepad. For information on using Wedit, click here. For programming examples, check out the other tutorials on this site.

Customising the Makefile

As you start adding new source and header files to your project you'll need to update your makefile to incorporate the new files. The following instructions assume makefile V1.6. There are two things you need to change for each new file you add. Lets say you want to add a new source file called cannon.c which includes the header files cannon.h and g_local.h. First it needs to be added to the object list.

OBJS= g_ai.obj g_cmds.obj g_combat.obj g_func.obj g_items.obj g_main.obj \
g_misc.obj g_monster.obj g_phys.obj g_save.obj g_spawn.obj g_target.obj \
g_trigger.obj g_turret.obj g_utils.obj g_weapon.obj m_actor.obj \
m_berserk.obj m_boss2.obj m_boss3.obj m_boss31.obj m_boss32.obj \
m_brain.obj m_chick.obj m_flash.obj m_flipper.obj m_float.obj \
m_flyer.obj m_gladiator.obj m_gunner.obj m_hover.obj m_infantry.obj \
m_insane.obj m_medic.obj m_move.obj m_mutant.obj m_parasite.obj \
m_soldier.obj m_supertank.obj m_tank.obj p_client.obj p_hud.obj \
p_trail.obj p_view.obj p_weapon.obj q_shared.obj lcchack.obj

The object list is a list of all the object files that are passed to the linker program to produce the finished DLL file. For each object file there is one source file that is compiled to create the object file. The linker joins all the object files together, (plus a few runtime libraries) and creates a DLL file. In this part of the makefile, a backslash at the end of each line means "continue on the next line". Basically its just a way of entering a large number of items that you want to be functionally part of the same line, but physically on separate lines to keep it readable. Just make sure there is a backslash on the end of each line except the last one and you wont go wrong. We will change this section to:

OBJS= g_ai.obj g_cmds.obj g_combat.obj g_func.obj g_items.obj g_main.obj \
g_misc.obj g_monster.obj g_phys.obj g_save.obj g_spawn.obj g_target.obj \
g_trigger.obj g_turret.obj g_utils.obj g_weapon.obj m_actor.obj \
m_berserk.obj m_boss2.obj m_boss3.obj m_boss31.obj m_boss32.obj \
m_brain.obj m_chick.obj m_flash.obj m_flipper.obj m_float.obj \
m_flyer.obj m_gladiator.obj m_gunner.obj m_hover.obj m_infantry.obj \
m_insane.obj m_medic.obj m_move.obj m_mutant.obj m_parasite.obj \
m_soldier.obj m_supertank.obj m_tank.obj p_client.obj p_hud.obj \
p_trail.obj p_view.obj p_weapon.obj q_shared.obj lcchack.obj \

cannon.obj

The object listing is part of a rule that describes how to link the project, likewise there is a rule for each object file that describes how compile it, and what files are necessary to compile it. Lets look at an example:

p_client.obj:     p_client.c m_player.h g_local.h q_shared.h game.h
    $(CC) $(CFLAGS) p_client.c

The first part, p_client.obj: specifies which object file this rule applies to. There is one of these for EACH object file. What is immediately after the colon describes what other files this object file is dependent on. Dependent files are basically ones that are required to compile this object file, and should they be edited, then this particular object file would need to be recompiled. This is the basis of the make process - by knowing which files affect which other files, it is possible to compile only the files that are necessary, rather than recompiling everything each time.

The make program examines the date and time of all the files on the first line, and if any file on the right-hand side has a more recent date than the file on the left-hand side then it assumes changes have been made and recompiles this file, otherwise it leaves it alone. Which files should an object file be dependent on? The source file, and all the header files referenced by that source file. In this case the source file is p_client.c and if you examine the top of that file, you'll see it includes the headers m_player.h and g_local.h.  Ok, so why is there q_shared.h and game.h as well? The reason is that its possible to include another header file from within a header file, and g_local.h itself includes q_shared.h and game.h. Because of this, we must also include them in our dependency list since they affect the compiling of this file. The only header file supplied with the game DLL source that includes other header files is g_local.h as mentioned, so keep it in mind if your new source file includes g_local.h.

The second line describes HOW to recompile the object file, should it be necessary. The second line MUST begin with a tab, not spaces. You must be certain that when you edit this line that your editor doesn't replace tabs with spaces or you will get strange errors. The two strings preceded by $ signs are macros, which are defined at the top of the makefile:

CC=lcc
CFLAGS=-DC_ONLY -o2

$(CC) is substituted with lcc and $(CFLAGS) is substituted with -DC_ONLY -o2. The reason for doing this is that we are going to be running the compiler dozens of times, once for each source file, and if we wanted to change a command line option for the compiler and we weren't using macros, we would have to laboriously edit every occurrence in the makefile. This way we only have to change it once at the top. Finally on the second line we have the name of the source file that we will compile to create this object file.

Back to our example of cannon.c we would create a new entry just after the other ones that looks like:

cannon.obj:     cannon.c g_local.h q_shared.h game.h
   $(CC) $(CFLAGS) cannon.c