/* ** 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 . */ /*****************************************************************************\ 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 #ifdef COPY_PROTECT #include "Protect.h" #include #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: "<= 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: "<