9

Peeking under the hood of Command & Conquer

 3 years ago
source link: http://www.hydrogen18.com/blog/peeking-under-the-hood-of-command-conquer.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

The original source code for Command & Conquer as well as Command & Conqueror: Red Alert have been released by Electronic Arts. You can grab it here on GitHub .

Everything is licensed under the GPL v3 and the source code contains all the original comments. The original changelog from whatever VSC was used is not present. It looks like everything was just put into Git very recently.

I decided to take a peak at what goes on inside this game engine. I'm not going to spend my time reviewing every line of code, but it should at least be interesting to find out what game development in C++ was like in the early 1990s.

I'm looking only at the source code for "Command & Conquer: Red Alert" as it looks like it is a fork of the original. You can find it under the REDALERT directory.

Statistics

  1. 290 C++ Header files
  2. 296 C++ implementation files
  3. 14 assembler files containing x86 assembler instructions
  4. 222090 lines of C++ code

I got the lines of code figure by counting up the non empty lines then subtracting out lines that were obviously just comments.

Almost all file names are uppercase only.

There is also a "RedAlert.vcxproj" file, so presumably this is ready to build in newer versions of visual studio. I did not confirm this.

Is this everything?

First off, there are no assets at all. Not even a test set. So you would need to somehow legally acquire those, for example you might buy the game from EA. Until you do that, even if you compile the code you can't really prove it works.

The code is littered with #ifdef WIN32 , in fact there are at least 430 occurences. There is a folder WIN32LIB that contains some platform specific implementations for Microsoft Windows. There are no other platforms implemented. The compiler directives for #ifdef WIN32 don't have an #else anywhere I saw for other platforms. So I'm not entirely sure what the point was, but DOS was a supported platform. So maybe all the checks allow a build of the game that works on DOS but not Windows.

I thought I found two files LCW.CPP and LCWUNCMP.CPP which reference the playstation platform indirectly

// From LCW.CPP
 *               Project Name : WESTWOOD LIBRARY (PSX)                     *

This file implements the LCW compression algorithm.

But what the "PSX" actually refers to is the POSIX standard through a less than common acronym. There is even another implementation of the same compression function LCW_Comp from LCW.CPP in LCWCOMP.ASM . I think the C++ implementation must have just been used on some platforms where the assembly code wouldn't work.

So I guess they didn't release the Playstation port. I don't even know if the Playstation port was done by Westwood or if it was just subcontracted out.

Interestingly, LZO & LZW compression algorithms are also implemented. So I guess Westwood studios strategy for choosing which technology was "all of the above". LCW should have been sufficient to dodge the Unisys patent claims on LZW, but maybe the LZW code just stuck around. The only usage of the LZW code is in LZWPIPE.CPP which defines the LZWPipe class. The only usage of that code is commented out

// From SAVELOAD.CPP:423

        LZOPipe pipe(LZOPipe::COMPRESS, SAVE_BLOCK_SIZE);
//      LZWPipe pipe(LZWPipe::COMPRESS, SAVE_BLOCK_SIZE);
//      LCWPipe pipe(LCWPipe::COMPRESS, SAVE_BLOCK_SIZE);

It looks like a few different algorithms were tried in this case and LZO was settled on. So LZW appears to be available, but not used.

Unisys patent claim against LZW expired a long time ago, so there is no longer any concern over this.

Weird headers

A header like this present in LZW.H and other files

// From the top of LZW.H
/* $Header: /CounterStrike/LZW.H 1     3/03/97 10:25a Joe_bostic $ */

The "CounterStrike" here refers to "Command And Conquer: Counterstrike" which is an expansion pack. There are 215 occurences of #ifdef FIXIT_CSII that somehow changes things for the expansion pack. The full explanation comes from "awj" on 9-28-1998 in a comment

// From DEFINES.H

#define FIXIT_CSII                                      // Adds Aftermath CounterStrike II units
//      ajw 9/28/98 - Note about FIXIT_CSII. Changes seem to have been made for Aftermath ("Counterstrike II") that: a) were
//      bug fixes that should never be rolled back, b) change the nature of the game, at least in multi-player. This meant
//      that the "Red Alert" executable ( == Counterstrike executable ) could no longer be built. Apparently, at the time,
//      this was justified, as it was believed that no further patches to the RA executable would ever be necessary.
//      Given that Denzil's DVD changes and my WOLAPI integration are essentially a patch, we've got a problem.
//      We've decided to level the field and make sure every who gets or patches to the new version of Red Alert, CS, AM, (and
//      their DVD equivalent(s)) will have the same executable. So we're assuming that all of the FIXIT_CSII changes are 
//      permanent (as, in fact, all prior FIXIT_'s are - makes me wonder why the old non-compiling code has to hang around
//      forever), and fixing the code so that the assumption "this is an Aftermath game" is no longer hard-coded, but can 
//      change at runtime. (Which is what should have been done when Aftermath was created.)
//      <This goes for the following three defines as well.>
#define FIXIT_CARRIER                           // Adds Aftermath aircraft carrier
#define FIXIT_PHASETRANSPORT            // Adds Aftermath cloaking APC
//      ajw - Discovered that engineer changing fields were specifically left out of aftrmath.ini, thus this has no effect.
//      Engineer changes (and other game rule changes) are in mplayer.ini, which was loaded before aftermath-only mplayer games.
#define FIXIT_ENGINEER                          // Adds Engineer rules.ini overrides

Missing source code

There is also the question of WOLAPI.dll which seems to mean "Westwood Online API". This was used for online matchmaking. The author of Command & Conquer: Red Alert apparently disliked this library enough to write a complete wrapper around it, it is in WOLAPIOB.H . You can probably just go find the original wolsetup.exe file and extract the DLL from it. But it also likely isn't very useful.

Funnily enough, it appears that WOLAPI.dll was actually reverse engineered over 9 years ago

This is not a full source code release , but should be enough for people to understand the internal game mechanics. EA stated the reason for their release was to help modders, so this probably accomplishes that. With substantial work a standalone game could be built.

How does the game startup?

I decided to start by looking at how the game starts up. Startup code usually gives you a good idea of how a game engine communicates with the operating system. This is presented roughly in the order that it actually is in the code, but not exactly. Anything not interesting is skipped.

The C++ main function.

The main function is defined in STARTUP.CPP . The game appears to have recently been rebuilt as a DLL, presumably for the remastered version that is being released. So the first thing it does is assigns RunningAsDLL = true;

The main function checks #ifdef MPEGMOVIE as well as #ifdef MCIMPEG all over the place. In DEFINES.H this is commented out entirely

// Define DVD to turn on RADVD additions/changes - Denzil
#ifdef DVD
//#define INTERNET_OFF
//#define MPEGMOVIE //PG
//#define MCIMPEG
#endif

So presumably the functionality to play MPEG cinematics is missing from the game.

The first thing that happens is it checks for another copy of the game runing and refuses to start. This seems to have been disabled with a #if (0) . There are many sections of code disabled in this manner, each one featuring a //PG . One of the committers in Git is PG-SteveT so I guess the same person who put this project in Git was responsible for porting this to be a DLL rather than a standalone executable. There are many more sections like this in the main function, I am referring to them just as disabled from here forward.

Next up is checking for free memory. The first check is just allocate 13 megabytes of memory and if that works free it. There is another check that is disabled that tries to allocate 13 megabytes of memory in a loop. Each iteration of the loop reduces the requested allocation by 1 kilobyte until it passes. I think it is safe to assume modern computers running Command & Conquer will have the needed 13 megabytes of memory, so it is sensible that this is disabled.

The next check is to see if the command line contains f:\\projects\\c&c0 or F:\\PROJECTS\C&C0 and refuse to start if that is the case. If it is, a dialog box saying "Playing off of the network is not allowed." pops up and the game exits. A quick inspection of some header files lead me to find this

 "/* $Header:   F:\projects\c&c0\vcs\code\wwalloc.h_v   4.9   07 May 1996 17:14:00   JOE_BOSTIC  $ */" in WWALLOC.H.

Since "vcs" is in the path it seems like an early version control system was used, that acted as a mounted drive in Microsoft Windows. The last time I used IBM Rational ClearCase it still works like this. This probably was done to prevent people from running the game from the network share and constantly overwriting each other's save files.

The game implements its own command line parsing logic, implemented inline in the main function

// From STARTUP.CPP

        /*
        ** Get pointers to command line arguments just like if we were in DOS
        **
        ** The command line we get is cr/zero? terminated.
        **
        */

        command_scan=0;

        do {
                /*
                ** Scan for non-space character on command line
                */
                do {
                        command_char = *( command_line+command_scan++ );
                } while ( command_char==' ' );

                if ( command_char!=0 && command_char != 13 ) {
                        argv[argc++]=command_line+command_scan-1;

                        /*
                        ** Scan for space character on command line
                        */
                        bool in_quotes = false;
                        do {
                                command_char = *( command_line+command_scan++ );
                                if (command_char == '"') {
                                        in_quotes = !in_quotes;
                                }
                        } while ( (in_quotes || command_char!=' ') && command_char != 0 && command_char!=13 );
                *( command_line+command_scan-1 ) = 0;
                }

        } while ( command_char != 0 && command_char != 13 && argc<20 );

I didn't really bother proving this works, but it looks like it scans for things being in quotes on the command line and then tries to treat them as groups if they are quoted using " . I guess the Windows command line couldn't handle this so it was done in the game directly. I actually don't know if cmd.exe implements this quoting logic even today.

Main then saves off the original working directory and drive the game was launched from, before changing to the directory with the executable. Interestingly this seems to default to A: which would usually be a floppy drive on Windows. This code path is disabled entirely now.

Next up is the first #ifdef WOLAPI_INTEGRATION . This isn't defined and the source code for the Westwood Online API is not available. If it were defined, the game would try and find a file named wolsetup.exe and run it, before checking for a Windows registry key indicating that setup is complete. A rather interesting comment shows up here

// From STARTUP.CPP

        //      I've been having problems getting the patch to delete "conquer.eng", which is present in the game
        //      directory for 1.08, but which must NOT be present for this version (Aftermath mix files provide the
        //      string overrides that the 1.08 separate conquer.eng did before Aftermath).
        //      Delete conquer.eng if it's found.
        if( FindFirstFile( "conquer.eng", &wfd ) != INVALID_HANDLE_VALUE )
                DeleteFile( "conquer.eng" );

Apparently the developers after version 1.08 of the game really needed to not have "conquer.eng" be present. So they just try and delete it every time the game starts up. Why this is packed in with the Westwood Online code, I don't know.

Now we move on to a check for #if(TEN) which is not defined. If it were defined, the game would setup for support for the Total Entertainment Network .

Right after that check there is a check for #if(MPATH) which is also not defined. It if were then the game would have support for the MPlayer .

Both were decently popular online gaming platforms in the last half of the 1990s. My guess is there was a custom build for MPlayer & TEN support. Neither exists today. I discuss these more later in this article.

Now the game starts a Windows timer, sleeps for 1000 milliseconds and makes sure the system clock has advances. If this fails it exits the game. I guess the implementation of sleep on some platforms was sufficiently faulty to warrant such a check during startup. This code has now been disabled.

// From STARTUP.CPP

#if (0)//PG
                int time_test = WindowsTimer->Get_System_Tick_Count();
                Sleep (1000);
                if (WindowsTimer->Get_System_Tick_Count() == time_test){
                        MessageBox(0, TEXT_ERROR_TIMER, TEXT_SHORT_TITLE, MB_OK|MB_ICONSTOP);
                        return(EXIT_FAILURE);
                }
#endif

Then the game checks for free disk space and if it doesn't have enough it exits. This code has now been disabled.

It's now onwards to loading the configuration generated from the ccsetup program that comae with the original game. This doesn't do anything too interesting, but I did notice this

: cpp

// From STARTUP.CPP

                        if (!Debug_Quiet) {
                                Audio_Init(NewConfig.DigitCard,
                                                NewConfig.Port,
                                                NewConfig.IRQ,
                                                NewConfig.DMA,
                                                PLAYBACK_RATE_NORMAL,
//                                              (NewConfig.Speed) ? PLAYBACK_RATE_SLOW : PLAYBACK_RATE_NORMAL,
                                                NewConfig.BitsPerSample,
//                                              4,
                                                (Get_CPU() < 5) ? 3 : 5,
//                                              (NewConfig.Speed) ? 3 : 5,
                                                NewConfig.Reverse);
                                SoundOn = true;
                        } else {
                                Audio_Init(0, -1, -1, -1, PLAYBACK_RATE_NORMAL, 8, 5, false);
                        }

A "DebugQuiet" configuration was added that appears to select an audio output that does nothing. I suppose it would be quite annoying if you were testing the game to be continually subjected to the noise.

At this point the game is ready to configure the display resolution. This code is exceptionally interesting. The first step is to check for a screen height of 400, which means a mode of 640 x 400 has been selected. If this can't be set it falls back to 640 x 480. Any other screen resolution is uset directly. What is so special about a mode of 640 x 400?

To actually do rendering the game needs graphics memory access. A screen width of 320 results in a special code path running to initialize the display buffers. My guess is anyone running on a video mode of 320x240 (which is still VGA) is running on such old hardware they don't bother trying to use hardware buffers. Any other screen height results in an attempt to allocate hardware buffers for the visible memory page and a hidden memory page. I suspect double buffering of the graphics is used, hence two buffers. For the visible memory page it is mandatory that hardware memory is available. There is some interesting logic around this allocation

// From STARTUP.CPP

                                VisiblePage.Init( ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)(GBC_VISIBLE | GBC_VIDEOMEM));

                                /*
                                ** Check that we really got a video memory page. Failure is fatal.
                                */
                                memset (&surface_capabilities, 0, sizeof(surface_capabilities));
                                VisiblePage.Get_DD_Surface()->GetCaps(&surface_capabilities);
                                if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) {
                                        /*
                                        ** Aaaarrgghh!
                                        */
                                        WWDebugString(TEXT_DDRAW_ERROR);WWDebugString("\n");
                                        MessageBox(MainWindow, TEXT_DDRAW_ERROR, TEXT_SHORT_TITLE, MB_ICONEXCLAMATION|MB_OK);
                                        if (WindowsTimer) delete WindowsTimer;
                                        return (EXIT_FAILURE);
                                }

I can only imagine that the "Aaaarrgghh!" comment was added after a long session of debugging performance issues on some machines. At some point someone realized that the DirectDraw API (part of DirectX) could return a system memory page even when a hardware video page is asked for.

The hidden page is a bit more lenient, but has the logic and even better comments

// From STARTUP.CPP

                                /*
                                ** If we have enough left then put the hidpage in video memory unless...
                                **
                                ** If there is no blitter then we will get better performance with a system
                                ** memory hidpage
                                **
                                ** Use a system memory page if the user has specified it via the ccsetup program.
                                */
                                unsigned video_memory = Get_Free_Video_Memory();
                                unsigned video_capabilities = Get_Video_Hardware_Capabilities();
                                if (video_memory < (unsigned int)(ScreenWidth*ScreenHeight) ||
                                                (! (video_capabilities & VIDEO_BLITTER)) ||
                                                (video_capabilities & VIDEO_NO_HARDWARE_ASSIST) ||
                                                !VideoBackBufferAllowed) {
                                        HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0);
                                } else {
                                        //HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0);
                                        HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)GBC_VIDEOMEM);

                                        /*
                                        ** Make sure we really got a video memory hid page. If we didnt then things
                                        ** will run very slowly.
                                        */
                                        memset (&surface_capabilities, 0, sizeof(surface_capabilities));
                                        HiddenPage.Get_DD_Surface()->GetCaps(&surface_capabilities);
                                        if (surface_capabilities.dwCaps & DDSCAPS_SYSTEMMEMORY) {

                                                /*
                                                ** Oh dear, big trub. This must be an IBM Aptiva or something similarly cruddy.
                                                ** We must redo the Hidden Page as system memory.
                                                */
                                                AllSurfaces.Remove_DD_Surface(HiddenPage.Get_DD_Surface());  // Remove the old surface from the AllSurfaces list
                                                HiddenPage.Get_DD_Surface()->Release();
                                                HiddenPage.Init (ScreenWidth , ScreenHeight , NULL , 0 , (GBC_Enum)0);
                                        } else {
                                                VisiblePage.Attach_DD_Surface(&HiddenPage);
                                        }
                                }

The same "double check" applies to this page as well, but can fallback to system memory. The comment specifically calls out the IBM Aptiva as a source of these issues. I suspect the IBM Aptiva must have been the butt of many jokes during develpoment because DEFINES.H even has a line for #define FIXIT_APTIVA_MODEM which does in fact use special logic for dealing with the IBM Aptiva's modem.

At this point the game now configures the ScreenHeight to exactly 3072. A graphics buffer is attached to the visible & hidden pages it with dimensions of 3072 x 3072. As far as I can tell almost all the above code around getting hardware memory is in fact disabled by the use of #if statements. It's such a mess it is hard to tell what is enabled and what is not. But all the press releases about the remaster mention "4K" support. So maybe the real resolution of this "4K" remaster is in fact 3072 x 3072.

As recently as 2019, some changes were made with comments indicating the date and time. Someone with initials JAS added a check to see if they were running from the editor (presumably the level editor) and avoids taking over the mosue if that is the case. The remaster was announce in Novement 2018, so this timeframe makes sense for various changes need to implement & test 4K support with new asset files.

There is all kinds of now disabled logic about forcing the intro cinematic on the first run through. It's almost bipolar, unable to make its mind up as to whether or not the user really needs to see this cinematic. At some point this was commented out, probably during the remaster development. I imagine developers and testers got very tired of watching the intro cinematic very quick, so I suspect it would have been removed during the original devlopment as well.

The game is now ready to play! It then runs Main_Game (from CONQUER.CPP ) which is the actual game loop. The start date in a comment in this file is "April 3, 1991", so the original Command & Conquer had 4 years of development before release in 1995!

Once the game is complete, the Main_Game function can return. There is a bunch of cleanup code, but before we get there we have this check

// From STARTUP.CPP

                        if (RunningAsDLL) {     //PG
                                return (EXIT_SUCCESS);
                        }

So presumably the DLL leaves the graphics in a weird state and probably leaks memory all over the place.

Other investigations

I decided to spend some time looking at other things I found interesting, based off the filenames.

Windows 95 Stack traces.

The file W95TRACE.CPP contains the comment

/ * Implementation of Win95 tracing facility to mimic that of NT".  */

This seems to suggest development was done on Windows NT, but testing was probably done on Windows 95 to make sure it worked for the intended audience. This would make sense as NT would be available as early as July 1993 to developers working on the game. I suspect the Windows 95 stack traces are woefully unhelpful compared to what Windows NT could provide.

Watcom compiler

There is also WATCOM.H that defines lots of "#pragma", which are all obviously specific to the then popular Watcom compiler.

This is sort of interesting, because the Watcom compiler first release was 1993 for C++ support. There are other development notes indicating this project goes back to 1991. Did it start off as a C project and move to C++? Or did they have another C++ compiler available?

There is also this gem from the same file, I guess some compiler writers could not be bothered to implement true & false at this time

// From WATCOM.H

// Fix deficiency in Watcom so that true/false will be defined.
#ifndef __BORLANDC__
#ifndef TRUE_FALSE_DEFINED
#define TRUE_FALSE_DEFINED
enum {false=0,true=1};
typedef int bool;
#endif
#endif

Encryption

Blowfish encryption is implemented in BLOWFISH.CPP, which presumably is the well known Blowfish cipher . The only real use I can think for for this is to obfuscate network traffic.

Entity system

The CCPtr class in CCPTR.H is a pointer like type that uses an ID as an offset into a memory heap. This is pretty common, because it makes saving the state of the game to disk easier if all objects are tracked in one place. This functions as a very simple way to have an entity system. If you're curious about what the advantages of an entity system are in a video game, you can read more here .

Vectors

The VECTOR.H class defines a template class vaguely similar to std::vector. Presumably this was developed too early to take advantage of STL.

Dipthongs

So a dipthong is a combination of two adjacent vowel sounds within the same syllable. What does this have to do with video game code? Nothing honestly. The strangest file is WIN32LIHB/DIPTHONG.H . The actual description given of this is found in the header

// From WIN32LIB/DIPTHONG.CPP

 * DIGRAM or DIATOMIC encoding is the correct term for this method.        *
 * This is a fixed dictionary digram encoding optimized for English text.  *

This is bascially a simple compression algorithm that assumes you're compressing text and does some subsequence replacement.

In the code we find the magic number 4567 used when making some defines

// From WIN32LIB/DIPTHONG.CPP
#define TXT_GUEST                                       4567+3
#define TXT_LOGIN                                       4567+4
#define TXT_LOGIN_TO_INTERNET   4567+5
#define TXT_YOUR_HANDLE                         4567+6
#define TXT_YOUR_PASSWORD               4567+7
#define TXT_INTERNET_HOST               4567+8
#define TXT_INTERNET_JOIN               4567+9
#define TXT_INTERNET_GAME_TYPE  4567+10
#define TXT_JOIN_INTERNET_GAME  4567+11
#define TXT_ENTER_IP_ADDRESS    4567+12
#define TXT_WINSOCK_CONNECTING                                                  4567+13
#define TXT_WINSOCK_NOT_CONNECTING                                      4567+14
#define TXT_WINSOCK_UNABLE_TO_CONNECT_TO_SERVER 4567+15
#define TXT_WINSOCK_CONTACTING_SERVER                           4567+16
#define TXT_WINSOCK_SERVER_ADDRESS_LOOKUP_FAILED        4567+17
#define TXT_WINSOCK_UNABLE_TO_ACCEPT_CLIENT             4567+18
#define TXT_WINSOCK_UNABLE_TO_CONNECT                           4567+19
#define TXT_WINSOCK_CONNECTION_LOST                                     4567+20
#define TXT_WINSOCK_RESOLVING_HOST_ADDRESS                      4567+21

The following code runs as part of decompression

// From WIN32LIB/DIPTHONG.CPP
if (string >= 4567) return (InternetTxt[string-4567]);

        ptr = (unsigned short int const *)data;
        return (((char*)data) + ptr[string]);

The array "InternetTxt" is just a bunch of predefined strings. It appears anything equal to or above "4567" is considered to be a predefined string. My guess is this constant was chosen by the developer pressing the keys 4-5-6-7 on the digits row on their keyboard. When I need to generate a random integer to use as a constant, I prefer this method .

The Entertainment Network & MPlayer

As I mentioned up above, there appear to be custom builds for supporting The Entertainment Network & MPlayer. These were online multiplayer services at the time.

Each service got its own custom initialization & teardown code.

The Entertainment Network (TEN) files

  1. CCTEN.CPP
  2. TENMGR.H

The following functions are used but have no implementation in the source code

  1. tenArSendToPlayer
  2. tenArExitArena
  3. tenArIdleArena
  4. tenArReturnGameOptions
  5. tenArReturnPlayerOptions
  6. tenArSendToOtherPlayers
  7. tenArSendToPlayer
  8. tenArSetAlertMessageRoutine
  9. tenArSetIncomingPacketRoutine
  10. tenArSetOption
  11. tenArSetPlayerEnteredRoutine
  12. tenArSetPlayerState
  13. tenArSetPregameHookRoutine
  14. tenArUnreliableSendToOtherPlayers
  15. tenArUnreliableSendToPlayer

MPlayer (MPATH) files

  1. CCMPATH.CPP
  2. MPMGRW.H
  3. MPMGRD.H

The following functions are used but have no implementation in the source code

  1. MGenMoveTo
  2. MGenGetMasterNode
  3. MGenFlushNodes
  4. MGenMCount
  5. MGenSanityCheck
  6. MGenGetNode
  7. MGenGetQueueCtr

I can't find any details on the missing functions from the TEN code.

The "MPATH" code calls a bunch of "MGen" functions which are also missing. However, you can just go look them up in the Quake Source code . Quake was also available on both of these networks. The TEN code isn't present in the public Quake source however. I am guessing that MPlayer integration was done by just sharing a standard library with the development studio and having them integrate it.

My guess is EA was cautious and didn't feel they owned this code, whereas id software may have simply not cared at the time that Quake was released.

Of course, no one actually needs this code. It's just the last thing that remains of what was a staple of late 1990s gaming services.

Translation & Localization

It's always common to see games ported to other languages, so they can appeal to a wider market than the English speaking world. Interesting, the localization was done at least in part by using #ifdef GERMAN statements sprinkled around as well as being checked in LANGUAGE.H before defining things like TEXT_SETUP_FIRST .

The check #ifdef GERMAN shows up 28 times, but #ifdef FRENCH shows up 37 times. Maybe the French translation was just a bit more thorough.

Of course DEFINES.H has the best translation implementation

// From DEFINES.H
//#define SPAIN 1       (never used)

I guess they just never got around to the Spanish translation.

Dongle protection

The hardware dongle was a common method of DRM for all sorts of software at some point in the past. Basically you have this obscure & obfuscated piece of hardware you attach to your PC. The software issues some sort of "challenge" to it and refuses to start if it doesn't get an answer it likes.

I don't think Command & Conquer every shipped with this as a thing, but DEFINES.H does have a commented out line or two about it

// From DEFINES.H
/**********************************************************************
** ColinM
** Set this to enable dongle protection
*/
//#define DONGLE

Virgin Interactive

Westwood studios developed Command & Conquer but was owned by Virgin Interactive. There is this uniquely named check

// From DEFINES.H
/**********************************************************************
**      If this is defined, the special Virgin limited cheat keys
**      are enabled. This allows the "cheat" parameter and then only
**      allows the ALT-W to win the mission.
*/
#ifdef PLAYTEST_VERSION
#define VIRGIN_CHEAT_KEYS
#endif
// From INIT.CPP
#ifdef VIRGIN_CHEAT_KEYS
                        case PARM_PLAYTEST:
                                Debug_Playtest = true;
                                break;
#endif

I guess that before release, Virgin Interactive got a special built that allowed them do some play testing on the product.

The end

That's it. I didn't look at everything, just what I found interesting and had time to. I learned a little bit about early 1990s video game development and maybe I can put some of that knowledge to use in the present.

Maybe in the future I will visit this again. I was really looking forward seeing how the the Playstation port of the game compared, but I am guessing EA cannot actually release such a thing even if they have the source code still.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK