| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /*****************************************************************************\
- C O N F I D E N T I A L --- W E S T W O O D S T U D I O S
- *******************************************************************************
- File: main.cpp
- Programmer: Neal Kettler
- StartDate: Feb 6, 1998
- LastUpdate: Feb 10, 1998
- -------------------------------------------------------------------------------
- Launcher application for games/apps using the chat API. This should be
- run by the user and it will start the actual game executable. If a patch
- file has been downloaded the patch will be applied before starting the game.
- This does not download patches or do version checks, the game/app is responsible
- for that. This just applies patches that are in the correct location for the
- game. All patches should be in the "Patches" folder of the app.
- The launcher should have a config file (launcher.cfg) so it knows which apps
- should be checked for patches. The file should look like this:
- # comment
- # RUN = the game to launch
- RUN = . notepad.exe # directory and app name
- # RUN2 = the 2nd launcher if using one
- RUN2 = . wordpad.exe
- # FLAG = time in seconds of when to stop using 2nd launcher
- FLAG = 996778113
- #
- # Sku's to check for patches
- #
- SKU1 = 1100 SOFTWARE\Westwood\WOnline # skus and registry keys
- SKU2 = 1234 SOFTWARE\Westwood\FakeGame
- \*****************************************************************************/
- #include "dialog.h"
- #include "patch.h"
- #include "findpatch.h"
- #include "process.h"
- #include "wdebug.h"
- #include "monod.h"
- #include "filed.h"
- #include "configfile.h"
- #include <windows.h>
- #ifdef COPY_PROTECT
- #include "Protect.h"
- #include <Debug\DebugPrint.h>
- #endif
- #define UPDATE_RETVAL 123456789 // if a program returns this it means it wants to check for patches
- #include "..\combat\specialbuilds.h"
- /*
- #ifdef FREEDEDICATEDSERVER
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeFDS\\"
- #else //FREEDEDICATEDSERVER
- #ifdef MULTIPLAYERDEMO
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeMPDemo\\"
- #else //MULTIPLAYERDEMO
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\Renegade\\"
- #endif //MULTIPLAYERDEMO
- #endif //FREEDEDICATEDSERVER
- */
- #if defined(FREEDEDICATEDSERVER)
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeFDS\\"
- #elif defined(MULTIPLAYERDEMO)
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeMPDemo\\"
- #elif defined(BETACLIENT)
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeBeta\\"
- #else
- #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\Renegade\\"
- #endif
- #define APPLICATION_SUB_KEY_NAME_WOLSETTINGS "WOLSettings\\"
- #define APPLICATION_SUB_KEY_NAME_AUTOSTART "AutoRestartFlag"
- void CreatePrimaryWin(char *prefix);
- void myChdir(char *path);
- bool Get_Restart_Flag(Process &proc, bool &slave);
- void RunGame(char *thePath, ConfigFile &config, Process &proc)
- {
- char patchFile[MAX_PATH];
- bool launchgame = true;
- // MDC 8/23/2001 Wait 3 seconds for the installer to release the mutex
- //
- //
- // TO_FIX - Use installer mutex to see when installer has gone away.
- //Sleep(3000);
- while (true)
- {
- int skuIndex;
- while ((skuIndex = Find_Patch(patchFile, MAX_PATH, config)) != 0)
- {
- //
- // If there is a patch and we are a slave server then we need to exit quick so the master server can apply it.
- //
- bool slave = false;
- bool restart = Get_Restart_Flag(proc, slave);
- if (!restart || !slave) {
- // Pass in the restart flag so that the patch code knows not to bring up the changes dialog.
- Apply_Patch(patchFile, config, skuIndex, !restart);
- launchgame = true;
- } else {
- launchgame = false;
- }
- }
- // launch the game if first pass, or found a patch
- if (launchgame)
- {
- // don't relaunch unless we find a patch (or checking for patches)
- launchgame = false;
- myChdir(thePath);
- #ifndef COPY_PROTECT
- Create_Process(proc);
- #else // COPY_PROTECT
- #ifdef OLDWAY
- Protect protect;
- Create_Process(proc);
- protect.SendMappedFileHandle(proc.hProcess, proc.dwThreadID);
- #else
- InitializeProtect();
- Create_Process(proc);
- SendProtectMessage(proc.hProcess, proc.dwThreadID);
- #endif
- #endif // COPY_PROTECT
- DWORD exit_code;
- Wait_Process(proc, &exit_code);
- // Relaunch if the game crashed unexpectedly (the auto restart flag is set).
- bool slave = false;
- if (Get_Restart_Flag(proc, slave)) {
- launchgame = true;
- } else {
- #ifndef MULTIPLAYERDEMO
- if (exit_code == UPDATE_RETVAL)
- {
- // They just want to check for patches
- launchgame = true;
- // Start patchgrabber
- Process patchgrab;
- strcpy(patchgrab.directory,proc.directory); // same dir as game
- strcpy(patchgrab.command,"patchget.dat"); // the program that grabs game patches
- strcpy(patchgrab.args,"");
- Create_Process(patchgrab);
- Wait_Process(patchgrab); // wait for completion
- }
- #endif //MULTIPLAYERDEMO
- }
- #ifdef COPY_PROTECT
- #ifndef OLDWAY
- ShutdownProtect();
- #endif
- #endif
- }
- else
- {
- // Don't delete patches if we are a slave server. That's the master servers job.
- bool slave = false;
- bool restart = Get_Restart_Flag(proc, slave);
- if (!slave) {
- Delete_Patches(config); // delete all patches
- }
- break;
- }
- }
- }
- // The other launcher will handle itself. Just fire and forget.
- void RunLauncher(char *thePath, Process &proc)
- {
- myChdir(thePath);
- Create_Process(proc);
- }
- //
- // Called by WinMain
- //
- int main(int argc, char *argv[])
- {
- char patchFile[MAX_PATH];
- char cwd[MAX_PATH]; // save current directory before game start
- _getcwd(cwd, MAX_PATH);
- InitCommonControls();
- // Goto the folder where launcher is installed
- myChdir(argv[0]);
- // extract the program name from argv[0]. Change the extension to
- // .lcf (Launcher ConFig). This is the name of our config file.
- char configName[MAX_PATH + 3];
- strcpy(configName, argv[0]);
- char* extension = configName;
- char* tempptr;
- while ((tempptr = strchr(extension + 1, '.')))
- {
- extension = tempptr;
- }
- if (*extension == '.')
- {
- *extension = 0;
- }
- #ifdef DEBUG
- ///MonoD outputDevice;
- char debugFile[MAX_PATH + 3];
- strcpy(debugFile, configName);
- strcat(debugFile, ".txt");
- // strcpy(debugLogName, strrchr(configName, '\\'));
- // strcat(debugLogName, "Log");
- FileD outputDevice(debugFile);
- MsgManager::setAllStreams(&outputDevice);
- DBGMSG("Launcher initialized");
- #endif
- strcat(configName, ".lcf");
- DBGMSG("Config Name: "<<configName);
- ConfigFile config;
- FILE* in = fopen(configName, "r");
- if (in == NULL)
- {
- MessageBox(NULL,"You must run the game from its install directory.",
- "Launcher config file missing",MB_OK);
- exit(-1);
- }
- bit8 ok = config.readFile(in);
- fclose(in);
- if (ok == FALSE)
- {
- MessageBox(NULL,"File 'launcher.cfg' is corrupt","Error",MB_OK);
- exit(-1);
- }
- // Load process info
- Process proc;
- Read_Process_Info(config,proc);
- // Possible 2nd EXE
- Process proc2;
- int hasSecondEXE = Read_Process_Info(config,proc2,"RUN2");
- DBGMSG("Read process info");
- for (int i = 1; i < argc; i++)
- {
- strcat(proc.args, " ");
- strcat(proc.args, argv[i]);
- if (hasSecondEXE)
- {
- strcat(proc2.args, " ");
- strcat(proc2.args, argv[i]);
- }
- }
- DBGMSG("ARGS: "<<proc.args);
- // Just spawn the patchgrabber & apply any patches it downloads
- if ((argc >= 2) && (strcmp(argv[1], "GrabPatches") == 0))
- {
- // Start patchgrabber
- Process patchgrab;
- // same dir as game
- strcpy(patchgrab.directory, proc.directory);
- // the program that grabs game patches
- strcpy(patchgrab.command, "patchget.dat");
- strcpy(patchgrab.args, "");
- Create_Process(patchgrab);
- Wait_Process(patchgrab);
- // Apply any patches I find
- int skuIndex;
- while ((skuIndex = Find_Patch(patchFile, MAX_PATH, config)) != 0)
- {
- Apply_Patch(patchFile, config, skuIndex);
- }
- myChdir(cwd);
- return 0;
- }
- // Look for patch file(s) to apply
- bool launchgame = true;
- time_t cutoffTime = 0;
- if (hasSecondEXE)
- {
- Wstring timeStr;
- if (config.getString("FLAG", timeStr)!=FALSE)
- {
- cutoffTime = atoi(timeStr.get());
- }
- if (cutoffTime == 0)
- {
- // We didn't have the FLAG parameter; somebody's been hacking. No game for you! Bad hacker!
- DBGMSG("Saw cutoffTime of 0; real time is " << time(NULL));
- MessageBox(NULL,"File 'launcher.cfg' is corrupt","Error",MB_OK);
- exit(-1);
- }
- if (time(NULL) > cutoffTime)
- {
- // The future is now! Just run the game.
- RunGame(argv[0], config, proc);
- }
- else
- {
- // Its still early in the product's lifetime, so run the 2nd (SafeDisk'd) launcher.
- // We don't have to wait around since it'll do the entire talk to game, look for patches,
- // etc. deal.
- RunLauncher(argv[0], proc2);
- }
- }
- else
- {
- // We're the second (or only) launcher, so act normally
- RunGame(argv[0], config, proc);
- }
- myChdir(cwd);
- // Exit normally
- return 0;
- }
- //
- // Create a primary window
- //
- void CreatePrimaryWin(char *prefix)
- {
- char name[256];
- sprintf(name, "launcher_%s", prefix);
- DBGMSG("CreatePrimary: "<<name);
- /*
- ** set up and register window class
- */
- WNDCLASS wc;
- wc.style = CS_HREDRAW | CS_VREDRAW;
- wc.lpfnWndProc = DefWindowProc;
- wc.cbClsExtra = 0; // Don't need any extra class data
- wc.cbWndExtra = 0; // No extra win data
- wc.hInstance = Global_instance;
- wc.hIcon=LoadIcon(Global_instance, MAKEINTRESOURCE(IDI_ICON));
- wc.hCursor = NULL; /////////LoadCursor( NULL, IDC_ARROW );
- wc.hbrBackground = NULL;
- wc.lpszMenuName = name;
- wc.lpszClassName = name;
- RegisterClass(&wc);
- /*
- ** create a window
- */
- HWND hwnd = CreateWindowEx(WS_EX_TOPMOST, name, name, WS_POPUP, 0, 0,
- GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
- NULL, NULL, Global_instance, NULL);
- if(!hwnd)
- {
- DBGMSG("Couldn't make window!");
- }
- else
- {
- DBGMSG("Window created!");
- }
- }
- //void DestroyPrimaryWin(void)
- //{
- // DestroyWindow(PrimaryWin);
- // UnregisterClass(classname);
- //}
- //
- // If given a file, it'll goto it's directory. If on a diff drive,
- // it'll go there.
- //
- void myChdir(char *path)
- {
- char drive[10];
- char dir[255];
- char file[255];
- char ext[64];
- char filepath[513];
- int abc;
- _splitpath( path, drive, dir, file, ext );
- _makepath ( filepath, drive, dir, NULL, NULL );
- if ( filepath[ strlen( filepath ) - 1 ] == '\\' )
- {
- filepath[ strlen( filepath ) - 1 ] = '\0';
- }
- abc = (unsigned)( toupper( filepath[0] ) - 'A' + 1 );
- if ( !_chdrive( abc ))
- {
- abc = chdir( filepath ); // Will fail with ending '\\'
- }
- // should be in proper folder now....
- }
- /***********************************************************************************************
- * Get_Restart_Flag -- Get AutoRestartFlag from the registry *
- * *
- * *
- * *
- * INPUT: Nothing *
- * *
- * OUTPUT: State of flag *
- * *
- * WARNINGS: None *
- * *
- * HISTORY: *
- * 11/5/2001 7:14PM ST : Created *
- *=============================================================================================*/
- bool Get_Restart_Flag(Process &proc, bool &slave)
- {
- HKEY key;
- char args[1024];
- strcpy(args, proc.args);
- strupr(args);
- /*
- ** Is this a slave server according to the command line?
- */
- if (strstr(args, "SLAVE")) {
- slave = true;
- } else {
- slave = false;
- }
- char registry_modifier[256];
- /*
- ** Use a modified registry path if one was specified on the games command line.
- */
- char *regmod = strstr(args, "REGMOD=");
- if (regmod) {
- strcpy(registry_modifier, regmod + 7);
- }
- char regpath[1024];
- strcpy(regpath, APPLICATION_SUB_KEY_NAME);
- if (regmod) {
- strcat(regpath, registry_modifier);
- strcat(regpath, "\\");
- }
- strcat(regpath, APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
- int result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_ALL_ACCESS, &key);
- if (result == ERROR_SUCCESS) {
- unsigned long type;
- unsigned long data = 0;
- unsigned long data_len = sizeof(data);
- if ((RegQueryValueEx(key, APPLICATION_SUB_KEY_NAME_AUTOSTART, NULL, &type, (LPBYTE)&data, &data_len) == ERROR_SUCCESS) && (type == REG_DWORD)) {
- return((data != 0) ? true : false);
- }
- }
- return(false);
- }
|