main.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*****************************************************************************\
  19. 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
  20. *******************************************************************************
  21. File: main.cpp
  22. Programmer: Neal Kettler
  23. StartDate: Feb 6, 1998
  24. LastUpdate: Feb 10, 1998
  25. -------------------------------------------------------------------------------
  26. Launcher application for games/apps using the chat API. This should be
  27. run by the user and it will start the actual game executable. If a patch
  28. file has been downloaded the patch will be applied before starting the game.
  29. This does not download patches or do version checks, the game/app is responsible
  30. for that. This just applies patches that are in the correct location for the
  31. game. All patches should be in the "Patches" folder of the app.
  32. The launcher should have a config file (launcher.cfg) so it knows which apps
  33. should be checked for patches. The file should look like this:
  34. # comment
  35. # RUN = the game to launch
  36. RUN = . notepad.exe # directory and app name
  37. # RUN2 = the 2nd launcher if using one
  38. RUN2 = . wordpad.exe
  39. # FLAG = time in seconds of when to stop using 2nd launcher
  40. FLAG = 996778113
  41. #
  42. # Sku's to check for patches
  43. #
  44. SKU1 = 1100 SOFTWARE\Westwood\WOnline # skus and registry keys
  45. SKU2 = 1234 SOFTWARE\Westwood\FakeGame
  46. \*****************************************************************************/
  47. #include "dialog.h"
  48. #include "patch.h"
  49. #include "findpatch.h"
  50. #include "process.h"
  51. #include "wdebug.h"
  52. #include "monod.h"
  53. #include "filed.h"
  54. #include "configfile.h"
  55. #include <windows.h>
  56. #ifdef COPY_PROTECT
  57. #include "Protect.h"
  58. #include <Debug\DebugPrint.h>
  59. #endif
  60. #define UPDATE_RETVAL 123456789 // if a program returns this it means it wants to check for patches
  61. #include "..\combat\specialbuilds.h"
  62. /*
  63. #ifdef FREEDEDICATEDSERVER
  64. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeFDS\\"
  65. #else //FREEDEDICATEDSERVER
  66. #ifdef MULTIPLAYERDEMO
  67. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeMPDemo\\"
  68. #else //MULTIPLAYERDEMO
  69. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\Renegade\\"
  70. #endif //MULTIPLAYERDEMO
  71. #endif //FREEDEDICATEDSERVER
  72. */
  73. #if defined(FREEDEDICATEDSERVER)
  74. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeFDS\\"
  75. #elif defined(MULTIPLAYERDEMO)
  76. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeMPDemo\\"
  77. #elif defined(BETACLIENT)
  78. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\RenegadeBeta\\"
  79. #else
  80. #define APPLICATION_SUB_KEY_NAME "Software\\Westwood\\Renegade\\"
  81. #endif
  82. #define APPLICATION_SUB_KEY_NAME_WOLSETTINGS "WOLSettings\\"
  83. #define APPLICATION_SUB_KEY_NAME_AUTOSTART "AutoRestartFlag"
  84. void CreatePrimaryWin(char *prefix);
  85. void myChdir(char *path);
  86. bool Get_Restart_Flag(Process &proc, bool &slave);
  87. void RunGame(char *thePath, ConfigFile &config, Process &proc)
  88. {
  89. char patchFile[MAX_PATH];
  90. bool launchgame = true;
  91. // MDC 8/23/2001 Wait 3 seconds for the installer to release the mutex
  92. //
  93. //
  94. // TO_FIX - Use installer mutex to see when installer has gone away.
  95. //Sleep(3000);
  96. while (true)
  97. {
  98. int skuIndex;
  99. while ((skuIndex = Find_Patch(patchFile, MAX_PATH, config)) != 0)
  100. {
  101. //
  102. // If there is a patch and we are a slave server then we need to exit quick so the master server can apply it.
  103. //
  104. bool slave = false;
  105. bool restart = Get_Restart_Flag(proc, slave);
  106. if (!restart || !slave) {
  107. // Pass in the restart flag so that the patch code knows not to bring up the changes dialog.
  108. Apply_Patch(patchFile, config, skuIndex, !restart);
  109. launchgame = true;
  110. } else {
  111. launchgame = false;
  112. }
  113. }
  114. // launch the game if first pass, or found a patch
  115. if (launchgame)
  116. {
  117. // don't relaunch unless we find a patch (or checking for patches)
  118. launchgame = false;
  119. myChdir(thePath);
  120. #ifndef COPY_PROTECT
  121. Create_Process(proc);
  122. #else // COPY_PROTECT
  123. #ifdef OLDWAY
  124. Protect protect;
  125. Create_Process(proc);
  126. protect.SendMappedFileHandle(proc.hProcess, proc.dwThreadID);
  127. #else
  128. InitializeProtect();
  129. Create_Process(proc);
  130. SendProtectMessage(proc.hProcess, proc.dwThreadID);
  131. #endif
  132. #endif // COPY_PROTECT
  133. DWORD exit_code;
  134. Wait_Process(proc, &exit_code);
  135. // Relaunch if the game crashed unexpectedly (the auto restart flag is set).
  136. bool slave = false;
  137. if (Get_Restart_Flag(proc, slave)) {
  138. launchgame = true;
  139. } else {
  140. #ifndef MULTIPLAYERDEMO
  141. if (exit_code == UPDATE_RETVAL)
  142. {
  143. // They just want to check for patches
  144. launchgame = true;
  145. // Start patchgrabber
  146. Process patchgrab;
  147. strcpy(patchgrab.directory,proc.directory); // same dir as game
  148. strcpy(patchgrab.command,"patchget.dat"); // the program that grabs game patches
  149. strcpy(patchgrab.args,"");
  150. Create_Process(patchgrab);
  151. Wait_Process(patchgrab); // wait for completion
  152. }
  153. #endif //MULTIPLAYERDEMO
  154. }
  155. #ifdef COPY_PROTECT
  156. #ifndef OLDWAY
  157. ShutdownProtect();
  158. #endif
  159. #endif
  160. }
  161. else
  162. {
  163. // Don't delete patches if we are a slave server. That's the master servers job.
  164. bool slave = false;
  165. bool restart = Get_Restart_Flag(proc, slave);
  166. if (!slave) {
  167. Delete_Patches(config); // delete all patches
  168. }
  169. break;
  170. }
  171. }
  172. }
  173. // The other launcher will handle itself. Just fire and forget.
  174. void RunLauncher(char *thePath, Process &proc)
  175. {
  176. myChdir(thePath);
  177. Create_Process(proc);
  178. }
  179. //
  180. // Called by WinMain
  181. //
  182. int main(int argc, char *argv[])
  183. {
  184. char patchFile[MAX_PATH];
  185. char cwd[MAX_PATH]; // save current directory before game start
  186. _getcwd(cwd, MAX_PATH);
  187. InitCommonControls();
  188. // Goto the folder where launcher is installed
  189. myChdir(argv[0]);
  190. // extract the program name from argv[0]. Change the extension to
  191. // .lcf (Launcher ConFig). This is the name of our config file.
  192. char configName[MAX_PATH + 3];
  193. strcpy(configName, argv[0]);
  194. char* extension = configName;
  195. char* tempptr;
  196. while ((tempptr = strchr(extension + 1, '.')))
  197. {
  198. extension = tempptr;
  199. }
  200. if (*extension == '.')
  201. {
  202. *extension = 0;
  203. }
  204. #ifdef DEBUG
  205. ///MonoD outputDevice;
  206. char debugFile[MAX_PATH + 3];
  207. strcpy(debugFile, configName);
  208. strcat(debugFile, ".txt");
  209. // strcpy(debugLogName, strrchr(configName, '\\'));
  210. // strcat(debugLogName, "Log");
  211. FileD outputDevice(debugFile);
  212. MsgManager::setAllStreams(&outputDevice);
  213. DBGMSG("Launcher initialized");
  214. #endif
  215. strcat(configName, ".lcf");
  216. DBGMSG("Config Name: "<<configName);
  217. ConfigFile config;
  218. FILE* in = fopen(configName, "r");
  219. if (in == NULL)
  220. {
  221. MessageBox(NULL,"You must run the game from its install directory.",
  222. "Launcher config file missing",MB_OK);
  223. exit(-1);
  224. }
  225. bit8 ok = config.readFile(in);
  226. fclose(in);
  227. if (ok == FALSE)
  228. {
  229. MessageBox(NULL,"File 'launcher.cfg' is corrupt","Error",MB_OK);
  230. exit(-1);
  231. }
  232. // Load process info
  233. Process proc;
  234. Read_Process_Info(config,proc);
  235. // Possible 2nd EXE
  236. Process proc2;
  237. int hasSecondEXE = Read_Process_Info(config,proc2,"RUN2");
  238. DBGMSG("Read process info");
  239. for (int i = 1; i < argc; i++)
  240. {
  241. strcat(proc.args, " ");
  242. strcat(proc.args, argv[i]);
  243. if (hasSecondEXE)
  244. {
  245. strcat(proc2.args, " ");
  246. strcat(proc2.args, argv[i]);
  247. }
  248. }
  249. DBGMSG("ARGS: "<<proc.args);
  250. // Just spawn the patchgrabber & apply any patches it downloads
  251. if ((argc >= 2) && (strcmp(argv[1], "GrabPatches") == 0))
  252. {
  253. // Start patchgrabber
  254. Process patchgrab;
  255. // same dir as game
  256. strcpy(patchgrab.directory, proc.directory);
  257. // the program that grabs game patches
  258. strcpy(patchgrab.command, "patchget.dat");
  259. strcpy(patchgrab.args, "");
  260. Create_Process(patchgrab);
  261. Wait_Process(patchgrab);
  262. // Apply any patches I find
  263. int skuIndex;
  264. while ((skuIndex = Find_Patch(patchFile, MAX_PATH, config)) != 0)
  265. {
  266. Apply_Patch(patchFile, config, skuIndex);
  267. }
  268. myChdir(cwd);
  269. return 0;
  270. }
  271. // Look for patch file(s) to apply
  272. bool launchgame = true;
  273. time_t cutoffTime = 0;
  274. if (hasSecondEXE)
  275. {
  276. Wstring timeStr;
  277. if (config.getString("FLAG", timeStr)!=FALSE)
  278. {
  279. cutoffTime = atoi(timeStr.get());
  280. }
  281. if (cutoffTime == 0)
  282. {
  283. // We didn't have the FLAG parameter; somebody's been hacking. No game for you! Bad hacker!
  284. DBGMSG("Saw cutoffTime of 0; real time is " << time(NULL));
  285. MessageBox(NULL,"File 'launcher.cfg' is corrupt","Error",MB_OK);
  286. exit(-1);
  287. }
  288. if (time(NULL) > cutoffTime)
  289. {
  290. // The future is now! Just run the game.
  291. RunGame(argv[0], config, proc);
  292. }
  293. else
  294. {
  295. // Its still early in the product's lifetime, so run the 2nd (SafeDisk'd) launcher.
  296. // We don't have to wait around since it'll do the entire talk to game, look for patches,
  297. // etc. deal.
  298. RunLauncher(argv[0], proc2);
  299. }
  300. }
  301. else
  302. {
  303. // We're the second (or only) launcher, so act normally
  304. RunGame(argv[0], config, proc);
  305. }
  306. myChdir(cwd);
  307. // Exit normally
  308. return 0;
  309. }
  310. //
  311. // Create a primary window
  312. //
  313. void CreatePrimaryWin(char *prefix)
  314. {
  315. char name[256];
  316. sprintf(name, "launcher_%s", prefix);
  317. DBGMSG("CreatePrimary: "<<name);
  318. /*
  319. ** set up and register window class
  320. */
  321. WNDCLASS wc;
  322. wc.style = CS_HREDRAW | CS_VREDRAW;
  323. wc.lpfnWndProc = DefWindowProc;
  324. wc.cbClsExtra = 0; // Don't need any extra class data
  325. wc.cbWndExtra = 0; // No extra win data
  326. wc.hInstance = Global_instance;
  327. wc.hIcon=LoadIcon(Global_instance, MAKEINTRESOURCE(IDI_ICON));
  328. wc.hCursor = NULL; /////////LoadCursor( NULL, IDC_ARROW );
  329. wc.hbrBackground = NULL;
  330. wc.lpszMenuName = name;
  331. wc.lpszClassName = name;
  332. RegisterClass(&wc);
  333. /*
  334. ** create a window
  335. */
  336. HWND hwnd = CreateWindowEx(WS_EX_TOPMOST, name, name, WS_POPUP, 0, 0,
  337. GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
  338. NULL, NULL, Global_instance, NULL);
  339. if(!hwnd)
  340. {
  341. DBGMSG("Couldn't make window!");
  342. }
  343. else
  344. {
  345. DBGMSG("Window created!");
  346. }
  347. }
  348. //void DestroyPrimaryWin(void)
  349. //{
  350. // DestroyWindow(PrimaryWin);
  351. // UnregisterClass(classname);
  352. //}
  353. //
  354. // If given a file, it'll goto it's directory. If on a diff drive,
  355. // it'll go there.
  356. //
  357. void myChdir(char *path)
  358. {
  359. char drive[10];
  360. char dir[255];
  361. char file[255];
  362. char ext[64];
  363. char filepath[513];
  364. int abc;
  365. _splitpath( path, drive, dir, file, ext );
  366. _makepath ( filepath, drive, dir, NULL, NULL );
  367. if ( filepath[ strlen( filepath ) - 1 ] == '\\' )
  368. {
  369. filepath[ strlen( filepath ) - 1 ] = '\0';
  370. }
  371. abc = (unsigned)( toupper( filepath[0] ) - 'A' + 1 );
  372. if ( !_chdrive( abc ))
  373. {
  374. abc = chdir( filepath ); // Will fail with ending '\\'
  375. }
  376. // should be in proper folder now....
  377. }
  378. /***********************************************************************************************
  379. * Get_Restart_Flag -- Get AutoRestartFlag from the registry *
  380. * *
  381. * *
  382. * *
  383. * INPUT: Nothing *
  384. * *
  385. * OUTPUT: State of flag *
  386. * *
  387. * WARNINGS: None *
  388. * *
  389. * HISTORY: *
  390. * 11/5/2001 7:14PM ST : Created *
  391. *=============================================================================================*/
  392. bool Get_Restart_Flag(Process &proc, bool &slave)
  393. {
  394. HKEY key;
  395. char args[1024];
  396. strcpy(args, proc.args);
  397. strupr(args);
  398. /*
  399. ** Is this a slave server according to the command line?
  400. */
  401. if (strstr(args, "SLAVE")) {
  402. slave = true;
  403. } else {
  404. slave = false;
  405. }
  406. char registry_modifier[256];
  407. /*
  408. ** Use a modified registry path if one was specified on the games command line.
  409. */
  410. char *regmod = strstr(args, "REGMOD=");
  411. if (regmod) {
  412. strcpy(registry_modifier, regmod + 7);
  413. }
  414. char regpath[1024];
  415. strcpy(regpath, APPLICATION_SUB_KEY_NAME);
  416. if (regmod) {
  417. strcat(regpath, registry_modifier);
  418. strcat(regpath, "\\");
  419. }
  420. strcat(regpath, APPLICATION_SUB_KEY_NAME_WOLSETTINGS);
  421. int result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, regpath, 0, KEY_ALL_ACCESS, &key);
  422. if (result == ERROR_SUCCESS) {
  423. unsigned long type;
  424. unsigned long data = 0;
  425. unsigned long data_len = sizeof(data);
  426. if ((RegQueryValueEx(key, APPLICATION_SUB_KEY_NAME_AUTOSTART, NULL, &type, (LPBYTE)&data, &data_len) == ERROR_SUCCESS) && (type == REG_DWORD)) {
  427. return((data != 0) ? true : false);
  428. }
  429. }
  430. return(false);
  431. }