WinMain.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: WinMain.cpp //////////////////////////////////////////////////////////
  24. //
  25. // Entry point for game application
  26. //
  27. // Author: Colin Day, April 2001
  28. //
  29. ///////////////////////////////////////////////////////////////////////////////
  30. // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
  31. #define WIN32_LEAN_AND_MEAN // only bare bones windows stuff wanted
  32. #include <windows.h>
  33. #include <stdlib.h>
  34. #include <crtdbg.h>
  35. #include <eh.h>
  36. #include <ole2.h>
  37. #include <dbt.h>
  38. // USER INCLUDES //////////////////////////////////////////////////////////////
  39. #include "WinMain.h"
  40. #include "Lib/BaseType.h"
  41. #include "Common/CopyProtection.h"
  42. #include "Common/CriticalSection.h"
  43. #include "Common/GlobalData.h"
  44. #include "Common/GameEngine.h"
  45. #include "Common/GameSounds.h"
  46. #include "Common/Debug.h"
  47. #include "Common/GameMemory.h"
  48. #include "Common/SafeDisc/CdaPfn.h"
  49. #include "Common/StackDump.h"
  50. #include "Common/MessageStream.h"
  51. #include "Common/Registry.h"
  52. #include "Common/Team.h"
  53. #include "GameClient/InGameUI.h"
  54. #include "GameClient/GameClient.h"
  55. #include "GameLogic/GameLogic.h" ///< @todo for demo, remove
  56. #include "GameClient/Mouse.h"
  57. #include "GameClient/IMEManager.h"
  58. #include "Win32Device/GameClient/Win32Mouse.h"
  59. #include "Win32Device/Common/Win32GameEngine.h"
  60. #include "Common/Version.h"
  61. #include "BuildVersion.h"
  62. #include "GeneratedVersion.h"
  63. #include "Resource.h"
  64. #include <rts/profile.h>
  65. #ifdef _INTERNAL
  66. // for occasional debugging...
  67. //#pragma optimize("", off)
  68. //#pragma message("************************************** WARNING, optimization disabled for debugging purposes")
  69. #endif
  70. // GLOBALS ////////////////////////////////////////////////////////////////////
  71. HINSTANCE ApplicationHInstance = NULL; ///< our application instance
  72. HWND ApplicationHWnd = NULL; ///< our application window handle
  73. Bool ApplicationIsWindowed = false;
  74. Win32Mouse *TheWin32Mouse= NULL; ///< for the WndProc() only
  75. DWORD TheMessageTime = 0; ///< For getting the time that a message was posted from Windows.
  76. const Char *g_strFile = "data\\Generals.str";
  77. const Char *g_csfFile = "data\\%s\\Generals.csf";
  78. char *gAppPrefix = ""; /// So WB can have a different debug log file name.
  79. static HANDLE GeneralsMutex = NULL;
  80. #define GENERALS_GUID "685EAFF2-3216-4265-B047-251C5F4B82F3"
  81. #define DEFAULT_XRESOLUTION 800
  82. #define DEFAULT_YRESOLUTION 600
  83. extern void Reset_D3D_Device(bool active);
  84. static Bool gInitializing = false;
  85. static Bool gDoPaint = true;
  86. static Bool isWinMainActive = false;
  87. static HBITMAP gLoadScreenBitmap = NULL;
  88. //#define DEBUG_WINDOWS_MESSAGES
  89. #ifdef DEBUG_WINDOWS_MESSAGES
  90. static const char *messageToString(unsigned int message)
  91. {
  92. static char name[32];
  93. switch (message)
  94. {
  95. case WM_NULL: return "WM_NULL";
  96. case WM_CREATE: return "WM_CREATE";
  97. case WM_DESTROY: return "WM_DESTROY";
  98. case WM_MOVE: return "WM_MOVE";
  99. case WM_SIZE: return "WM_SIZE";
  100. case WM_ACTIVATE: return "WM_ACTIVATE";
  101. case WM_SETFOCUS: return "WM_SETFOCUS";
  102. case WM_KILLFOCUS: return "WM_KILLFOCUS";
  103. case WM_ENABLE: return "WM_ENABLE";
  104. case WM_SETREDRAW: return "WM_SETREDRAW";
  105. case WM_SETTEXT: return "WM_SETTEXT";
  106. case WM_GETTEXT: return "WM_GETTEXT";
  107. case WM_GETTEXTLENGTH: return "WM_GETTEXTLENGTH";
  108. case WM_PAINT: return "WM_PAINT";
  109. case WM_CLOSE: return "WM_CLOSE";
  110. case WM_QUERYENDSESSION: return "WM_QUERYENDSESSION";
  111. case WM_QUIT: return "WM_QUIT";
  112. case WM_QUERYOPEN: return "WM_QUERYOPEN";
  113. case WM_ERASEBKGND: return "WM_ERASEBKGND";
  114. case WM_SYSCOLORCHANGE: return "WM_SYSCOLORCHANGE";
  115. case WM_ENDSESSION: return "WM_ENDSESSION";
  116. case WM_SHOWWINDOW: return "WM_SHOWWINDOW";
  117. case WM_WININICHANGE: return "WM_WININICHANGE";
  118. case WM_DEVMODECHANGE: return "WM_DEVMODECHANGE";
  119. case WM_ACTIVATEAPP: return "WM_ACTIVATEAPP";
  120. case WM_FONTCHANGE: return "WM_FONTCHANGE";
  121. case WM_TIMECHANGE: return "WM_TIMECHANGE";
  122. case WM_CANCELMODE: return "WM_CANCELMODE";
  123. case WM_SETCURSOR: return "WM_SETCURSOR";
  124. case WM_MOUSEACTIVATE: return "WM_MOUSEACTIVATE";
  125. case WM_CHILDACTIVATE: return "WM_CHILDACTIVATE";
  126. case WM_QUEUESYNC: return "WM_QUEUESYNC";
  127. case WM_GETMINMAXINFO: return "WM_GETMINMAXINFO";
  128. case WM_PAINTICON: return "WM_PAINTICON";
  129. case WM_ICONERASEBKGND: return "WM_ICONERASEBKGND";
  130. case WM_NEXTDLGCTL: return "WM_NEXTDLGCTL";
  131. case WM_SPOOLERSTATUS: return "WM_SPOOLERSTATUS";
  132. case WM_DRAWITEM: return "WM_DRAWITEM";
  133. case WM_MEASUREITEM: return "WM_MEASUREITEM";
  134. case WM_DELETEITEM: return "WM_DELETEITEM";
  135. case WM_VKEYTOITEM: return "WM_VKEYTOITEM";
  136. case WM_CHARTOITEM: return "WM_CHARTOITEM";
  137. case WM_SETFONT: return "WM_SETFONT";
  138. case WM_GETFONT: return "WM_GETFONT";
  139. case WM_SETHOTKEY: return "WM_SETHOTKEY";
  140. case WM_GETHOTKEY: return "WM_GETHOTKEY";
  141. case WM_QUERYDRAGICON: return "WM_QUERYDRAGICON";
  142. case WM_COMPAREITEM: return "WM_COMPAREITEM";
  143. case WM_COMPACTING: return "WM_COMPACTING";
  144. case WM_COMMNOTIFY: return "WM_COMMNOTIFY";
  145. case WM_WINDOWPOSCHANGING: return "WM_WINDOWPOSCHANGING";
  146. case WM_WINDOWPOSCHANGED: return "WM_WINDOWPOSCHANGED";
  147. case WM_POWER: return "WM_POWER";
  148. case WM_COPYDATA: return "WM_COPYDATA";
  149. case WM_CANCELJOURNAL: return "WM_CANCELJOURNAL";
  150. case WM_NOTIFY: return "WM_NOTIFY";
  151. case WM_INPUTLANGCHANGEREQUEST: return "WM_INPUTLANGCHANGEREQUES";
  152. case WM_INPUTLANGCHANGE: return "WM_INPUTLANGCHANGE";
  153. case WM_TCARD: return "WM_TCARD";
  154. case WM_HELP: return "WM_HELP";
  155. case WM_USERCHANGED: return "WM_USERCHANGED";
  156. case WM_NOTIFYFORMAT: return "WM_NOTIFYFORMAT";
  157. case WM_CONTEXTMENU: return "WM_CONTEXTMENU";
  158. case WM_STYLECHANGING: return "WM_STYLECHANGING";
  159. case WM_STYLECHANGED: return "WM_STYLECHANGED";
  160. case WM_DISPLAYCHANGE: return "WM_DISPLAYCHANGE";
  161. case WM_GETICON: return "WM_GETICON";
  162. case WM_SETICON: return "WM_SETICON";
  163. case WM_NCCREATE: return "WM_NCCREATE";
  164. case WM_NCDESTROY: return "WM_NCDESTROY";
  165. case WM_NCCALCSIZE: return "WM_NCCALCSIZE";
  166. case WM_NCHITTEST: return "WM_NCHITTEST";
  167. case WM_NCPAINT: return "WM_NCPAINT";
  168. case WM_NCACTIVATE: return "WM_NCACTIVATE";
  169. case WM_GETDLGCODE: return "WM_GETDLGCODE";
  170. case WM_SYNCPAINT: return "WM_SYNCPAINT";
  171. case WM_NCMOUSEMOVE: return "WM_NCMOUSEMOVE";
  172. case WM_NCLBUTTONDOWN: return "WM_NCLBUTTONDOWN";
  173. case WM_NCLBUTTONUP: return "WM_NCLBUTTONUP";
  174. case WM_NCLBUTTONDBLCLK: return "WM_NCLBUTTONDBLCLK";
  175. case WM_NCRBUTTONDOWN: return "WM_NCRBUTTONDOWN";
  176. case WM_NCRBUTTONUP: return "WM_NCRBUTTONUP";
  177. case WM_NCRBUTTONDBLCLK: return "WM_NCRBUTTONDBLCLK";
  178. case WM_NCMBUTTONDOWN: return "WM_NCMBUTTONDOWN";
  179. case WM_NCMBUTTONUP: return "WM_NCMBUTTONUP";
  180. case WM_NCMBUTTONDBLCLK: return "WM_NCMBUTTONDBLCLK";
  181. case WM_KEYDOWN: return "WM_KEYDOWN";
  182. case WM_KEYUP: return "WM_KEYUP";
  183. case WM_CHAR: return "WM_CHAR";
  184. case WM_DEADCHAR: return "WM_DEADCHAR";
  185. case WM_SYSKEYDOWN: return "WM_SYSKEYDOWN";
  186. case WM_SYSKEYUP: return "WM_SYSKEYUP";
  187. case WM_SYSCHAR: return "WM_SYSCHAR";
  188. case WM_SYSDEADCHAR: return "WM_SYSDEADCHAR";
  189. case WM_KEYLAST: return "WM_KEYLAST";
  190. case WM_IME_STARTCOMPOSITION: return "WM_IME_STARTCOMPOSITION";
  191. case WM_IME_ENDCOMPOSITION: return "WM_IME_ENDCOMPOSITION";
  192. case WM_IME_COMPOSITION: return "WM_IME_COMPOSITION";
  193. case WM_INITDIALOG: return "WM_INITDIALOG";
  194. case WM_COMMAND: return "WM_COMMAND";
  195. case WM_SYSCOMMAND: return "WM_SYSCOMMAND";
  196. case WM_TIMER: return "WM_TIMER";
  197. case WM_HSCROLL: return "WM_HSCROLL";
  198. case WM_VSCROLL: return "WM_VSCROLL";
  199. case WM_INITMENU: return "WM_INITMENU";
  200. case WM_INITMENUPOPUP: return "WM_INITMENUPOPUP";
  201. case WM_MENUSELECT: return "WM_MENUSELECT";
  202. case WM_MENUCHAR: return "WM_MENUCHAR";
  203. case WM_ENTERIDLE: return "WM_ENTERIDLE";
  204. case WM_CTLCOLORMSGBOX: return "WM_CTLCOLORMSGBOX";
  205. case WM_CTLCOLOREDIT: return "WM_CTLCOLOREDIT";
  206. case WM_CTLCOLORLISTBOX: return "WM_CTLCOLORLISTBOX";
  207. case WM_CTLCOLORBTN: return "WM_CTLCOLORBTN";
  208. case WM_CTLCOLORDLG: return "WM_CTLCOLORDLG";
  209. case WM_CTLCOLORSCROLLBAR: return "WM_CTLCOLORSCROLLBAR";
  210. case WM_CTLCOLORSTATIC: return "WM_CTLCOLORSTATIC";
  211. case WM_MOUSEMOVE: return "WM_MOUSEMOVE";
  212. case WM_LBUTTONDOWN: return "WM_LBUTTONDOWN";
  213. case WM_LBUTTONUP: return "WM_LBUTTONUP";
  214. case WM_LBUTTONDBLCLK: return "WM_LBUTTONDBLCLK";
  215. case WM_RBUTTONDOWN: return "WM_RBUTTONDOWN";
  216. case WM_RBUTTONUP: return "WM_RBUTTONUP";
  217. case WM_RBUTTONDBLCLK: return "WM_RBUTTONDBLCLK";
  218. case WM_MBUTTONDOWN: return "WM_MBUTTONDOWN";
  219. case WM_MBUTTONUP: return "WM_MBUTTONUP";
  220. case WM_MBUTTONDBLCLK: return "WM_MBUTTONDBLCLK";
  221. // case WM_MOUSEWHEEL: return "WM_MOUSEWHEEL";
  222. case WM_PARENTNOTIFY: return "WM_PARENTNOTIFY";
  223. case WM_ENTERMENULOOP: return "WM_ENTERMENULOOP";
  224. case WM_EXITMENULOOP: return "WM_EXITMENULOOP";
  225. case WM_NEXTMENU: return "WM_NEXTMENU";
  226. case WM_SIZING: return "WM_SIZING";
  227. case WM_CAPTURECHANGED: return "WM_CAPTURECHANGED";
  228. case WM_MOVING: return "WM_MOVING";
  229. case WM_POWERBROADCAST: return "WM_POWERBROADCAST";
  230. case WM_DEVICECHANGE: return "WM_DEVICECHANGE";
  231. case WM_MDICREATE: return "WM_MDICREATE";
  232. case WM_MDIDESTROY: return "WM_MDIDESTROY";
  233. case WM_MDIACTIVATE: return "WM_MDIACTIVATE";
  234. case WM_MDIRESTORE: return "WM_MDIRESTORE";
  235. case WM_MDINEXT: return "WM_MDINEXT";
  236. case WM_MDIMAXIMIZE: return "WM_MDIMAXIMIZE";
  237. case WM_MDITILE: return "WM_MDITILE";
  238. case WM_MDICASCADE: return "WM_MDICASCADE";
  239. case WM_MDIICONARRANGE: return "WM_MDIICONARRANGE";
  240. case WM_MDIGETACTIVE: return "WM_MDIGETACTIVE";
  241. case WM_MDISETMENU: return "WM_MDISETMENU";
  242. case WM_ENTERSIZEMOVE: return "WM_ENTERSIZEMOVE";
  243. case WM_EXITSIZEMOVE: return "WM_EXITSIZEMOVE";
  244. case WM_DROPFILES: return "WM_DROPFILES";
  245. case WM_MDIREFRESHMENU: return "WM_MDIREFRESHMENU";
  246. case WM_IME_SETCONTEXT: return "WM_IME_SETCONTEXT";
  247. case WM_IME_NOTIFY: return "WM_IME_NOTIFY";
  248. case WM_IME_CONTROL: return "WM_IME_CONTROL";
  249. case WM_IME_COMPOSITIONFULL: return "WM_IME_COMPOSITIONFULL";
  250. case WM_IME_SELECT: return "WM_IME_SELECT";
  251. case WM_IME_CHAR: return "WM_IME_CHAR";
  252. case WM_IME_KEYDOWN: return "WM_IME_KEYDOWN";
  253. case WM_IME_KEYUP: return "WM_IME_KEYUP";
  254. // case WM_MOUSEHOVER: return "WM_MOUSEHOVER";
  255. // case WM_MOUSELEAVE: return "WM_MOUSELEAVE";
  256. case WM_CUT: return "WM_CUT";
  257. case WM_COPY: return "WM_COPY";
  258. case WM_PASTE: return "WM_PASTE";
  259. case WM_CLEAR: return "WM_CLEAR";
  260. case WM_UNDO: return "WM_UNDO";
  261. case WM_RENDERFORMAT: return "WM_RENDERFORMAT";
  262. case WM_RENDERALLFORMATS: return "WM_RENDERALLFORMATS";
  263. case WM_DESTROYCLIPBOARD: return "WM_DESTROYCLIPBOARD";
  264. case WM_DRAWCLIPBOARD: return "WM_DRAWCLIPBOARD";
  265. case WM_PAINTCLIPBOARD: return "WM_PAINTCLIPBOARD";
  266. case WM_VSCROLLCLIPBOARD: return "WM_VSCROLLCLIPBOARD";
  267. case WM_SIZECLIPBOARD: return "WM_SIZECLIPBOARD";
  268. case WM_ASKCBFORMATNAME: return "WM_ASKCBFORMATNAME";
  269. case WM_CHANGECBCHAIN: return "WM_CHANGECBCHAIN";
  270. case WM_HSCROLLCLIPBOARD: return "WM_HSCROLLCLIPBOARD";
  271. case WM_QUERYNEWPALETTE: return "WM_QUERYNEWPALETTE";
  272. case WM_PALETTEISCHANGING: return "WM_PALETTEISCHANGING";
  273. case WM_PALETTECHANGED: return "WM_PALETTECHANGED";
  274. case WM_HOTKEY: return "WM_HOTKEY";
  275. case WM_PRINT: return "WM_PRINT";
  276. case WM_PRINTCLIENT: return "WM_PRINTCLIENT";
  277. case WM_HANDHELDFIRST: return "WM_HANDHELDFIRST";
  278. case WM_HANDHELDLAST: return "WM_HANDHELDLAST";
  279. case WM_AFXFIRST: return "WM_AFXFIRST";
  280. case WM_AFXLAST: return "WM_AFXLAST";
  281. case WM_PENWINFIRST: return "WM_PENWINFIRST";
  282. case WM_PENWINLAST: return "WM_PENWINLAST";
  283. default: return "WM_UNKNOWN";
  284. };
  285. }
  286. #endif
  287. // WndProc ====================================================================
  288. /** Window Procedure */
  289. //=============================================================================
  290. LRESULT CALLBACK WndProc( HWND hWnd, UINT message,
  291. WPARAM wParam, LPARAM lParam )
  292. {
  293. try
  294. {
  295. // First let the IME manager do it's stuff.
  296. if ( TheIMEManager )
  297. {
  298. if ( TheIMEManager->serviceIMEMessage( hWnd, message, wParam, lParam ) )
  299. {
  300. // The manager intercepted an IME message so return the result
  301. return TheIMEManager->result();
  302. }
  303. }
  304. #ifdef DO_COPY_PROTECTION
  305. // Check for messages from the launcher
  306. CopyProtect::checkForMessage(message, lParam);
  307. #endif
  308. #ifdef DEBUG_WINDOWS_MESSAGES
  309. static msgCount=0;
  310. char testString[256];
  311. sprintf(testString,"\n%d: %s (%X,%X)", msgCount++,messageToString(message), wParam, lParam);
  312. OutputDebugString(testString);
  313. #endif
  314. // handle all window messages
  315. switch( message )
  316. {
  317. //-------------------------------------------------------------------------
  318. case WM_NCHITTEST:
  319. // Prevent the user from selecting the menu in fullscreen mode
  320. if( !ApplicationIsWindowed )
  321. return HTCLIENT;
  322. break;
  323. //-------------------------------------------------------------------------
  324. case WM_POWERBROADCAST:
  325. switch( wParam )
  326. {
  327. #ifndef PBT_APMQUERYSUSPEND
  328. #define PBT_APMQUERYSUSPEND 0x0000
  329. #endif
  330. case PBT_APMQUERYSUSPEND:
  331. // At this point, the app should save any data for open
  332. // network connections, files, etc., and prepare to go into
  333. // a suspended mode.
  334. return TRUE;
  335. #ifndef PBT_APMRESUMESUSPEND
  336. #define PBT_APMRESUMESUSPEND 0x0007
  337. #endif
  338. case PBT_APMRESUMESUSPEND:
  339. // At this point, the app should recover any data, network
  340. // connections, files, etc., and resume running from when
  341. // the app was suspended.
  342. return TRUE;
  343. }
  344. break;
  345. //-------------------------------------------------------------------------
  346. case WM_SYSCOMMAND:
  347. // Prevent moving/sizing and power loss in fullscreen mode
  348. switch( wParam )
  349. {
  350. case SC_MOVE:
  351. case SC_SIZE:
  352. case SC_MAXIMIZE:
  353. case SC_KEYMENU:
  354. case SC_MONITORPOWER:
  355. if( FALSE == ApplicationIsWindowed )
  356. return 1;
  357. break;
  358. }
  359. break;
  360. case WM_QUERYENDSESSION:
  361. {
  362. TheMessageStream->appendMessage(GameMessage::MSG_META_DEMO_INSTANT_QUIT);
  363. return 0; //don't allow Windows to shutdown while game is running.
  364. }
  365. // ------------------------------------------------------------------------
  366. case WM_CLOSE:
  367. if (!TheGameEngine->getQuitting())
  368. {
  369. //user is exiting without using the menus
  370. //This method didn't work in cinematics because we don't process messages.
  371. //But it's the cleanest way to exit that's similar to using menus.
  372. TheMessageStream->appendMessage(GameMessage::MSG_META_DEMO_INSTANT_QUIT);
  373. //This method used to disable quitting. We just put up the options screen instead.
  374. //TheMessageStream->appendMessage(GameMessage::MSG_META_OPTIONS);
  375. //This method works everywhere but isn't as clean at shutting down.
  376. //TheGameEngine->checkAbnormalQuitting(); //old way to log disconnections for ALT-F4
  377. //TheGameEngine->reset();
  378. //TheGameEngine->setQuitting(TRUE);
  379. //_exit(EXIT_SUCCESS);
  380. return 0;
  381. }
  382. // ------------------------------------------------------------------------
  383. case WM_SETFOCUS:
  384. {
  385. //
  386. // reset the state of our keyboard cause we haven't been paying
  387. // attention to the keys while focus was away
  388. //
  389. if( TheKeyboard )
  390. TheKeyboard->resetKeys();
  391. if (TheWin32Mouse)
  392. TheWin32Mouse->lostFocus(FALSE);
  393. break;
  394. } // end set focus
  395. //-------------------------------------------------------------------------
  396. case WM_SIZE:
  397. // When W3D initializes, it resizes the window. So stop repainting.
  398. if (!gInitializing)
  399. gDoPaint = false;
  400. break;
  401. //-------------------------------------------------------------------------
  402. case WM_KILLFOCUS:
  403. {
  404. if (TheKeyboard )
  405. TheKeyboard->resetKeys();
  406. if (TheWin32Mouse)
  407. TheWin32Mouse->lostFocus(TRUE);
  408. break;
  409. }
  410. //-------------------------------------------------------------------------
  411. case WM_ACTIVATEAPP:
  412. {
  413. // DWORD threadId=GetCurrentThreadId();
  414. if ((bool) wParam != isWinMainActive)
  415. { isWinMainActive = (BOOL) wParam;
  416. if (TheGameEngine)
  417. TheGameEngine->setIsActive(isWinMainActive);
  418. Reset_D3D_Device(isWinMainActive);
  419. if (isWinMainActive)
  420. { //restore mouse cursor to our custom version.
  421. if (TheWin32Mouse)
  422. TheWin32Mouse->setCursor(TheWin32Mouse->getMouseCursor());
  423. }
  424. }
  425. return 0;
  426. }
  427. //-------------------------------------------------------------------------
  428. case WM_ACTIVATE:
  429. {
  430. Int active = LOWORD( wParam );
  431. //
  432. // when window is becoming deactivated we must release mouse cursor
  433. // locks on our region, otherwise set the mouse limit region again
  434. // which will clip the cursor to our window
  435. //
  436. if( active == WA_INACTIVE )
  437. {
  438. ClipCursor( NULL );
  439. if (TheAudio)
  440. TheAudio->loseFocus();
  441. } // end if
  442. else
  443. {
  444. if( TheMouse )
  445. TheMouse->setMouseLimits();
  446. if (TheAudio)
  447. TheAudio->regainFocus();
  448. } // end else
  449. break;
  450. } // end case activate
  451. //-------------------------------------------------------------------------
  452. case WM_KEYDOWN:
  453. {
  454. Int key = (Int)wParam;
  455. switch( key )
  456. {
  457. //---------------------------------------------------------------------
  458. case VK_ESCAPE:
  459. {
  460. PostQuitMessage( 0 );
  461. break;
  462. } // end VK_ESCAPE
  463. } // end switch
  464. return 0;
  465. } // end WM_KEYDOWN
  466. //-------------------------------------------------------------------------
  467. case WM_LBUTTONDOWN:
  468. case WM_LBUTTONUP:
  469. case WM_LBUTTONDBLCLK:
  470. case WM_MBUTTONDOWN:
  471. case WM_MBUTTONUP:
  472. case WM_MBUTTONDBLCLK:
  473. case WM_RBUTTONDOWN:
  474. case WM_RBUTTONUP:
  475. case WM_RBUTTONDBLCLK:
  476. {
  477. if( TheWin32Mouse )
  478. TheWin32Mouse->addWin32Event( message, wParam, lParam, TheMessageTime );
  479. return 0;
  480. } // end WM_LBUTTONDOWN
  481. //-------------------------------------------------------------------------
  482. case 0x020A: // WM_MOUSEWHEEL
  483. {
  484. long x = (long) LOWORD(lParam);
  485. long y = (long) HIWORD(lParam);
  486. RECT rect;
  487. // ignore when outside of client area
  488. GetWindowRect( ApplicationHWnd, &rect );
  489. if( x < rect.left || x > rect.right || y < rect.top || y > rect.bottom )
  490. return 0;
  491. if( TheWin32Mouse )
  492. TheWin32Mouse->addWin32Event( message, wParam, lParam, TheMessageTime );
  493. return 0;
  494. } // end WM_MOUSEWHEEL
  495. //-------------------------------------------------------------------------
  496. case WM_MOUSEMOVE:
  497. {
  498. Int x = (Int)LOWORD( lParam );
  499. Int y = (Int)HIWORD( lParam );
  500. RECT rect;
  501. // Int keys = wParam;
  502. // ignore when outside of client area
  503. GetClientRect( ApplicationHWnd, &rect );
  504. if( x < rect.left || x > rect.right || y < rect.top || y > rect.bottom )
  505. return 0;
  506. if( TheWin32Mouse )
  507. TheWin32Mouse->addWin32Event( message, wParam, lParam, TheMessageTime );
  508. return 0;
  509. } // end WM_MOUSEMOVE
  510. //-------------------------------------------------------------------------
  511. case WM_SETCURSOR:
  512. {
  513. if (TheWin32Mouse && (HWND)wParam == ApplicationHWnd)
  514. TheWin32Mouse->setCursor(TheWin32Mouse->getMouseCursor());
  515. return TRUE; //tell Windows not to reset mouse cursor image to default.
  516. }
  517. case WM_PAINT:
  518. {
  519. if (gDoPaint) {
  520. PAINTSTRUCT paint;
  521. HDC dc = ::BeginPaint(hWnd, &paint);
  522. #if 0
  523. ::SetTextColor(dc, RGB(255,255,255));
  524. ::SetBkColor(dc, RGB(0,0,0));
  525. ::TextOut(dc, 30, 30, "Loading Command & Conquer Generals...", 37);
  526. #endif
  527. if (gLoadScreenBitmap!=NULL) {
  528. Int savContext = ::SaveDC(dc);
  529. HDC tmpDC = ::CreateCompatibleDC(dc);
  530. HBITMAP savBitmap = (HBITMAP)::SelectObject(tmpDC, gLoadScreenBitmap);
  531. ::BitBlt(dc, 0, 0, DEFAULT_XRESOLUTION, DEFAULT_YRESOLUTION, tmpDC, 0, 0, SRCCOPY);
  532. ::SelectObject(tmpDC, savBitmap);
  533. ::DeleteDC(tmpDC);
  534. ::RestoreDC(dc, savContext);
  535. }
  536. ::EndPaint(hWnd, &paint);
  537. return TRUE;
  538. }
  539. break;
  540. }
  541. case WM_ERASEBKGND:
  542. {
  543. if (!gDoPaint)
  544. return TRUE; //we don't need to erase the background because we always draw entire window.
  545. break;
  546. }
  547. // Well, it was a nice idea, but we don't get a message for an ejection.
  548. // (Really unforunate, actually.) I'm leaving this in in-case some one wants
  549. // to trap a different device change (for instance, removal of a mouse) - jkmcd
  550. #if 0
  551. case WM_DEVICECHANGE:
  552. {
  553. if (((UINT) wParam) == DBT_DEVICEREMOVEPENDING)
  554. {
  555. DEV_BROADCAST_HDR *hdr = (DEV_BROADCAST_HDR*) lParam;
  556. if (!hdr) {
  557. break;
  558. }
  559. if (hdr->dbch_devicetype != DBT_DEVTYP_VOLUME) {
  560. break;
  561. }
  562. // Lets discuss how Windows is a flaming pile of poo. I'm now casting the header
  563. // directly into the structure, because its the one I want, and this is just how
  564. // its done. I hate Windows. - jkmcd
  565. DEV_BROADCAST_VOLUME *vol = (DEV_BROADCAST_VOLUME*) (hdr);
  566. // @todo - Yikes. This could cause us all kinds of pain. I don't really want
  567. // to even think about the stink this could cause us.
  568. TheFileSystem->unloadMusicFilesFromCD(vol->dbcv_unitmask);
  569. return TRUE;
  570. }
  571. break;
  572. }
  573. #endif
  574. } // end switch
  575. }
  576. catch (...)
  577. {
  578. RELEASE_CRASH(("Uncaught exception in Main::WndProc... probably should not happen\n"));
  579. // no rethrow
  580. }
  581. //In full-screen mode, only pass these messages onto the default windows handler.
  582. //Appears to fix issues with dual monitor systems but doesn't seem safe?
  583. ///@todo: Look into proper support for dual monitor systems.
  584. /* if (!ApplicationIsWindowed)
  585. switch (message)
  586. {
  587. case WM_PAINT:
  588. case WM_NCCREATE:
  589. case WM_NCDESTROY:
  590. case WM_NCCALCSIZE:
  591. case WM_NCPAINT:
  592. return DefWindowProc( hWnd, message, wParam, lParam );
  593. }
  594. return 0;*/
  595. return DefWindowProc( hWnd, message, wParam, lParam );
  596. } // end WndProc
  597. // initializeAppWindows =======================================================
  598. /** Register windows class and create application windows. */
  599. //=============================================================================
  600. static Bool initializeAppWindows( HINSTANCE hInstance, Int nCmdShow, Bool runWindowed )
  601. {
  602. DWORD windowStyle;
  603. Int startWidth = DEFAULT_XRESOLUTION,
  604. startHeight = DEFAULT_YRESOLUTION;
  605. // register the window class
  606. WNDCLASS wndClass = { CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, WndProc, 0, 0, hInstance,
  607. LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ApplicationIcon)),
  608. NULL/*LoadCursor(NULL, IDC_ARROW)*/,
  609. (HBRUSH)GetStockObject(BLACK_BRUSH), NULL,
  610. TEXT("Game Window") };
  611. RegisterClass( &wndClass );
  612. // Create our main window
  613. windowStyle = WS_POPUP|WS_VISIBLE;
  614. if (runWindowed)
  615. windowStyle |= WS_DLGFRAME | WS_CAPTION | WS_SYSMENU;
  616. else
  617. windowStyle |= WS_EX_TOPMOST | WS_SYSMENU;
  618. RECT rect;
  619. rect.left = 0;
  620. rect.top = 0;
  621. rect.right = startWidth;
  622. rect.bottom = startHeight;
  623. AdjustWindowRect (&rect, windowStyle, FALSE);
  624. if (runWindowed) {
  625. // Makes the normal debug 800x600 window center in the screen.
  626. startWidth = DEFAULT_XRESOLUTION;
  627. startHeight= DEFAULT_YRESOLUTION;
  628. }
  629. gInitializing = true;
  630. HWND hWnd = CreateWindow( TEXT("Game Window"),
  631. TEXT("Command and Conquer Generals"),
  632. windowStyle,
  633. (GetSystemMetrics( SM_CXSCREEN ) / 2) - (startWidth / 2), // original position X
  634. (GetSystemMetrics( SM_CYSCREEN ) / 2) - (startHeight / 2),// original position Y
  635. // Lorenzen nudged the window higher
  636. // so the constantdebug report would
  637. // not get obliterated by assert windows, thank you.
  638. //(GetSystemMetrics( SM_CXSCREEN ) / 2) - (startWidth / 2), //this works with any screen res
  639. //(GetSystemMetrics( SM_CYSCREEN ) / 25) - (startHeight / 25),//this works with any screen res
  640. rect.right-rect.left,
  641. rect.bottom-rect.top,
  642. 0L,
  643. 0L,
  644. hInstance,
  645. 0L );
  646. if (!runWindowed)
  647. { SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
  648. }
  649. else
  650. SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0,SWP_NOSIZE |SWP_NOMOVE);
  651. SetFocus(hWnd);
  652. SetForegroundWindow(hWnd);
  653. ShowWindow( hWnd, nCmdShow );
  654. UpdateWindow( hWnd );
  655. // save our application instance and window handle for future use
  656. ApplicationHInstance = hInstance;
  657. ApplicationHWnd = hWnd;
  658. gInitializing = false;
  659. if (!runWindowed) {
  660. gDoPaint = false;
  661. }
  662. return true; // success
  663. } // end initializeAppWindows
  664. void munkeeFunc(void);
  665. CDAPFN_DECLARE_GLOBAL(munkeeFunc, CDAPFN_OVERHEAD_L5, CDAPFN_CONSTRAINT_NONE);
  666. void munkeeFunc(void)
  667. {
  668. CDAPFN_ENDMARK(munkeeFunc);
  669. }
  670. void checkProtection(void)
  671. {
  672. #ifdef _INTERNAL
  673. __try
  674. {
  675. munkeeFunc();
  676. }
  677. __except(EXCEPTION_EXECUTE_HANDLER)
  678. {
  679. exit(0); // someone is messing with us.
  680. }
  681. #endif
  682. }
  683. // strtrim ====================================================================
  684. /** Trim leading and trailing whitespace from a character string (in place). */
  685. //=============================================================================
  686. static char* strtrim(char* buffer)
  687. {
  688. if (buffer != NULL) {
  689. // Strip leading white space from the string.
  690. char * source = buffer;
  691. while ((*source != 0) && ((unsigned char)*source <= 32))
  692. {
  693. source++;
  694. }
  695. if (source != buffer)
  696. {
  697. strcpy(buffer, source);
  698. }
  699. // Clip trailing white space from the string.
  700. for (int index = strlen(buffer)-1; index >= 0; index--)
  701. {
  702. if ((*source != 0) && ((unsigned char)buffer[index] <= 32))
  703. {
  704. buffer[index] = '\0';
  705. }
  706. else
  707. {
  708. break;
  709. }
  710. }
  711. }
  712. return buffer;
  713. }
  714. char *nextParam(char *newSource, char *seps)
  715. {
  716. static char *source = NULL;
  717. if (newSource)
  718. {
  719. source = newSource;
  720. }
  721. if (!source)
  722. {
  723. return NULL;
  724. }
  725. // find first separator
  726. char *first = source;//strpbrk(source, seps);
  727. if (first)
  728. {
  729. // go past separator
  730. char *firstSep = strpbrk(first, seps);
  731. char firstChar[2] = {0,0};
  732. if (firstSep == first)
  733. {
  734. firstChar[0] = *first;
  735. while (*first == firstChar[0]) first++;
  736. }
  737. // find end
  738. char *end;
  739. if (firstChar[0])
  740. end = strpbrk(first, firstChar);
  741. else
  742. end = strpbrk(first, seps);
  743. // trim string & save next start pos
  744. if (end)
  745. {
  746. source = end+1;
  747. *end = 0;
  748. if (!*source)
  749. source = NULL;
  750. }
  751. else
  752. {
  753. source = NULL;
  754. }
  755. if (first && !*first)
  756. first = NULL;
  757. }
  758. return first;
  759. }
  760. // Necessary to allow memory managers and such to have useful critical sections
  761. static CriticalSection critSec1, critSec2, critSec3, critSec4, critSec5;
  762. // WinMain ====================================================================
  763. /** Application entry point */
  764. //=============================================================================
  765. Int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
  766. LPSTR lpCmdLine, Int nCmdShow )
  767. {
  768. checkProtection();
  769. #ifdef _PROFILE
  770. Profile::StartRange("init");
  771. #endif
  772. try {
  773. _set_se_translator( DumpExceptionInfo ); // Hook that allows stack trace.
  774. //
  775. // there is something about checkin in and out the .dsp and .dsw files
  776. // that blows the working directory information away on each of the
  777. // developers machines so we're going to hack it for a while and set our
  778. // working directory to the directory with the .exe since that's not the
  779. // default in a DevStudio project
  780. //
  781. TheUnicodeStringCriticalSection = &critSec2;
  782. TheDmaCriticalSection = &critSec3;
  783. TheMemoryPoolCriticalSection = &critSec4;
  784. TheDebugLogCriticalSection = &critSec5;
  785. /// @todo remove this force set of working directory later
  786. Char buffer[ _MAX_PATH ];
  787. GetModuleFileName( NULL, buffer, sizeof( buffer ) );
  788. Char *pEnd = buffer + strlen( buffer );
  789. while( pEnd != buffer )
  790. {
  791. if( *pEnd == '\\' )
  792. {
  793. *pEnd = 0;
  794. break;
  795. }
  796. pEnd--;
  797. }
  798. ::SetCurrentDirectory(buffer);
  799. /*
  800. ** Convert WinMain arguments to simple main argc and argv
  801. */
  802. int argc = 1;
  803. char * argv[20];
  804. argv[0] = NULL;
  805. char *token;
  806. token = nextParam(lpCmdLine, "\" ");
  807. while (argc < 20 && token != NULL) {
  808. argv[argc++] = strtrim(token);
  809. //added a preparse step for this flag because it affects window creation style
  810. if (stricmp(token,"-win")==0)
  811. ApplicationIsWindowed=true;
  812. token = nextParam(NULL, "\" ");
  813. }
  814. if (argc>2 && strcmp(argv[1],"-DX")==0) {
  815. Int i;
  816. DEBUG_LOG(("\n--- DX STACK DUMP\n"));
  817. for (i=2; i<argc; i++) {
  818. Int pc;
  819. pc = 0;
  820. sscanf(argv[i], "%x", &pc);
  821. char name[_MAX_PATH], file[_MAX_PATH];
  822. unsigned int line;
  823. unsigned int addr;
  824. GetFunctionDetails((void*)pc, name, file, &line, &addr);
  825. DEBUG_LOG(("0x%x - %s, %s, line %d address 0x%x\n", pc, name, file, line, addr));
  826. }
  827. DEBUG_LOG(("\n--- END OF DX STACK DUMP\n"));
  828. return 0;
  829. }
  830. #ifdef _DEBUG
  831. // Turn on Memory heap tracking
  832. int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
  833. tmpFlag |= (_CRTDBG_LEAK_CHECK_DF|_CRTDBG_ALLOC_MEM_DF);
  834. tmpFlag &= ~_CRTDBG_CHECK_CRT_DF;
  835. _CrtSetDbgFlag( tmpFlag );
  836. #endif
  837. // install debug callbacks
  838. // WWDebug_Install_Message_Handler(WWDebug_Message_Callback);
  839. // WWDebug_Install_Assert_Handler(WWAssert_Callback);
  840. // Force "splash image" to be loaded from a file, not a resource so same exe can be used in different localizations.
  841. #if defined _DEBUG || defined _INTERNAL || defined _PROFILE
  842. // check both localized directory and root dir
  843. char filePath[_MAX_PATH];
  844. char *fileName = "Install_Final.bmp";
  845. static const char *localizedPathFormat = "Data/%s/";
  846. sprintf(filePath,localizedPathFormat, GetRegistryLanguage().str());
  847. strcat( filePath, fileName );
  848. FILE *fileImage = fopen(filePath, "r");
  849. if (fileImage) {
  850. fclose(fileImage);
  851. gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, filePath, IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE);
  852. }
  853. else {
  854. gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, fileName, IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE);
  855. }
  856. #else
  857. // in release, the file only ever lives in the root dir
  858. gLoadScreenBitmap = (HBITMAP)LoadImage(hInstance, "Install_Final.bmp", IMAGE_BITMAP, 0, 0, LR_SHARED|LR_LOADFROMFILE);
  859. #endif
  860. // register windows class and create application window
  861. if( initializeAppWindows( hInstance, nCmdShow, ApplicationIsWindowed) == false )
  862. return 0;
  863. if (gLoadScreenBitmap!=NULL) {
  864. ::DeleteObject(gLoadScreenBitmap);
  865. gLoadScreenBitmap = NULL;
  866. }
  867. // BGC - initialize COM
  868. // OleInitialize(NULL);
  869. // start the log
  870. DEBUG_INIT(DEBUG_FLAGS_DEFAULT);
  871. initMemoryManager();
  872. // Set up version info
  873. TheVersion = NEW Version;
  874. TheVersion->setVersion(VERSION_MAJOR, VERSION_MINOR, VERSION_BUILDNUM, VERSION_LOCALBUILDNUM,
  875. AsciiString(VERSION_BUILDUSER), AsciiString(VERSION_BUILDLOC),
  876. AsciiString(__TIME__), AsciiString(__DATE__));
  877. #ifdef DO_COPY_PROTECTION
  878. if (!CopyProtect::isLauncherRunning())
  879. {
  880. DEBUG_LOG(("Launcher is not running - about to bail\n"));
  881. delete TheVersion;
  882. TheVersion = NULL;
  883. shutdownMemoryManager();
  884. DEBUG_SHUTDOWN();
  885. return 0;
  886. }
  887. #endif
  888. //Create a mutex with a unique name to Generals in order to determine if
  889. //our app is already running.
  890. //WARNING: DO NOT use this number for any other application except Generals.
  891. GeneralsMutex = CreateMutex(NULL, FALSE, GENERALS_GUID);
  892. if (GetLastError() == ERROR_ALREADY_EXISTS)
  893. {
  894. HWND ccwindow = FindWindow(GENERALS_GUID, NULL);
  895. if (ccwindow)
  896. {
  897. SetForegroundWindow(ccwindow);
  898. ShowWindow(ccwindow, SW_RESTORE);
  899. }
  900. if (GeneralsMutex != NULL)
  901. {
  902. CloseHandle(GeneralsMutex);
  903. GeneralsMutex = NULL;
  904. }
  905. DEBUG_LOG(("Generals is already running...Bail!\n"));
  906. delete TheVersion;
  907. TheVersion = NULL;
  908. shutdownMemoryManager();
  909. DEBUG_SHUTDOWN();
  910. return 0;
  911. }
  912. DEBUG_LOG(("Create GeneralsMutex okay.\n"));
  913. #ifdef DO_COPY_PROTECTION
  914. if (!CopyProtect::notifyLauncher())
  915. {
  916. DEBUG_LOG(("Could not talk to the launcher - about to bail\n"));
  917. delete TheVersion;
  918. TheVersion = NULL;
  919. shutdownMemoryManager();
  920. DEBUG_SHUTDOWN();
  921. return 0;
  922. }
  923. #endif
  924. DEBUG_LOG(("CRC message is %d\n", GameMessage::MSG_LOGIC_CRC));
  925. // run the game main loop
  926. GameMain(argc, argv);
  927. #ifdef DO_COPY_PROTECTION
  928. // Clean up copy protection
  929. CopyProtect::shutdown();
  930. #endif
  931. delete TheVersion;
  932. TheVersion = NULL;
  933. #ifdef MEMORYPOOL_DEBUG
  934. TheMemoryPoolFactory->debugMemoryReport(REPORT_POOLINFO | REPORT_POOL_OVERFLOW | REPORT_SIMPLE_LEAKS, 0, 0);
  935. #endif
  936. #if defined(_DEBUG) || defined(_INTERNAL)
  937. TheMemoryPoolFactory->memoryPoolUsageReport("AAAMemStats");
  938. #endif
  939. // close the log
  940. shutdownMemoryManager();
  941. DEBUG_SHUTDOWN();
  942. // BGC - shut down COM
  943. // OleUninitialize();
  944. }
  945. catch (...)
  946. {
  947. }
  948. TheUnicodeStringCriticalSection = NULL;
  949. TheDmaCriticalSection = NULL;
  950. TheMemoryPoolCriticalSection = NULL;
  951. return 0;
  952. } // end WinMain
  953. // CreateGameEngine ===========================================================
  954. /** Create the Win32 game engine we're going to use */
  955. //=============================================================================
  956. GameEngine *CreateGameEngine( void )
  957. {
  958. Win32GameEngine *engine;
  959. engine = NEW Win32GameEngine;
  960. //game engine may not have existed when app got focus so make sure it
  961. //knows about current focus state.
  962. engine->setIsActive(isWinMainActive);
  963. return engine;
  964. } // end CreateGameEngine