WinMain.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597
  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. *** Confidential - Westwood Studios ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Installer *
  23. * *
  24. * $Archive:: /Commando/Code/Installer/WinMain.cpp $*
  25. * *
  26. * $Author:: Ian_l $*
  27. * *
  28. * $Modtime:: 1/22/02 2:17p $*
  29. * *
  30. * $Revision:: 12 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. // Include files.
  36. #include "Argv.h"
  37. #include "Bufffile.h"
  38. #include "Chunkio.h"
  39. #include "ErrorHandler.h"
  40. #include "FFactory.h"
  41. #include "Installer.h"
  42. #include "MixFile.h"
  43. #include "Msgloop.h"
  44. #include "RAMFileFactory.h"
  45. #include "Resource.h"
  46. #include "SafeTimer.h"
  47. #include "SaveLoad.h"
  48. #include "Timer.h"
  49. #include "Translator.h"
  50. #include "Win.h"
  51. #include "WW3D.h"
  52. #include "WWFile.h"
  53. #include <malloc.h>
  54. #include <dbt.h>
  55. // Defines.
  56. #define AUTORUN_MUTEX_OBJECT TEXT("01AF9993-3492-11d3-8F6F-0060089C05B1")
  57. #define APPLICATION_MUTEX_OBJECT TEXT("C6D925A3-7A9B-4ca3-866D-8B4D506C3665")
  58. // Static variables.
  59. // Foward declarations.
  60. LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
  61. ATOM MyRegisterClass (HINSTANCE hInstance);
  62. bool Is_Autorun_Running();
  63. bool Is_Application_Running();
  64. bool Is_Win_95_Or_Above();
  65. bool Is_Win_2K_Or_Above();
  66. bool Running_As_Administrator();
  67. void Prog_End();
  68. /***********************************************************************************************
  69. * WinMain -- Entry point to program. *
  70. * *
  71. * INPUT: *
  72. * *
  73. * OUTPUT: *
  74. * *
  75. * WARNINGS: *
  76. * *
  77. * HISTORY: *
  78. * 08/22/01 IML : Created. *
  79. *=============================================================================================*/
  80. int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
  81. {
  82. int result;
  83. // Initialize command line parser.
  84. ArgvClass::Init (lpCmdLine);
  85. TranslateDBClass::Initialize();
  86. try {
  87. // Set global copy of program instance.
  88. ProgramInstance = hInstance;
  89. // Ensure Autorun or another instance of this application is not running.
  90. if ((!Is_Autorun_Running()) && (!Is_Application_Running())) {
  91. // WARNING: Can only raise fatal system errors (not fatal application errors) until
  92. // the translation table has been loaded.
  93. const char *installmixname = "InstallMix.dat";
  94. const char *installstringsfilename = "InstallStrings.tdb";
  95. RAMFileFactoryClass ramfilefactory;
  96. MixFileFactoryClass mixfilefactory (installmixname, &ramfilefactory);
  97. FileClass *file;
  98. // Perform application initialization.
  99. // An invalid mixfile factory indicates a read error.
  100. if (!mixfilefactory.Is_Valid()) FATAL_SYSTEM_ERROR;
  101. _TheFileFactory = &mixfilefactory;
  102. // Load the translation table.
  103. file = _TheFileFactory->Get_File (installstringsfilename);
  104. if (file == NULL) {
  105. // Output an appropriate Windows error message.
  106. SetLastError (2);
  107. FATAL_SYSTEM_ERROR;
  108. }
  109. file->Open (FileClass::READ);
  110. if (file->Is_Available()) {
  111. ChunkLoadClass cload (file);
  112. SaveLoadSystemClass::Load (cload);
  113. } else {
  114. // Output an appropriate Windows error message.
  115. SetLastError (2);
  116. FATAL_SYSTEM_ERROR;
  117. }
  118. file->Close();
  119. _TheFileFactory->Return_File (file);
  120. // Check for valid OS.
  121. if (!(Is_Win_95_Or_Above() || Is_Win_2K_Or_Above())) FATAL_APP_ERROR (IDS_BAD_OS);
  122. // Check for Administrator rights under Win 2k or above.
  123. if (Is_Win_2K_Or_Above()) {
  124. if (!Running_As_Administrator()) FATAL_APP_ERROR (IDS_NOT_ADMINISTRATOR);
  125. }
  126. // Register function to be called at exit.
  127. atexit (Prog_End);
  128. MyRegisterClass (hInstance);
  129. MainWindow = CreateWindow (RxStringClass (IDS_APPLICATION_MAIN_WINDOW),
  130. StringClass (TxWideStringClass (IDS_APPLICATION_NAME)),
  131. WS_SYSMENU|WS_CAPTION|WS_MINIMIZEBOX|WS_CLIPCHILDREN,
  132. 0, 0, 0, 0,
  133. NULL, NULL,
  134. hInstance, NULL);
  135. if (MainWindow == NULL) FATAL_SYSTEM_ERROR;
  136. ShowCursor (false);
  137. _Installer.Install (&mixfilefactory);
  138. ShowCursor (true);
  139. }
  140. result = 1;
  141. } catch (const WideStringClass &errormessage) {
  142. // Catch handler for fatal errors.
  143. DestroyWindow (MainWindow);
  144. Windows_Message_Handler();
  145. Message_Box (TxWideStringClass (IDS_APPLICATION_ERROR), errormessage);
  146. result = 0;
  147. }
  148. TranslateDBClass::Shutdown();
  149. ArgvClass::Free();
  150. return (result);
  151. }
  152. /***********************************************************************************************
  153. * MyRegisterClass -- *
  154. * *
  155. * INPUT: *
  156. * *
  157. * OUTPUT: *
  158. * *
  159. * WARNINGS: *
  160. * *
  161. * HISTORY: *
  162. * 08/22/01 IML : Created. *
  163. *=============================================================================================*/
  164. ATOM MyRegisterClass (HINSTANCE hInstance)
  165. {
  166. static RxStringClass _classname (IDS_APPLICATION_MAIN_WINDOW);
  167. WNDCLASSEX wcex;
  168. wcex.cbSize = sizeof (WNDCLASSEX);
  169. wcex.style = CS_HREDRAW | CS_VREDRAW;
  170. wcex.lpfnWndProc = (WNDPROC)WndProc;
  171. wcex.cbClsExtra = 0;
  172. wcex.cbWndExtra = 0;
  173. wcex.hInstance = hInstance;
  174. wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_INSTALLER);
  175. wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
  176. wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  177. wcex.lpszMenuName = (LPCSTR)IDS_INSTALLER;
  178. wcex.lpszClassName = _classname;
  179. wcex.hIconSm = LoadIcon (hInstance, (LPCTSTR)IDI_INSTALLER);
  180. return RegisterClassEx (&wcex);
  181. }
  182. /***********************************************************************************************
  183. * WndProc -- Windows message handler. *
  184. * *
  185. * INPUT: *
  186. * *
  187. * OUTPUT: *
  188. * *
  189. * WARNINGS: *
  190. * *
  191. * HISTORY: *
  192. * 08/22/01 IML : Created. *
  193. *=============================================================================================*/
  194. LRESULT CALLBACK WndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
  195. {
  196. // Pass this message through to the input handler. If the message
  197. // was processed and requires no further action, then return with
  198. // this information.
  199. if (_Installer.Get_Input() != NULL) {
  200. LRESULT result = 0;
  201. if (_Installer.Get_Input()->ProcessMessage (hWnd, message, wParam, lParam, result)) {
  202. return (result);
  203. }
  204. }
  205. switch (message)
  206. {
  207. case WM_ACTIVATEAPP:
  208. if (wParam && !GameInFocus) {
  209. GameInFocus = true;
  210. } else {
  211. if (!wParam && GameInFocus) {
  212. GameInFocus = false;
  213. }
  214. }
  215. return (0);
  216. case WM_ERASEBKGND:
  217. return (1);
  218. case WM_PAINT:
  219. ValidateRect (hWnd, NULL);
  220. break;
  221. case WM_CREATE:
  222. break;
  223. case WM_DESTROY:
  224. PostQuitMessage (0);
  225. break;
  226. case WM_SYSCOMMAND:
  227. switch (wParam) {
  228. case SC_CLOSE:
  229. // Windows sent us a close message - probably in response to Alt-F4. Ignore it.
  230. return (0);
  231. case SC_SCREENSAVE:
  232. // Windows is about to start the screen saver. If we just return without passing
  233. // this message to DefWindowProc then the screen saver will not be allowed to start.
  234. return (0);
  235. case SC_KEYMENU:
  236. // Ignore all "menu-activation" commands.
  237. return (0);
  238. }
  239. break;
  240. case WM_KEYUP:
  241. // Test for SHIFT + ESC.
  242. if ((wParam & 0xff) == VK_ESCAPE && ((GetKeyState (VK_SHIFT) & 0x8000) != 0x0)) {
  243. _Installer.Cancel_Introduction();
  244. }
  245. #if !NDEBUG
  246. // Test for Print Screen (screenshot).
  247. if ((wParam & 0xff) == VK_SNAPSHOT) {
  248. WW3D::Make_Screen_Shot();
  249. }
  250. #endif
  251. case WM_MOUSEWHEEL:
  252. {
  253. if (_Installer.Get_Input() != NULL) {
  254. _Installer.Get_Input()->Add_Mouse_Wheel (HIWORD (wParam));
  255. return (0);
  256. }
  257. break;
  258. }
  259. case WM_DEVICECHANGE:
  260. {
  261. PDEV_BROADCAST_HDR pdbch;
  262. pdbch = (PDEV_BROADCAST_HDR) lParam;
  263. if (pdbch != NULL) {
  264. if (pdbch->dbch_devicetype == DBT_DEVTYP_VOLUME) {
  265. // Assume that this is the CD-ROM drive.
  266. if (wParam == DBT_DEVICEQUERYREMOVE) {
  267. ShowWindow (hWnd, SW_MINIMIZE);
  268. } else {
  269. if (wParam == DBT_DEVICEARRIVAL) {
  270. if (IsIconic (hWnd)) {
  271. ShowWindow (hWnd, SW_RESTORE);
  272. }
  273. SetForegroundWindow (hWnd);
  274. }
  275. }
  276. return (TRUE);
  277. }
  278. }
  279. break;
  280. }
  281. default:
  282. break;
  283. }
  284. return (DefWindowProc (hWnd, message, wParam, lParam));
  285. }
  286. /***********************************************************************************************
  287. * InstallerClass::Is_Autorun_Running -- Determine whether Autorun (a sister application) is *
  288. * running. *
  289. * *
  290. * INPUT: *
  291. * *
  292. * OUTPUT: *
  293. * *
  294. * WARNINGS: *
  295. * *
  296. * HISTORY: *
  297. * 08/22/01 IML : Created. *
  298. *=============================================================================================*/
  299. bool Is_Autorun_Running()
  300. {
  301. HANDLE autorunmutex = OpenMutex (MUTEX_ALL_ACCESS & SYNCHRONIZE, FALSE, AUTORUN_MUTEX_OBJECT);
  302. if (autorunmutex != NULL) {
  303. HWND window = FindWindow (RxStringClass (IDS_AUTORUN_MAIN_WINDOW), NULL);
  304. if (window) {
  305. CDTimerClass <SafeTimerClass> delaytimer (10 * 1000);
  306. // Hang around for a while to see if Autorun is about to quit...
  307. while ((delaytimer.Value() > 0) && (window != NULL)) {
  308. window = FindWindow (RxStringClass (IDS_AUTORUN_MAIN_WINDOW), NULL);
  309. }
  310. }
  311. CloseHandle (autorunmutex);
  312. // If the Autorun window still exists bring it to the foreground.
  313. if (window) {
  314. if (IsIconic (window)) {
  315. ShowWindow (window, SW_RESTORE);
  316. }
  317. SetForegroundWindow (window);
  318. }
  319. }
  320. return (autorunmutex != NULL);
  321. }
  322. /***********************************************************************************************
  323. * InstallerClass::Is_Application_Running -- Determine whether this application is running. *
  324. * *
  325. * INPUT: *
  326. * *
  327. * OUTPUT: *
  328. * *
  329. * WARNINGS: *
  330. * *
  331. * HISTORY: *
  332. * 08/22/01 IML : Created. *
  333. *=============================================================================================*/
  334. bool Is_Application_Running()
  335. {
  336. HANDLE appmutex = CreateMutex (NULL, FALSE, APPLICATION_MUTEX_OBJECT);
  337. if (appmutex == NULL) FATAL_SYSTEM_ERROR;
  338. // See if the application was already running.
  339. HWND prev = FindWindow (RxStringClass (IDS_APPLICATION_MAIN_WINDOW), NULL);
  340. if (prev) {
  341. if (IsIconic (prev)) {
  342. ShowWindow (prev, SW_RESTORE);
  343. }
  344. SetForegroundWindow (prev);
  345. return (true);
  346. } else {
  347. return (false);
  348. }
  349. }
  350. /***********************************************************************************************
  351. * InstallerClass::Is_Win_95_Or_Above -- *
  352. * *
  353. * INPUT: *
  354. * *
  355. * OUTPUT: *
  356. * *
  357. * WARNINGS: *
  358. * *
  359. * HISTORY: *
  360. * 08/22/01 IML : Created. *
  361. *=============================================================================================*/
  362. bool Is_Win_95_Or_Above()
  363. {
  364. OSVERSIONINFO versioninfo;
  365. BOOL result;
  366. bool validos = false;
  367. versioninfo.dwOSVersionInfoSize = sizeof (versioninfo);
  368. result = GetVersionEx (&versioninfo);
  369. if (result) {
  370. if (versioninfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) {
  371. validos = true;
  372. }
  373. }
  374. return (validos);
  375. }
  376. /***********************************************************************************************
  377. * InstallerClass::Is_Win_2K_Or_Above -- *
  378. * *
  379. * INPUT: *
  380. * *
  381. * OUTPUT: *
  382. * *
  383. * WARNINGS: *
  384. * *
  385. * HISTORY: *
  386. * 08/22/01 IML : Created. *
  387. *=============================================================================================*/
  388. bool Is_Win_2K_Or_Above()
  389. {
  390. OSVERSIONINFO versioninfo;
  391. BOOL result;
  392. bool validos = false;
  393. versioninfo.dwOSVersionInfoSize = sizeof (versioninfo);
  394. result = GetVersionEx (&versioninfo);
  395. if (result) {
  396. if (versioninfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
  397. validos = (versioninfo.dwMajorVersion >= 5) && (versioninfo.dwMinorVersion >= 0);
  398. }
  399. }
  400. return (validos);
  401. }
  402. /***********************************************************************************************
  403. * InstallerClass::Running_As_Administrator -- *
  404. * *
  405. * INPUT: *
  406. * *
  407. * OUTPUT: *
  408. * *
  409. * WARNINGS: *
  410. * *
  411. * HISTORY: *
  412. * 08/22/01 IML : Created. *
  413. *=============================================================================================*/
  414. bool Running_As_Administrator()
  415. {
  416. bool fAdmin;
  417. HANDLE hThread;
  418. TOKEN_GROUPS *ptg = NULL;
  419. DWORD cbTokenGroups;
  420. DWORD dwGroup;
  421. PSID psidAdmin;
  422. SID_IDENTIFIER_AUTHORITY SystemSidAuthority = SECURITY_NT_AUTHORITY;
  423. // Open a handle to the access token for this thread.
  424. if (!OpenThreadToken (GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread)) {
  425. if (GetLastError() == ERROR_NO_TOKEN) {
  426. // If the thread does not have an access token, examine the access token associated with the process.
  427. if (! OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hThread)) {
  428. return (false);
  429. }
  430. } else {
  431. return (false);
  432. }
  433. }
  434. // Query the size of the group information associated with the token. Note that we expect a FALSE result from GetTokenInformation
  435. // because we've given it a NULL buffer. On exit cbTokenGroups will tell the size of the group information.
  436. if (GetTokenInformation (hThread, TokenGroups, NULL, 0, &cbTokenGroups)) {
  437. return (false);
  438. }
  439. // Verify that GetTokenInformation failed for lack of a large enough buffer.
  440. if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
  441. return (false);
  442. }
  443. // Allocate a buffer for the group information. Since _alloca allocates on the stack, we don't have
  444. // to explicitly deallocate it. That happens automatically when we exit this function.
  445. if (!(ptg = (TOKEN_GROUPS*) _alloca (cbTokenGroups))) {
  446. return (false);
  447. }
  448. // Ask for the group information again. This may fail if an administrator has added this account
  449. // to an additional group between our first call to GetTokenInformation and this one.
  450. if (!GetTokenInformation (hThread, TokenGroups, ptg, cbTokenGroups, &cbTokenGroups)) {
  451. return (false);
  452. }
  453. // Create a System Identifier for the Admin group.
  454. if (!AllocateAndInitializeSid (&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmin)) {
  455. return (false);
  456. }
  457. // Iterate through the list of groups for this access token looking for a match against the SID we created above.
  458. fAdmin = false;
  459. for (dwGroup = 0; dwGroup < ptg->GroupCount; dwGroup++) {
  460. if (EqualSid (ptg->Groups[dwGroup].Sid, psidAdmin)) {
  461. fAdmin = true;
  462. break;
  463. }
  464. }
  465. // Explicity deallocate the SID we created.
  466. FreeSid (psidAdmin);
  467. return (fAdmin);
  468. }
  469. /***********************************************************************************************
  470. * Prog_End -- *
  471. * *
  472. * INPUT: *
  473. * *
  474. * OUTPUT: *
  475. * *
  476. * WARNINGS: *
  477. * *
  478. * HISTORY: *
  479. * 08/22/01 IML : Created. *
  480. *=============================================================================================*/
  481. void Prog_End()
  482. {
  483. try {
  484. _Installer.On_Prog_End();
  485. } catch (const WideStringClass &errormessage) {
  486. // Catch handler for fatal errors.
  487. DestroyWindow (MainWindow);
  488. Windows_Message_Handler();
  489. Message_Box (TxWideStringClass (IDS_APPLICATION_ERROR), errormessage);
  490. }
  491. }