----------------------------------------------------------------------

How to Compile the Half-Life Standard SDK on the Linux Platform

Leon Hartwig (jehannum@planethalflife.com)
August 22, 1999 (revised February 5, 2000)

----------------------------------------------------------------------

A very high percentage of the internet Half-Life servers are run using
the Linux operating system and the Linux version of the Half-Life
server. Surprisingly, aside from my own Phineas Bot, there have been
few Linux versions of other mods released. Hopefully, this article
will change that. I will provide a step-by-step walkthrough,
explaining exactly what must be edited in the Standard SDK in order to
successfully compile it under Linux. Editing the Half-Life Standard
SDK source code and compiling it for Linux is so easy that every
mod-maker who is familiar with compiling programs under Linux, or
knows someone who is, should consider it. 

This article will not teach you the basics. It is written for people
who know what their compilers are, and how to use them. If you don't
know what a makefile is and how to use one, I would recommend that you
find someone who does know and is willing to help you out with your
mod. 

This article does not address the Professional SDK, only the Standard
SDK. I do not have the Pro SDK, nor am I interested in it. You may or
may not be able to use this text as a guideline in porting the Pro
SDK, but I am not supporting such use of this article. 

Before I begin the walkthrough, I would like to address one of the
problems that people often run into when using Windows text files in
Linux. Windows text files have a ^M (carriage return) character at the
end of every line that is very unfriendly to many Linux preprocessors
and compilers. You will get otherwise unexplainable errors when you
try to compile files with these characters in them. When you transfer
source files over to a Linux OS, you must find some way of stripping
those characters out of the files. There are programs that do this
(many text editors will), but one good way is to use FTP. Chances are,
you will already be using FTP to transfer the files to a Linux
computer. The trick is to transfer .h and .cpp files in ASCII mode,
rather than BINARY mode. This will strip out the ^M characters with no
extra effort. 

Now, on to the porting.


(STEP #1)
In the "engine/" directory, rename the file "PROGS.H" to "progs.h"
The only change is to make all of the letters in the filename lowercase. 


(STEP #2)
In the "engine/" directory, edit the file "eiface.h": 

Near line 37, change this code block:
----------------------------------------------------------------------
#define DLLEXPORT __stdcall
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#ifdef _WIN32
#define DLLEXPORT __stdcall
#else
#define DLLEXPORT __attribute__((stdcall))
#endif
/* END LINUX COMPILE */
----------------------------------------------------------------------


(STEP #3)
In the "dlls/" directory, edit the file "cbase.h": 

Near line 54, change this code block:
----------------------------------------------------------------------
#define EXPORT    _declspec( dllexport )
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#ifdef _WIN32
#define EXPORT  _declspec( dllexport )
#else
#define EXPORT
#endif
/* END LINUX COMPILE */
----------------------------------------------------------------------


(STEP #4)
In the "dlls/" directory, edit the file "extdll.h": 

Near line 35, change this code block:
----------------------------------------------------------------------
// Prevent tons of unused windows definitions
#define WIN32_LEAN_AND_MEAN
#define NOWINRES
#define NOSERVICE
#define NOMCX
#define NOIME
#include "WINDOWS.H"

// Misc C-runtime library headers
#include "STDIO.H"
#include "STDLIB.H"
#include "MATH.H"
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#ifdef _WIN32

// Prevent tons of unused windows definitions
#define WIN32_LEAN_AND_MEAN
#define NOWINRES
#define NOSERVICE
#define NOMCX
#define NOIME
#include "WINDOWS.H"

// Misc C-runtime library headers
#include "STDIO.H"
#include "STDLIB.H"
#include "MATH.H"

#else
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <ctype.h>

#define ULONG ulong
#define FALSE 0
#define TRUE  1

#ifndef max
#define max(a,b)    (((a) > (b)) ? (a) : (b))
#endif

#ifndef min
#define min(a,b)    (((a) < (b)) ? (a) : (b))
#endif

#define itoa(a,b,c) sprintf(b, "%d", a)

typedef unsigned char BYTE;
#endif
/* END LINUX COMPILE */
----------------------------------------------------------------------


(STEP #5)
In the "dlls/" directory, edit the file "h_export.cpp": 

Near line 48, change this code block:
----------------------------------------------------------------------
void DLLEXPORT GiveFnptrsToDll(   enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#ifdef _WIN32
void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
#else
extern "C" void DLLEXPORT GiveFnptrsToDll( enginefuncs_t* pengfuncsFromEngine, globalvars_t *pGlobals )
#endif
/* END LINUX COMPILE */
----------------------------------------------------------------------

Also in "h_export.cpp", near line 29, change this code block:
----------------------------------------------------------------------
// Required DLL entry point
BOOL WINAPI DllMain(
   HINSTANCE hinstDLL,
   DWORD fdwReason,
   LPVOID lpvReserved)
{
        if      (fdwReason == DLL_PROCESS_ATTACH)
    {
    }
        else if (fdwReason == DLL_PROCESS_DETACH)
    {
    }
        return TRUE;
}
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#ifdef _WIN32
// Required DLL entry point
BOOL WINAPI DllMain(
   HINSTANCE hinstDLL,
   DWORD fdwReason,
   LPVOID lpvReserved)
{
        if      (fdwReason == DLL_PROCESS_ATTACH)
    {
    }
        else if (fdwReason == DLL_PROCESS_DETACH)
    {
    }
        return TRUE;
}
#endif
/* END LINUX COMPILE */
----------------------------------------------------------------------


(STEP #6)
In the "dlls/" directory, edit the file "plane.cpp": 

Near line 15, change this code block:
----------------------------------------------------------------------
#include "extdll.h"
#include "plane.h"
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
#include "extdll.h"
/* LINUX COMPILE */
#include "util.h"
/* END LINUX COMPILE */
#include "plane.h"
----------------------------------------------------------------------


(STEP #7)
In the "dlls/" directory, edit the file "util.h": 

Near line 89, change this code block:
----------------------------------------------------------------------
#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \
        extern "C" _declspec( dllexport ) void mapClassName( entvars_t *pev ); \
        void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); }
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#define LINK_ENTITY_TO_CLASS(mapClassName,DLLClassName) \
        extern "C" EXPORT void mapClassName( entvars_t *pev ); \
        void mapClassName( entvars_t *pev ) { GetClassPtr( (DLLClassName *)pev ); }
/* END LINUX COMPILE */
----------------------------------------------------------------------


(STEP #8)
In the "dlls/" directory, edit the file "util.cpp": 

Near line 1597, change this code block:
----------------------------------------------------------------------
unsigned int CSaveRestoreBuffer :: HashString( const char *pszToken )
{
        unsigned int    hash = 0;

        while ( *pszToken )
                hash = _rotr( hash, 4 ) ^ *pszToken++;

        return hash;
}
----------------------------------------------------------------------

To this code block:
----------------------------------------------------------------------
/* LINUX COMPILE */
#ifndef _WIN32
/* Thanks to Mike Harrington for this. */
extern "C" {
    unsigned _rotr ( unsigned val, int shift) {
        register unsigned lobit;        /* non-zero means lo bit set */
        register unsigned num = val;    /* number to rotate */

        shift &= 0x1f;                  /* modulo 32 -- this will also make
                                           negative shifts work */

        while (shift--) {
                lobit = num & 1;        /* get high bit */
                num >>= 1;              /* shift right one bit */
                if (lobit)
                        num |= 0x80000000;  /* set hi bit if lo bit was set */
        }

        return num;
    }
}
#endif
/* END LINUX COMPILE */

unsigned int CSaveRestoreBuffer :: HashString( const char *pszToken )
{
        unsigned int    hash = 0;

        while ( *pszToken )
                hash = _rotr( hash, 4 ) ^ *pszToken++;

        return hash;
}
----------------------------------------------------------------------

That's all there is to editing the SDK. Now, you just need a good
makefile and you should be all set. Luckily, I have one for you
(revised February 5, 2000):

http://www.planethalflife.com/phineas/files/Makefile.gz 

If you have problems compiling the source, you may need to use a
different version of your compiler. Check for compiler updates for
your Linux distribution, or consult the following site:

The GCC Home Page (http://gcc.gnu.org/)

EGCS-1.1.2 has been known to work very well and can be found here:

GCC/EGCS Old Releases (ftp://egcs.cygnus.com/pub/egcs/releases/)

Newer GCC releases may or may not work well, but can be found here if
you are adventurous:

GCC Releases (ftp://gcc.gnu.org/pub/gcc/releases/index.html)


If you have done extensive modifications to the SDK, what you see in
this article might not be enough to completely port your additional
work to Linux. If this happens and you need some help, feel free to
contact me. I have my hands full with projects of my own, so I can't
guarantee that I will spend much time on your problem (if any), but it
can't hurt to ask. 

I am completely open to suggestions on how to improve anything you've
read in this article, including the makefile. 

-Leon Hartwig
February 5, 2000 

----------------------------------------------------------------------

Copyright (C) 1999, 2000 Leon E. Hartwig III. All rights reserved.

Inquiries into reposting and/or distributing this article should be
made to the author. 
