Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2866 lines
78 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: The application object.
  4. //
  5. //===========================================================================//
  6. #include "stdafx.h"
  7. #include <io.h>
  8. #include <stdlib.h>
  9. #include <direct.h>
  10. #pragma warning(push, 1)
  11. #pragma warning(disable:4701 4702 4530)
  12. #include <fstream>
  13. #pragma warning(pop)
  14. #include "BuildNum.h"
  15. #include "EditGameConfigs.h"
  16. #include "Splash.h"
  17. #include "Options.h"
  18. #include "custommessages.h"
  19. #include "MainFrm.h"
  20. #include "MessageWnd.h"
  21. #include "ChildFrm.h"
  22. #include "MapDoc.h"
  23. #include "Manifest.h"
  24. #include "MapView3D.h"
  25. #include "MapView2D.h"
  26. #include "PakDoc.h"
  27. #include "PakViewDirec.h"
  28. #include "PakFrame.h"
  29. #include "Prefabs.h"
  30. #include "GlobalFunctions.h"
  31. #include "Shell.h"
  32. #include "ShellMessageWnd.h"
  33. #include "Options.h"
  34. #include "TextureSystem.h"
  35. #include "ToolManager.h"
  36. #include "Hammer.h"
  37. #include "StudioModel.h"
  38. #include "ibsplighting.h"
  39. #include "statusbarids.h"
  40. #include "tier0/icommandline.h"
  41. #include "soundsystem.h"
  42. #include "IHammer.h"
  43. #include "op_entity.h"
  44. #include "tier0/dbg.h"
  45. #include "materialsystem/imaterialsystemhardwareconfig.h"
  46. #include "istudiorender.h"
  47. #include "FileSystem.h"
  48. #include "engine_launcher_api.h"
  49. #include "filesystem_init.h"
  50. #include "utlmap.h"
  51. #include "utlvector.h"
  52. #include "progdlg.h"
  53. #include "MapWorld.h"
  54. #include "HammerVGui.h"
  55. #include "vgui_controls/Controls.h"
  56. #include "lpreview_thread.h"
  57. #include "SteamWriteMiniDump.h"
  58. #include "inputsystem/iinputsystem.h"
  59. #include "datacache/idatacache.h"
  60. #include "steam/steam_api.h"
  61. #include "toolframework/ienginetool.h"
  62. #include "toolutils/enginetools_int.h"
  63. #include "objectproperties.h"
  64. #include "particles/particles.h"
  65. #include "p4lib/ip4.h"
  66. #include "syncfiledialog.h"
  67. #include "vstdlib/jobthread.h"
  68. #include "gridnav.h"
  69. #include "tablet.h"
  70. #include "dialogwithcheckbox.h"
  71. #include "configmanager.h"
  72. // memdbgon must be the last include file in a .cpp file!!!
  73. #include <tier0/memdbgon.h>
  74. //
  75. // Note!
  76. //
  77. // If this DLL is dynamically linked against the MFC
  78. // DLLs, any functions exported from this DLL which
  79. // call into MFC must have the AFX_MANAGE_STATE macro
  80. // added at the very beginning of the function.
  81. //
  82. // For example:
  83. //
  84. // extern "C" BOOL PASCAL EXPORT ExportedFunction()
  85. // {
  86. // AFX_MANAGE_STATE(AfxGetStaticModuleState());
  87. // // normal function body here
  88. // }
  89. //
  90. // It is very important that this macro appear in each
  91. // function, prior to any calls into MFC. This means that
  92. // it must appear as the first statement within the
  93. // function, even before any object variable declarations
  94. // as their constructors may generate calls into the MFC
  95. // DLL.
  96. //
  97. // Please see MFC Technical Notes 33 and 58 for additional
  98. // details.
  99. //
  100. // dvs: hack
  101. extern LPCTSTR GetErrorString(void);
  102. extern void MakePrefabLibrary(LPCTSTR pszName);
  103. void EditorUtil_ConvertPath(CString &str, bool bSave);
  104. static bool bMakeLib = false;
  105. static float fSequenceVersion = 0.2f;
  106. static char *pszSequenceHdr = "Worldcraft Command Sequences\r\n\x1a";
  107. CHammer theApp;
  108. COptions Options;
  109. CShell g_Shell;
  110. CShellMessageWnd g_ShellMessageWnd;
  111. CMessageWnd *g_pwndMessage = NULL;
  112. // IPC structures for lighting preview thread
  113. CMessageQueue<MessageToLPreview> g_HammerToLPreviewMsgQueue;
  114. CMessageQueue<MessageFromLPreview> g_LPreviewToHammerMsgQueue;
  115. ThreadHandle_t g_LPreviewThread;
  116. CSteamAPIContext g_SteamAPIContext;
  117. CSteamAPIContext *steamapicontext = &g_SteamAPIContext;
  118. bool CHammer::m_bIsNewDocumentVisible = true;
  119. //-----------------------------------------------------------------------------
  120. // Expose singleton
  121. //-----------------------------------------------------------------------------
  122. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CHammer, IHammer, INTERFACEVERSION_HAMMER, theApp);
  123. //-----------------------------------------------------------------------------
  124. // global interfaces
  125. //-----------------------------------------------------------------------------
  126. IBaseFileSystem *g_pFileSystem;
  127. IEngineAPI *g_pEngineAPI;
  128. CreateInterfaceFn g_Factory;
  129. bool g_bHDR = true;
  130. bool IsRunningInEngine()
  131. {
  132. return g_pEngineAPI != NULL;
  133. }
  134. int WrapFunctionWithMinidumpHandler( int (*pfn)(void *pParam), void *pParam, int errorRetVal )
  135. {
  136. int nRetVal;
  137. if ( !Plat_IsInDebugSession() && !CommandLine()->FindParm( "-nominidumps") )
  138. {
  139. _set_se_translator( SteamWriteMiniDumpUsingExceptionInfo );
  140. try // this try block allows the SE translator to work
  141. {
  142. nRetVal = pfn( pParam );
  143. }
  144. catch( ... )
  145. {
  146. return errorRetVal;
  147. }
  148. }
  149. else
  150. {
  151. nRetVal = pfn( pParam );
  152. }
  153. return nRetVal;
  154. }
  155. //-----------------------------------------------------------------------------
  156. // Purpose: Logging listener so that Hammer can capture warning and error
  157. // output to write to the message window.
  158. //-----------------------------------------------------------------------------
  159. class CHammerMessageLoggingListener : public ILoggingListener
  160. {
  161. public:
  162. virtual void Log( const LoggingContext_t *pContext, const tchar *pMessage )
  163. {
  164. if ( pContext->m_Severity == LS_ERROR )
  165. {
  166. Msg( mwError, pMessage );
  167. }
  168. else if ( pContext->m_Severity == LS_WARNING )
  169. {
  170. Msg( mwWarning, pMessage );
  171. }
  172. }
  173. };
  174. //-----------------------------------------------------------------------------
  175. // Purpose: Outputs a formatted debug string.
  176. // Input : fmt - format specifier.
  177. // ... - arguments to format.
  178. //-----------------------------------------------------------------------------
  179. void DBG(char *fmt, ...)
  180. {
  181. char ach[128];
  182. va_list va;
  183. va_start(va, fmt);
  184. vsprintf(ach, fmt, va);
  185. va_end(va);
  186. OutputDebugString(ach);
  187. }
  188. void Msg(int type, const char *fmt, ...)
  189. {
  190. if ( !g_pwndMessage )
  191. return;
  192. va_list vl;
  193. char szBuf[512];
  194. va_start(vl, fmt);
  195. int len = _vsnprintf(szBuf, 512, fmt, vl);
  196. va_end(vl);
  197. if ((type == mwError) || (type == mwWarning))
  198. {
  199. g_pwndMessage->ShowMessageWindow();
  200. }
  201. char temp = 0;
  202. char *pMsg = szBuf;
  203. do
  204. {
  205. if (len >= MESSAGE_WND_MESSAGE_LENGTH)
  206. {
  207. temp = pMsg[MESSAGE_WND_MESSAGE_LENGTH-1];
  208. pMsg[MESSAGE_WND_MESSAGE_LENGTH-1] = '\0';
  209. }
  210. g_pwndMessage->AddMsg((MWMSGTYPE)type, pMsg);
  211. if (len >= MESSAGE_WND_MESSAGE_LENGTH)
  212. {
  213. pMsg[MESSAGE_WND_MESSAGE_LENGTH-1] = temp;
  214. pMsg += MESSAGE_WND_MESSAGE_LENGTH-1;
  215. }
  216. len -= MESSAGE_WND_MESSAGE_LENGTH-1;
  217. } while (len > 0);
  218. }
  219. //
  220. // Post-init and pre-shutdown routines management
  221. //
  222. //-----------------------------------------------------------------------------
  223. // Purpose: this routine calls the default doc template's OpenDocumentFile() but
  224. // with the ability to override the visible flag
  225. // Input : lpszPathName - the document to open
  226. // bMakeVisible - ignored
  227. // Output : returns the opened document if successful
  228. //-----------------------------------------------------------------------------
  229. CDocument *CHammerDocTemplate::OpenDocumentFile( LPCTSTR lpszPathName, BOOL bMakeVisible )
  230. {
  231. CDocument *pDoc = __super::OpenDocumentFile( lpszPathName, CHammer::IsNewDocumentVisible() );
  232. return pDoc;
  233. }
  234. //-----------------------------------------------------------------------------
  235. // Purpose: this function will attempt an orderly shutdown of all maps. It will attempt to
  236. // close only documents that have no references, hopefully freeing up additional documents
  237. // Input : bEndSession - ignored
  238. //-----------------------------------------------------------------------------
  239. void CHammerDocTemplate::CloseAllDocuments( BOOL bEndSession )
  240. {
  241. bool bFound = true;
  242. // rough loop to always remove the first map doc that has no references, then start over, try again.
  243. // if we still have maps with references ( that's bad ), we'll exit out of this loop and just do
  244. // the default shutdown to force them all to close.
  245. while( bFound )
  246. {
  247. bFound = false;
  248. POSITION pos = GetFirstDocPosition();
  249. while( pos != NULL )
  250. {
  251. CDocument *pDoc = GetNextDoc( pos );
  252. CMapDoc *pMapDoc = dynamic_cast< CMapDoc * >( pDoc );
  253. if ( pMapDoc && pMapDoc->GetReferenceCount() == 0 )
  254. {
  255. pDoc->OnCloseDocument();
  256. bFound = true;
  257. break;
  258. }
  259. }
  260. }
  261. #if 0
  262. POSITION pos = GetFirstDocPosition();
  263. while( pos != NULL )
  264. {
  265. CDocument *pDoc = GetNextDoc( pos );
  266. CMapDoc *pMapDoc = dynamic_cast< CMapDoc * >( pDoc );
  267. if ( pMapDoc )
  268. {
  269. pMapDoc->ForceNoReference();
  270. }
  271. }
  272. __super::CloseAllDocuments( bEndSession );
  273. #endif
  274. }
  275. //-----------------------------------------------------------------------------
  276. // Purpose: This function will allow hammer to control the initial visibility of an opening document
  277. // Input : pFrame - the new document's frame
  278. // pDoc - the new document
  279. // bMakeVisible - ignored as a parameter
  280. //-----------------------------------------------------------------------------
  281. void CHammerDocTemplate::InitialUpdateFrame( CFrameWnd* pFrame, CDocument* pDoc, BOOL bMakeVisible )
  282. {
  283. bMakeVisible = CHammer::IsNewDocumentVisible();
  284. __super::InitialUpdateFrame( pFrame, pDoc, bMakeVisible );
  285. if ( bMakeVisible )
  286. {
  287. CMapDoc *pMapDoc = dynamic_cast< CMapDoc * >( pDoc );
  288. if ( pMapDoc )
  289. {
  290. pMapDoc->SetInitialUpdate();
  291. }
  292. }
  293. }
  294. //-----------------------------------------------------------------------------
  295. // Purpose: this function will let all other maps know that an instanced map has been updated ( usually for volume size )
  296. // Input : pInstanceMapDoc - the document that has been updated
  297. //-----------------------------------------------------------------------------
  298. void CHammerDocTemplate::UpdateInstanceMap( CMapDoc *pInstanceMapDoc )
  299. {
  300. POSITION pos = GetFirstDocPosition();
  301. while( pos != NULL )
  302. {
  303. CDocument *pDoc = GetNextDoc( pos );
  304. CMapDoc *pMapDoc = dynamic_cast< CMapDoc * >( pDoc );
  305. if ( pMapDoc && pMapDoc != pInstanceMapDoc )
  306. {
  307. pMapDoc->UpdateInstanceMap( pInstanceMapDoc );
  308. }
  309. }
  310. }
  311. template < typename T, int Instance >
  312. static T& ReliableStaticStorage()
  313. {
  314. Instance;
  315. static T storage;
  316. return storage;
  317. }
  318. enum Storage_t
  319. {
  320. APP_FN_POST_INIT,
  321. APP_FN_PRE_SHUTDOWN,
  322. APP_FN_MESSAGE_LOOP,
  323. APP_FN_MESSAGE_PRETRANSLATE
  324. };
  325. #define s_appRegisteredPostInitFns ReliableStaticStorage< CUtlVector< void (*)() >, APP_FN_POST_INIT >()
  326. #define s_appRegisteredPreShutdownFns ReliableStaticStorage< CUtlVector< void (*)() >, APP_FN_PRE_SHUTDOWN >()
  327. #define s_appRegisteredMessageLoop ReliableStaticStorage< CUtlVector< void (*)() >, APP_FN_MESSAGE_LOOP >()
  328. #define s_appRegisteredMessagePreTrans ReliableStaticStorage< CUtlVector< void (*)( MSG * ) >, APP_FN_MESSAGE_PRETRANSLATE >()
  329. void AppRegisterPostInitFn( void (*fn)() )
  330. {
  331. s_appRegisteredPostInitFns.AddToTail( fn );
  332. }
  333. void AppRegisterMessageLoopFn( void (*fn)() )
  334. {
  335. s_appRegisteredMessageLoop.AddToTail( fn );
  336. }
  337. void AppRegisterMessagePretranslateFn( void (*fn)( MSG * ) )
  338. {
  339. s_appRegisteredMessagePreTrans.AddToTail( fn );
  340. }
  341. void AppRegisterPreShutdownFn( void (*fn)() )
  342. {
  343. s_appRegisteredPreShutdownFns.AddToTail( fn );
  344. }
  345. class CHammerCmdLine : public CCommandLineInfo
  346. {
  347. public:
  348. CHammerCmdLine(void)
  349. {
  350. m_bShowLogo = true;
  351. m_bGame = false;
  352. m_bConfigDir = false;
  353. }
  354. void ParseParam(LPCTSTR lpszParam, BOOL bFlag, BOOL bLast)
  355. {
  356. if ((!m_bGame) && (bFlag && !stricmp(lpszParam, "game")))
  357. {
  358. m_bGame = true;
  359. }
  360. else if (m_bGame)
  361. {
  362. if (!bFlag)
  363. {
  364. m_strGame = lpszParam;
  365. }
  366. m_bGame = false;
  367. }
  368. else if (bFlag && !strcmpi(lpszParam, "nologo"))
  369. {
  370. m_bShowLogo = false;
  371. }
  372. else if (bFlag && !strcmpi(lpszParam, "makelib"))
  373. {
  374. bMakeLib = TRUE;
  375. }
  376. else if (!bFlag && bMakeLib)
  377. {
  378. MakePrefabLibrary(lpszParam);
  379. }
  380. else if ((!m_bConfigDir) && (bFlag && !stricmp(lpszParam, "configdir")))
  381. {
  382. m_bConfigDir = true;
  383. }
  384. else if (m_bConfigDir)
  385. {
  386. if ( !bFlag )
  387. {
  388. Options.configs.m_strConfigDir = lpszParam;
  389. }
  390. m_bConfigDir = false;
  391. }
  392. else
  393. {
  394. CCommandLineInfo::ParseParam(lpszParam, bFlag, bLast);
  395. }
  396. }
  397. bool m_bShowLogo;
  398. bool m_bGame; // Used to find and parse the "-game blah" parameter pair.
  399. bool m_bConfigDir; // Used to find and parse the "-configdir blah" parameter pair.
  400. CString m_strGame; // The name of the game to use for this session, ie "hl2" or "cstrike". This should match the mod dir, not the config name.
  401. };
  402. BEGIN_MESSAGE_MAP(CHammer, CWinApp)
  403. //{{AFX_MSG_MAP(CHammer)
  404. ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
  405. ON_COMMAND(ID_FILE_OPEN, OnFileOpen)
  406. ON_COMMAND(ID_FILE_NEW, OnFileNew)
  407. ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
  408. ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)
  409. //}}AFX_MSG_MAP
  410. END_MESSAGE_MAP()
  411. //-----------------------------------------------------------------------------
  412. // Purpose: Constructor. Initializes member variables and creates a scratch
  413. // buffer for use when loading WAD files.
  414. //-----------------------------------------------------------------------------
  415. CHammer::CHammer(void)
  416. {
  417. m_bActiveApp = true;
  418. m_SuppressVideoAllocation = false;
  419. m_bForceRenderNextFrame = false;
  420. m_bClosing = false;
  421. m_bFoundryMode = false;
  422. m_CustomAcceleratorWindow = NULL;
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose: Destructor. Frees scratch buffer used when loading WAD files.
  426. // Deletes all command sequences used when compiling maps.
  427. //-----------------------------------------------------------------------------
  428. CHammer::~CHammer(void)
  429. {
  430. }
  431. //-----------------------------------------------------------------------------
  432. // Inherited from IAppSystem
  433. //-----------------------------------------------------------------------------
  434. bool CHammer::Connect( CreateInterfaceFn factory )
  435. {
  436. if ( !BaseClass::Connect( factory ) )
  437. return false;
  438. // bool bCVarOk = ConnectStudioRenderCVars( factory );
  439. g_pFileSystem = ( IBaseFileSystem * )factory( BASEFILESYSTEM_INTERFACE_VERSION, NULL );
  440. g_pStudioRender = ( IStudioRender * )factory( STUDIO_RENDER_INTERFACE_VERSION, NULL );
  441. g_pEngineAPI = ( IEngineAPI * )factory( VENGINE_LAUNCHER_API_VERSION, NULL );
  442. g_pMDLCache = (IMDLCache*)factory( MDLCACHE_INTERFACE_VERSION, NULL );
  443. p4 = ( IP4 * )factory( P4_INTERFACE_VERSION, NULL );
  444. g_Factory = factory;
  445. if ( !g_pMDLCache || !g_pFileSystem || !g_pFullFileSystem || !materials || !g_pMaterialSystemHardwareConfig || !g_pStudioRender )
  446. return false;
  447. WinTab_Init();
  448. // ensure we're in the same directory as the .EXE
  449. char *p;
  450. GetModuleFileName(NULL, m_szAppDir, MAX_PATH);
  451. p = strrchr(m_szAppDir, '\\');
  452. if(p)
  453. {
  454. // chop off \wc.exe
  455. p[0] = 0;
  456. }
  457. if ( IsRunningInEngine() )
  458. {
  459. strcat( m_szAppDir, "\\bin" );
  460. }
  461. // Create the message window object for capturing errors and warnings.
  462. // This does NOT create the window itself. That happens later in CMainFrame::Create.
  463. g_pwndMessage = CMessageWnd::CreateMessageWndObject();
  464. // Default location for GameConfig.txt is the same directory as Hammer.exe but this may be overridden on the command line
  465. char szGameConfigDir[MAX_PATH];
  466. APP()->GetDirectory( DIR_PROGRAM, szGameConfigDir );
  467. Options.configs.m_strConfigDir = szGameConfigDir;
  468. CHammerCmdLine cmdInfo;
  469. ParseCommandLine(cmdInfo);
  470. // Set up SteamApp() interface (for checking app ownership)
  471. SteamAPI_InitSafe();
  472. g_SteamAPIContext.Init();
  473. // Load the options
  474. // NOTE: Have to do this now, because we need it before Inits() are called
  475. // NOTE: SetRegistryKey will cause hammer to look into the registry for its values
  476. SetRegistryKey("Valve");
  477. Options.Init();
  478. if ( g_pThreadPool )
  479. {
  480. ThreadPoolStartParams_t startParams;
  481. // this will set ideal processor on each thread
  482. startParams.fDistribute = TRS_TRUE;
  483. g_pThreadPool->Start( startParams );
  484. }
  485. return true;
  486. }
  487. void CHammer::Disconnect()
  488. {
  489. g_pStudioRender = NULL;
  490. g_pFileSystem = NULL;
  491. g_pEngineAPI = NULL;
  492. g_pMDLCache = NULL;
  493. BaseClass::Disconnect();
  494. }
  495. void *CHammer::QueryInterface( const char *pInterfaceName )
  496. {
  497. // We also implement the IMatSystemSurface interface
  498. if (!Q_strncmp( pInterfaceName, INTERFACEVERSION_HAMMER, Q_strlen(INTERFACEVERSION_HAMMER) + 1))
  499. return (IHammer*)this;
  500. return NULL;
  501. }
  502. void CHammer::InitFoundryMode( CreateInterfaceFn factory, void *hGameWnd, const char *szGameDir )
  503. {
  504. m_bFoundryMode = true;
  505. if ( !CommandLine()->FindParm( "-foundrymode" ) )
  506. Error( "Running in Foundry requires -FoundryMode on the command line." );
  507. if ( !Connect( factory ) )
  508. Error( "CHammer::Connect failed" );
  509. if ( !InitSessionGameConfig( szGameDir ) )
  510. Error( "InitSessionGameConfig failed." );
  511. if ( HammerInternalInit() != INIT_OK )
  512. Error( "HammerInternalInit failed" );
  513. }
  514. void CHammer::NoteEngineGotFocus()
  515. {
  516. // Release focus on all our vgui stuff so the engine can own it.
  517. HammerVGui()->SetFocus( NULL );
  518. // Deactivate all CMapViews.
  519. CMapDoc::NoteEngineGotFocus();
  520. }
  521. bool CHammer::IsHammerVisible()
  522. {
  523. CWnd *pWnd = GetMainWnd();
  524. if ( !pWnd )
  525. return false;
  526. return pWnd->IsWindowVisible() ? true : false;
  527. }
  528. void CHammer::ToggleHammerVisible()
  529. {
  530. CWnd *pWnd = GetMainWnd();
  531. if ( !pWnd )
  532. return;
  533. if ( pWnd->IsWindowVisible() )
  534. {
  535. pWnd->ShowWindow( SW_HIDE );
  536. }
  537. else
  538. {
  539. pWnd->ShowWindow( SW_SHOW );
  540. }
  541. }
  542. //-----------------------------------------------------------------------------
  543. // Methods related to message pumping
  544. //-----------------------------------------------------------------------------
  545. bool CHammer::HammerPreTranslateMessage(MSG * pMsg)
  546. {
  547. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  548. // Copy this into the current message, needed for MFC
  549. #if _MSC_VER >= 1300
  550. _AFX_THREAD_STATE* pState = AfxGetThreadState();
  551. pState->m_msgCur = *pMsg;
  552. #else
  553. m_msgCur = *pMsg;
  554. #endif
  555. return (/*pMsg->message == WM_KICKIDLE ||*/ PreTranslateMessage(pMsg) != FALSE);
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Return true if the message just dispatched should cause OnIdle to run.
  559. //
  560. // Return false for messages which do not usually affect the state of the user
  561. // interface and happen very often.
  562. //-----------------------------------------------------------------------------
  563. bool CHammer::HammerIsIdleMessage(MSG *pMsg)
  564. {
  565. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  566. // We generate lots of WM_TIMER messages and shouldn't call OnIdle because of them.
  567. // This fixes tool tips not popping up when a map is open.
  568. if ( pMsg->message == WM_TIMER )
  569. return false;
  570. return ( IsIdleMessage(pMsg) == TRUE );
  571. }
  572. // return TRUE if more idle processing
  573. bool CHammer::HammerOnIdle(long count)
  574. {
  575. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  576. return OnIdle(count) != FALSE;
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Purpose: Adds a backslash to the end of a string if there isn't one already.
  580. // Input : psz - String to add the backslash to.
  581. //-----------------------------------------------------------------------------
  582. static void EnsureTrailingBackslash(char *psz)
  583. {
  584. if ((psz[0] != '\0') && (psz[strlen(psz) - 1] != '\\'))
  585. {
  586. strcat(psz, "\\");
  587. }
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Purpose: Tweaks our data members to enable us to import old Hammer settings
  591. // from the registry.
  592. //-----------------------------------------------------------------------------
  593. static const char *s_pszOldAppName = NULL;
  594. void CHammer::BeginImportWCSettings(void)
  595. {
  596. s_pszOldAppName = m_pszAppName;
  597. m_pszAppName = "Worldcraft";
  598. SetRegistryKey("Valve");
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose: Tweaks our data members to enable us to import old Valve Hammer Editor
  602. // settings from the registry.
  603. //-----------------------------------------------------------------------------
  604. void CHammer::BeginImportVHESettings(void)
  605. {
  606. s_pszOldAppName = m_pszAppName;
  607. m_pszAppName = "Valve Hammer Editor";
  608. SetRegistryKey("Valve");
  609. }
  610. //-----------------------------------------------------------------------------
  611. // Purpose: Restores our tweaked data members to their original state.
  612. //-----------------------------------------------------------------------------
  613. void CHammer::EndImportSettings(void)
  614. {
  615. m_pszAppName = s_pszOldAppName;
  616. SetRegistryKey("Valve");
  617. }
  618. //-----------------------------------------------------------------------------
  619. // Purpose: Retrieves various important directories.
  620. // Input : dir - Enumerated directory to retrieve.
  621. // p - Pointer to buffer that receives the full path to the directory.
  622. //-----------------------------------------------------------------------------
  623. void CHammer::GetDirectory(DirIndex_t dir, char *p)
  624. {
  625. switch (dir)
  626. {
  627. case DIR_PROGRAM:
  628. {
  629. strcpy(p, m_szAppDir);
  630. EnsureTrailingBackslash(p);
  631. break;
  632. }
  633. case DIR_PREFABS:
  634. {
  635. strcpy(p, g_pGameConfig->m_szPrefabDir);
  636. if (*p == '\0')
  637. {
  638. // The prefab folder has not been set up so quietly set it to the app directory + "/Prefabs"
  639. strcpy(p, m_szAppDir);
  640. EnsureTrailingBackslash(p);
  641. strcat(p, "Prefabs");
  642. strcpy( g_pGameConfig->m_szPrefabDir, p );
  643. }
  644. //
  645. // Make sure the prefabs folder exists. If not, create it
  646. //
  647. if ((_access( p, 0 )) == -1)
  648. {
  649. CreateDirectory(p, NULL);
  650. }
  651. break;
  652. }
  653. //
  654. // Get the game directory with a trailing backslash. This is
  655. // where the base game's resources are, such as "C:\Half-Life\valve\".
  656. //
  657. case DIR_GAME_EXE:
  658. {
  659. strcpy(p, g_pGameConfig->m_szGameExeDir);
  660. EnsureTrailingBackslash(p);
  661. break;
  662. }
  663. //
  664. // Get the mod directory with a trailing backslash. This is where
  665. // the mod's resources are, such as "C:\Half-Life\tfc\".
  666. //
  667. case DIR_MOD:
  668. {
  669. strcpy(p, g_pGameConfig->m_szModDir);
  670. EnsureTrailingBackslash(p);
  671. break;
  672. }
  673. //
  674. // Get the materials directory with a trailing backslash. This is where
  675. // the mod's materials are, such as "C:\Half-Life\tfc\materials".
  676. //
  677. case DIR_MATERIALS:
  678. {
  679. strcpy(p, g_pGameConfig->m_szModDir);
  680. EnsureTrailingBackslash(p);
  681. Q_strcat(p, "materials\\", MAX_PATH);
  682. break;
  683. }
  684. case DIR_AUTOSAVE:
  685. {
  686. strcpy( p, m_szAutosaveDir );
  687. EnsureTrailingBackslash(p);
  688. break;
  689. }
  690. }
  691. }
  692. void CHammer::SetDirectory(DirIndex_t dir, const char *p)
  693. {
  694. switch(dir)
  695. {
  696. case DIR_AUTOSAVE:
  697. {
  698. strcpy( m_szAutosaveDir, p );
  699. break;
  700. }
  701. }
  702. }
  703. //-----------------------------------------------------------------------------
  704. // Purpose: Returns a color from the application configuration storage.
  705. //-----------------------------------------------------------------------------
  706. COLORREF CHammer::GetProfileColor(const char *pszSection, const char *pszKey, int r, int g, int b)
  707. {
  708. int newR, newG, newB;
  709. CString strDefault;
  710. CString strReturn;
  711. char szBuff[128];
  712. sprintf(szBuff, "%i %i %i", r, g, b);
  713. strDefault = szBuff;
  714. strReturn = GetProfileString(pszSection, pszKey, strDefault);
  715. if (strReturn.IsEmpty())
  716. return 0;
  717. // Parse out colors.
  718. char *pStart;
  719. char *pCurrent;
  720. pStart = szBuff;
  721. pCurrent = pStart;
  722. strcpy( szBuff, (char *)(LPCSTR)strReturn );
  723. while (*pCurrent && *pCurrent != ' ')
  724. pCurrent++;
  725. *pCurrent++ = 0;
  726. newR = atoi(pStart);
  727. pStart = pCurrent;
  728. while (*pCurrent && *pCurrent != ' ')
  729. pCurrent++;
  730. *pCurrent++ = 0;
  731. newG = atoi(pStart);
  732. pStart = pCurrent;
  733. while (*pCurrent)
  734. pCurrent++;
  735. *pCurrent++ = 0;
  736. newB = atoi(pStart);
  737. return COLORREF(RGB(newR, newG, newB));
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Purpose:
  741. // Input : *pszURL -
  742. //-----------------------------------------------------------------------------
  743. void CHammer::OpenURL(const char *pszURL, HWND hwnd)
  744. {
  745. if (HINSTANCE(32) > ::ShellExecute(hwnd, "open", pszURL, NULL, NULL, 0))
  746. {
  747. AfxMessageBox("The website couldn't be opened.");
  748. }
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Purpose: Opens a URL in the default web browser by string ID.
  752. //-----------------------------------------------------------------------------
  753. void CHammer::OpenURL(UINT nID, HWND hwnd)
  754. {
  755. CString str;
  756. str.LoadString(nID);
  757. OpenURL(str, hwnd);
  758. }
  759. //-----------------------------------------------------------------------------
  760. // Purpose: Launches the help system for the specified help topic.
  761. // Input : pszTopic - Topic to open.
  762. //-----------------------------------------------------------------------------
  763. void CHammer::Help(const char *pszTopic)
  764. {
  765. //
  766. // Get the directory that the help file should be in (our program directory).
  767. //
  768. /*char szHelpDir[MAX_PATH];
  769. GetDirectory(DIR_PROGRAM, szHelpDir);
  770. //
  771. // Find the application that is associated with compiled HTML files.
  772. //
  773. char szHelpExe[MAX_PATH];
  774. HINSTANCE hResult = FindExecutable("wc.chm", szHelpDir, szHelpExe);
  775. if (hResult > (HINSTANCE)32)
  776. {
  777. //
  778. // Build the full topic with which to launch the help application.
  779. //
  780. char szParam[2 * MAX_PATH];
  781. strcpy(szParam, szHelpDir);
  782. strcat(szParam, "wc.chm");
  783. if (pszTopic != NULL)
  784. {
  785. strcat(szParam, "::/");
  786. strcat(szParam, pszTopic);
  787. }
  788. //
  789. // Launch the help application for the given topic.
  790. //
  791. hResult = ShellExecute(NULL, "open", szHelpExe, szParam, szHelpDir, SW_SHOW);
  792. }
  793. if (hResult <= (HINSTANCE)32)
  794. {
  795. char szError[MAX_PATH];
  796. sprintf(szError, "The help system could not be launched. The the following error was returned:\n%s (0x%X)", GetErrorString(), hResult);
  797. AfxMessageBox(szError);
  798. }
  799. */
  800. }
  801. static CSimpleWindowsLoggingListener s_SimpleWindowsLoggingListener;
  802. static CHammerMessageLoggingListener s_HammerMessageLoggingListener;
  803. //-----------------------------------------------------------------------------
  804. // Purpose:
  805. //-----------------------------------------------------------------------------
  806. static HANDLE dwChangeHandle = NULL;
  807. void UpdatePrefabs_Init()
  808. {
  809. // Watch the prefabs tree for file or directory creation
  810. // and deletion.
  811. if (dwChangeHandle == NULL)
  812. {
  813. char szPrefabDir[MAX_PATH];
  814. APP()->GetDirectory(DIR_PREFABS, szPrefabDir);
  815. dwChangeHandle = FindFirstChangeNotification(
  816. szPrefabDir, // directory to watch
  817. TRUE, // watch the subtree
  818. FILE_NOTIFY_CHANGE_DIR_NAME | FILE_NOTIFY_CHANGE_FILE_NAME); // watch file and dir name changes
  819. if (dwChangeHandle == INVALID_HANDLE_VALUE)
  820. {
  821. ExitProcess(GetLastError());
  822. }
  823. }
  824. }
  825. //-----------------------------------------------------------------------------
  826. // Purpose:
  827. //-----------------------------------------------------------------------------
  828. void UpdatePrefabs()
  829. {
  830. // Wait for notification.
  831. DWORD dwWaitStatus = WaitForSingleObject(dwChangeHandle, 0);
  832. if (dwWaitStatus == WAIT_OBJECT_0)
  833. {
  834. // A file was created or deleted in the prefabs tree.
  835. // Refresh the prefabs and restart the change notification.
  836. CPrefabLibrary::FreeAllLibraries();
  837. CPrefabLibrary::LoadAllLibraries();
  838. GetMainWnd()->m_ObjectBar.UpdateListForTool(ToolManager()->GetActiveToolID());
  839. if (FindNextChangeNotification(dwChangeHandle) == FALSE)
  840. {
  841. ExitProcess(GetLastError());
  842. }
  843. }
  844. }
  845. //-----------------------------------------------------------------------------
  846. // Purpose:
  847. //-----------------------------------------------------------------------------
  848. void UpdatePrefabs_Shutdown()
  849. {
  850. FindCloseChangeNotification(dwChangeHandle);
  851. }
  852. //-----------------------------------------------------------------------------
  853. // Purpose:
  854. //-----------------------------------------------------------------------------
  855. BOOL CHammer::InitInstance()
  856. {
  857. return TRUE;
  858. }
  859. //-----------------------------------------------------------------------------
  860. // Purpose: Prompt the user to select a game configuration.
  861. //-----------------------------------------------------------------------------
  862. CGameConfig *CHammer::PromptForGameConfig()
  863. {
  864. CEditGameConfigs dlg(TRUE, GetMainWnd());
  865. if (dlg.DoModal() != IDOK)
  866. {
  867. return NULL;
  868. }
  869. return dlg.GetSelectedGame();
  870. }
  871. //-----------------------------------------------------------------------------
  872. // Purpose:
  873. // Output : Returns true on success, false on failure.
  874. //-----------------------------------------------------------------------------
  875. bool CHammer::InitSessionGameConfig(const char *szGame)
  876. {
  877. CGameConfig *pConfig = NULL;
  878. bool bManualChoice = false;
  879. if ( CommandLine()->FindParm( "-chooseconfig" ) )
  880. {
  881. pConfig = PromptForGameConfig();
  882. bManualChoice = true;
  883. }
  884. if (!bManualChoice)
  885. {
  886. if (szGame && szGame[0] != '\0')
  887. {
  888. // They passed in -game on the command line, use that.
  889. pConfig = Options.configs.FindConfigForGame(szGame);
  890. if (!pConfig)
  891. {
  892. Msg(mwError, "Invalid game \"%s\" specified on the command-line, ignoring.", szGame);
  893. }
  894. }
  895. else
  896. {
  897. // No -game on the command line, try using VPROJECT.
  898. const char *pszGameDir = getenv("vproject");
  899. if ( pszGameDir )
  900. {
  901. pConfig = Options.configs.FindConfigForGame(pszGameDir);
  902. if (!pConfig)
  903. {
  904. Msg(mwError, "Invalid game \"%s\" found in VPROJECT environment variable, ignoring.", pszGameDir);
  905. }
  906. }
  907. }
  908. }
  909. if (pConfig == NULL)
  910. {
  911. // Nothing useful was passed in or found in VPROJECT.
  912. // If there's only one config, use that.
  913. if (Options.configs.GetGameConfigCount() == 1)
  914. {
  915. pConfig = Options.configs.GetGameConfig(0);
  916. }
  917. else
  918. {
  919. // Otherwise, prompt for a config to use.
  920. pConfig = PromptForGameConfig();
  921. }
  922. }
  923. if (pConfig)
  924. {
  925. CGameConfig::SetActiveGame(pConfig);
  926. return true;
  927. }
  928. return false;
  929. }
  930. //-----------------------------------------------------------------------------
  931. // Check for 16-bit color or higher.
  932. //-----------------------------------------------------------------------------
  933. bool CHammer::Check16BitColor()
  934. {
  935. // Check for 15-bit color or higher.
  936. HDC hDC = ::CreateCompatibleDC(NULL);
  937. if (hDC)
  938. {
  939. int bpp = GetDeviceCaps(hDC, BITSPIXEL);
  940. if (bpp < 15)
  941. {
  942. AfxMessageBox("Your screen must be in 16-bit color or higher to run Hammer.");
  943. return false;
  944. }
  945. ::DeleteDC(hDC);
  946. }
  947. return true;
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Purpose: Returns true if Hammer is in the process of shutting down.
  951. //-----------------------------------------------------------------------------
  952. InitReturnVal_t CHammer::Init()
  953. {
  954. return (InitReturnVal_t)WrapFunctionWithMinidumpHandler( &CHammer::StaticHammerInternalInit, this, INIT_FAILED );
  955. }
  956. int CHammer::StaticHammerInternalInit( void *pParam )
  957. {
  958. return (int)((CHammer*)pParam)->HammerInternalInit();
  959. }
  960. void HammerFileSystem_ReportSearchPath( const char *szPathID )
  961. {
  962. char szSearchPath[ 4096 ];
  963. g_pFullFileSystem->GetSearchPath( szPathID, true, szSearchPath, sizeof( szSearchPath ) );
  964. Msg( mwStatus, "------------------------------------------------------------------" );
  965. char *pszOnePath = strtok( szSearchPath, ";" );
  966. while ( pszOnePath )
  967. {
  968. Msg( mwStatus, "Search Path (%s): %s", szPathID, pszOnePath );
  969. pszOnePath = strtok( NULL, ";" );
  970. }
  971. }
  972. InitReturnVal_t CHammer::HammerInternalInit()
  973. {
  974. if ( !IsFoundryMode() )
  975. {
  976. LoggingSystem_PushLoggingState();
  977. LoggingSystem_RegisterLoggingListener( &s_SimpleWindowsLoggingListener );
  978. LoggingSystem_RegisterLoggingListener( &s_HammerMessageLoggingListener );
  979. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  980. }
  981. InitReturnVal_t nRetVal = BaseClass::Init();
  982. if ( nRetVal != INIT_OK )
  983. return nRetVal;
  984. if ( !Check16BitColor() )
  985. return INIT_FAILED;
  986. //
  987. // Create a custom window class for this application so that engine's
  988. // FindWindow will find us.
  989. //
  990. WNDCLASS wndcls;
  991. memset(&wndcls, 0, sizeof(WNDCLASS));
  992. wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
  993. wndcls.lpfnWndProc = AfxWndProc;
  994. wndcls.hInstance = AfxGetInstanceHandle();
  995. wndcls.hIcon = LoadIcon(IDR_MAINFRAME);
  996. wndcls.hCursor = LoadCursor( IDC_ARROW );
  997. wndcls.hbrBackground = (HBRUSH)0; // (COLOR_WINDOW + 1);
  998. wndcls.lpszMenuName = "IDR_MAINFRAME";
  999. wndcls.cbWndExtra = 0;
  1000. // HL Shell class name
  1001. wndcls.lpszClassName = "VALVEWORLDCRAFT";
  1002. // Register it, exit if it fails
  1003. if(!AfxRegisterClass(&wndcls))
  1004. {
  1005. AfxMessageBox("Could not register Hammer's main window class");
  1006. return INIT_FAILED;
  1007. }
  1008. srand(time(NULL));
  1009. WriteProfileString("General", "Directory", m_szAppDir);
  1010. //
  1011. // Create a window to receive shell commands from the engine, and attach it
  1012. // to our shell processor.
  1013. //
  1014. g_ShellMessageWnd.Create();
  1015. g_ShellMessageWnd.SetShell(&g_Shell);
  1016. if (bMakeLib)
  1017. return INIT_FAILED; // made library .. don't want to enter program
  1018. CHammerCmdLine cmdInfo;
  1019. ParseCommandLine(cmdInfo);
  1020. //
  1021. // Create and optionally display the splash screen.
  1022. //
  1023. CSplashWnd::EnableSplashScreen(cmdInfo.m_bShowLogo);
  1024. //
  1025. // load cmd sequences - different from options because
  1026. // users might want to share (darn registry)
  1027. //
  1028. if ( !LoadSequences( "CmdSeq.wc" ) )
  1029. {
  1030. // Try to load the default sequences if there are no user-defined ones.
  1031. LoadSequences( "CmdSeqDefault.wc" );
  1032. }
  1033. // other init:
  1034. randomize();
  1035. /*
  1036. #ifdef _AFXDLL
  1037. Enable3dControls(); // Call this when using MFC in a shared DLL
  1038. #else
  1039. Enable3dControlsStatic(); // Call this when linking to MFC statically
  1040. #endif
  1041. */
  1042. LoadStdProfileSettings(); // Load standard INI file options (including MRU)
  1043. // Register the application's document templates. Document templates
  1044. // serve as the connection between documents, frame windows and views.
  1045. pMapDocTemplate = new CHammerDocTemplate(
  1046. IDR_MAPDOC,
  1047. RUNTIME_CLASS(CMapDoc),
  1048. RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  1049. RUNTIME_CLASS(CMapView2D));
  1050. AddDocTemplate(pMapDocTemplate);
  1051. pManifestDocTemplate = new CHammerDocTemplate(
  1052. IDR_MANIFESTDOC,
  1053. RUNTIME_CLASS(CManifest),
  1054. RUNTIME_CLASS(CChildFrame), // custom MDI child frame
  1055. RUNTIME_CLASS(CMapView2D));
  1056. HINSTANCE hInst = AfxFindResourceHandle( MAKEINTRESOURCE( IDR_MAPDOC ), RT_MENU );
  1057. pManifestDocTemplate->m_hMenuShared = ::LoadMenu( hInst, MAKEINTRESOURCE( IDR_MAPDOC ) );
  1058. hInst = AfxFindResourceHandle( MAKEINTRESOURCE( IDR_MAPDOC ), RT_ACCELERATOR );
  1059. pManifestDocTemplate->m_hAccelTable = ::LoadAccelerators( hInst, MAKEINTRESOURCE( IDR_MAPDOC ) );
  1060. AddDocTemplate(pManifestDocTemplate);
  1061. // register shell file types
  1062. RegisterShellFileTypes();
  1063. //
  1064. // Initialize the rich edit control so we can use it in the entity help dialog.
  1065. //
  1066. AfxInitRichEdit();
  1067. //
  1068. // Create main MDI Frame window. Must be done AFTER registering the multidoc template!
  1069. //
  1070. CMainFrame *pMainFrame = new CMainFrame;
  1071. if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
  1072. return INIT_FAILED;
  1073. m_pMainWnd = pMainFrame;
  1074. // try to init VGUI
  1075. HammerVGui()->Init( m_pMainWnd->GetSafeHwnd() );
  1076. // The main window has been initialized, so show and update it.
  1077. if ( IsFoundryMode() )
  1078. {
  1079. m_nCmdShow = SW_SHOW;
  1080. CRect rcDesktop;
  1081. GetWindowRect( GetDesktopWindow(), &rcDesktop );
  1082. CRect rcEngineWnd;
  1083. HWND hEngineWnd = (HWND)enginetools->GetEngineHwnd();
  1084. GetWindowRect( hEngineWnd, &rcEngineWnd );
  1085. // Move the engine to the right side of the screen.
  1086. int nEngineWndX = rcDesktop.Width() - rcEngineWnd.Width();
  1087. SetWindowPos( hEngineWnd, NULL, nEngineWndX, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW );
  1088. // Move Hammer to the left and make it square.
  1089. int nHammerWndWidth = nEngineWndX;
  1090. int nHammerWndHeight = min( nHammerWndWidth, rcDesktop.Height() - 100 );
  1091. pMainFrame->SetWindowPos( NULL, 0, 0, nHammerWndWidth, nHammerWndHeight, SWP_NOZORDER | SWP_SHOWWINDOW );
  1092. // Move the properties dialog below the engine window.
  1093. pMainFrame->pObjectProperties->SetWindowPos( NULL, nEngineWndX, rcEngineWnd.Height(), 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_SHOWWINDOW );
  1094. }
  1095. else
  1096. {
  1097. m_nCmdShow = SW_SHOWMAXIMIZED;
  1098. pMainFrame->ShowWindow(m_nCmdShow);
  1099. }
  1100. pMainFrame->UpdateWindow();
  1101. if ( !IsFoundryMode() )
  1102. {
  1103. //
  1104. // Init the game and mod dirs in the file system.
  1105. // This needs to happen before calling Init on the material system.
  1106. //
  1107. CFSSearchPathsInit initInfo;
  1108. initInfo.m_pFileSystem = g_pFullFileSystem;
  1109. initInfo.m_pDirectoryName = g_pGameConfig->m_szModDir;
  1110. if ( !initInfo.m_pDirectoryName[0] )
  1111. {
  1112. static char pTempBuf[MAX_PATH];
  1113. APP()->GetDirectory(DIR_PROGRAM, pTempBuf);
  1114. strcat( pTempBuf, "..\\hl2" );
  1115. initInfo.m_pDirectoryName = pTempBuf;
  1116. }
  1117. CSplashWnd::ShowSplashScreen(pMainFrame);
  1118. if ( FileSystem_LoadSearchPaths( initInfo ) != FS_OK )
  1119. {
  1120. Error( "Unable to load search paths!\n" );
  1121. }
  1122. // Report this for the user's sake
  1123. HammerFileSystem_ReportSearchPath( "GAME" );
  1124. }
  1125. // Now that we've initialized the file system, we can parse this config's gameinfo.txt for the additional settings there.
  1126. g_pGameConfig->ParseGameInfo();
  1127. if ( !IsFoundryMode() )
  1128. {
  1129. materials->ModInit();
  1130. }
  1131. //
  1132. // Initialize the texture manager and load all textures.
  1133. //
  1134. if (!g_Textures.Initialize(m_pMainWnd->m_hWnd))
  1135. {
  1136. Msg(mwError, "Failed to initialize texture system.");
  1137. }
  1138. else
  1139. {
  1140. //
  1141. // Initialize studio model rendering (must happen after g_Textures.Initialize since
  1142. // g_Textures.Initialize kickstarts the material system and sets up g_MaterialSystemClientFactory)
  1143. //
  1144. StudioModel::Initialize();
  1145. g_Textures.LoadAllGraphicsFiles();
  1146. g_Textures.SetActiveConfig(g_pGameConfig);
  1147. }
  1148. //
  1149. // Initialize the particle system manager
  1150. //
  1151. g_pParticleSystemMgr->Init( NULL, true );
  1152. g_pParticleSystemMgr->AddBuiltinSimulationOperators();
  1153. g_pParticleSystemMgr->AddBuiltinRenderingOperators();
  1154. // Watch for changes to models.
  1155. InitStudioFileChangeWatcher();
  1156. LoadFileSystemDialogModule();
  1157. // Load detail object descriptions.
  1158. char szGameDir[_MAX_PATH];
  1159. APP()->GetDirectory(DIR_MOD, szGameDir);
  1160. DetailObjects::LoadEmitDetailObjectDictionary( szGameDir );
  1161. // Initialize the sound system
  1162. g_Sounds.Initialize();
  1163. UpdatePrefabs_Init();
  1164. // Indicate that we are ready to use.
  1165. m_pMainWnd->FlashWindow(TRUE);
  1166. // Parse command line for standard shell commands, DDE, file open
  1167. if ( !IsRunningInEngine() )
  1168. {
  1169. if ( Q_stristr( cmdInfo.m_strFileName, ".vmf" ) )
  1170. {
  1171. // we don't want to make a new file (default behavior if no file
  1172. // is specified on the commandline.)
  1173. // Dispatch commands specified on the command line
  1174. if (!ProcessShellCommand(cmdInfo))
  1175. return INIT_FAILED;
  1176. }
  1177. }
  1178. if ( !IsFoundryMode() && Options.general.bClosedCorrectly == FALSE )
  1179. {
  1180. CString strLastGoodSave = APP()->GetProfileString("General", "Last Good Save", "");
  1181. if ( strLastGoodSave.GetLength() != 0 )
  1182. {
  1183. char msg[1024];
  1184. V_snprintf( msg, sizeof( msg ), "Hammer did not shut down correctly the last time it was used.\nWould you like to load the last saved file?\n(%s)", (const char*)strLastGoodSave );
  1185. if ( AfxMessageBox( msg, MB_YESNO ) == IDYES )
  1186. {
  1187. LoadLastGoodSave();
  1188. }
  1189. }
  1190. }
  1191. #ifdef VPROF_HAMMER
  1192. g_VProfCurrentProfile.Start();
  1193. #endif
  1194. // Execute the post-init registered callbacks
  1195. for ( int iFn = 0; iFn < s_appRegisteredPostInitFns.Count(); ++ iFn )
  1196. {
  1197. void (*fn)() = s_appRegisteredPostInitFns[ iFn ];
  1198. (*fn)();
  1199. }
  1200. WinTab_Open( m_pMainWnd->m_hWnd );
  1201. CSplashWnd::HideSplashScreen();
  1202. // create the lighting preview thread
  1203. g_LPreviewThread = CreateSimpleThread( LightingPreviewThreadFN, 0 );
  1204. return INIT_OK;
  1205. }
  1206. int CHammer::MainLoop()
  1207. {
  1208. return WrapFunctionWithMinidumpHandler( StaticInternalMainLoop, this, -1 );
  1209. }
  1210. int CHammer::StaticInternalMainLoop( void *pParam )
  1211. {
  1212. return ((CHammer*)pParam)->InternalMainLoop();
  1213. }
  1214. int CHammer::InternalMainLoop()
  1215. {
  1216. MSG msg;
  1217. g_pDataCache->SetSize( 128 * 1024 * 1024 );
  1218. // For tracking the idle time state
  1219. bool bIdle = true;
  1220. long lIdleCount = 0;
  1221. // We've got our own message pump here
  1222. g_pInputSystem->EnableMessagePump( false );
  1223. // Acquire and dispatch messages until a WM_QUIT message is received.
  1224. for (;;)
  1225. {
  1226. RunFrame();
  1227. // Do idle processing at most once per frame, incrementing the counter until we get an
  1228. // idle message. The counter is used as a general indication of how idle the application is,
  1229. // so critical idle processing is done for lower values of lIdleCount, and less important
  1230. // stuff is done as lIdleCount increases.
  1231. //
  1232. // When there's no more idle work to do, OnIdle returns false and we stop doing idle
  1233. // processing until another idle message is processed by the message loop.
  1234. if ( bIdle && !HammerOnIdle(lIdleCount++) )
  1235. {
  1236. bIdle = false; // done with idle work for now
  1237. }
  1238. // Execute the message loop registered callbacks
  1239. for ( int iFn = 0; iFn < s_appRegisteredMessageLoop.Count(); ++ iFn )
  1240. {
  1241. void (*fn)() = s_appRegisteredMessageLoop[ iFn ];
  1242. (*fn)();
  1243. }
  1244. //
  1245. // Pump messages until the message queue is empty.
  1246. //
  1247. while (::PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE))
  1248. {
  1249. if ( msg.message == WM_QUIT )
  1250. return 1;
  1251. // Pump the message through a custom message
  1252. // pre-translation chain
  1253. for ( int iFn = 0; iFn < s_appRegisteredMessagePreTrans.Count(); ++ iFn )
  1254. {
  1255. void (*fn)( MSG * ) = s_appRegisteredMessagePreTrans[ iFn ];
  1256. (*fn)( &msg );
  1257. }
  1258. if ( !HammerPreTranslateMessage(&msg) )
  1259. {
  1260. ::TranslateMessage(&msg);
  1261. ::DispatchMessage(&msg);
  1262. }
  1263. // Reset idle state after pumping idle message.
  1264. if ( HammerIsIdleMessage(&msg) )
  1265. {
  1266. bIdle = true;
  1267. lIdleCount = 0;
  1268. }
  1269. }
  1270. }
  1271. Assert(0); // not reachable
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Shuts down hammer
  1275. //-----------------------------------------------------------------------------
  1276. void CHammer::Shutdown()
  1277. {
  1278. if ( g_LPreviewThread )
  1279. {
  1280. MessageToLPreview StopMsg( LPREVIEW_MSG_EXIT );
  1281. g_HammerToLPreviewMsgQueue.QueueMessage( StopMsg );
  1282. ThreadJoin( g_LPreviewThread );
  1283. g_LPreviewThread = 0;
  1284. }
  1285. // Execute the pre-shutdown registered callbacks
  1286. for ( int iFn = s_appRegisteredPreShutdownFns.Count(); iFn --> 0 ; )
  1287. {
  1288. void (*fn)() = s_appRegisteredPreShutdownFns[ iFn ];
  1289. (*fn)();
  1290. }
  1291. #ifdef VPROF_HAMMER
  1292. g_VProfCurrentProfile.Stop();
  1293. #endif
  1294. // PrintBudgetGroupTimes_Recursive( g_VProfCurrentProfile.GetRoot() );
  1295. HammerVGui()->Shutdown();
  1296. UnloadFileSystemDialogModule();
  1297. // Delete the command sequences.
  1298. int nSequenceCount = m_CmdSequences.GetSize();
  1299. for (int i = 0; i < nSequenceCount; i++)
  1300. {
  1301. CCommandSequence *pSeq = m_CmdSequences[i];
  1302. if ( pSeq != NULL )
  1303. {
  1304. delete pSeq;
  1305. m_CmdSequences[i] = NULL;
  1306. }
  1307. }
  1308. g_Textures.ShutDown();
  1309. // Shutdown the sound system
  1310. g_Sounds.ShutDown();
  1311. materials->ModShutdown();
  1312. BaseClass::Shutdown();
  1313. }
  1314. //-----------------------------------------------------------------------------
  1315. // Methods used by the engine
  1316. //-----------------------------------------------------------------------------
  1317. const char *CHammer::GetDefaultMod()
  1318. {
  1319. return g_pGameConfig->GetMod();
  1320. }
  1321. const char *CHammer::GetDefaultGame()
  1322. {
  1323. return g_pGameConfig->GetGame();
  1324. }
  1325. const char *CHammer::GetDefaultModFullPath()
  1326. {
  1327. return g_pGameConfig->m_szModDir;
  1328. }
  1329. //-----------------------------------------------------------------------------
  1330. // Pops up the options dialog
  1331. //-----------------------------------------------------------------------------
  1332. RequestRetval_t CHammer::RequestNewConfig()
  1333. {
  1334. if ( !Options.RunConfigurationDialog() )
  1335. return REQUEST_QUIT;
  1336. return REQUEST_OK;
  1337. }
  1338. //-----------------------------------------------------------------------------
  1339. // Purpose: Returns true if Hammer is in the process of shutting down.
  1340. //-----------------------------------------------------------------------------
  1341. bool CHammer::IsClosing()
  1342. {
  1343. return m_bClosing;
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // Purpose: Signals the beginning of app shutdown. Should be called before
  1347. // rendering views.
  1348. //-----------------------------------------------------------------------------
  1349. void CHammer::BeginClosing()
  1350. {
  1351. m_bClosing = true;
  1352. }
  1353. //-----------------------------------------------------------------------------
  1354. // Purpose:
  1355. //-----------------------------------------------------------------------------
  1356. int CHammer::ExitInstance()
  1357. {
  1358. g_ShellMessageWnd.DestroyWindow();
  1359. UpdatePrefabs_Shutdown();
  1360. if ( !IsFoundryMode() )
  1361. {
  1362. LoggingSystem_PopLoggingState();
  1363. }
  1364. SaveStdProfileSettings();
  1365. return CWinApp::ExitInstance();
  1366. }
  1367. //-----------------------------------------------------------------------------
  1368. // Purpose: this function sets the global flag indicating if new documents should
  1369. // be visible.
  1370. // Input : bIsVisible - flag to indicate visibility status.
  1371. //-----------------------------------------------------------------------------
  1372. void CHammer::SetIsNewDocumentVisible( bool bIsVisible )
  1373. {
  1374. CHammer::m_bIsNewDocumentVisible = bIsVisible;
  1375. }
  1376. //-----------------------------------------------------------------------------
  1377. // Purpose: this functionr eturns the global flag indicating if new documents should
  1378. // be visible.
  1379. //-----------------------------------------------------------------------------
  1380. bool CHammer::IsNewDocumentVisible( void )
  1381. {
  1382. return CHammer::m_bIsNewDocumentVisible;
  1383. }
  1384. void CHammer::SetCustomAccelerator( HWND hWnd, WORD nID )
  1385. {
  1386. m_CustomAcceleratorWindow = hWnd;
  1387. m_CustomAccelerator = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( nID ) );
  1388. }
  1389. void CHammer::ClearCustomAccelerator( )
  1390. {
  1391. m_CustomAcceleratorWindow = NULL;
  1392. }
  1393. /////////////////////////////////////////////////////////////////////////////
  1394. // CAboutDlg dialog used for App About
  1395. class CAboutDlg : public CDialog
  1396. {
  1397. public:
  1398. CAboutDlg();
  1399. // Dialog Data
  1400. //{{AFX_DATA(CAboutDlg)
  1401. enum { IDD = IDD_ABOUTBOX };
  1402. CStatic m_cRedHerring;
  1403. CButton m_Order;
  1404. //}}AFX_DATA
  1405. // ClassWizard generated virtual function overrides
  1406. //{{AFX_VIRTUAL(CAboutDlg)
  1407. protected:
  1408. virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
  1409. //}}AFX_VIRTUAL
  1410. // Implementation
  1411. protected:
  1412. //{{AFX_MSG(CAboutDlg)
  1413. afx_msg void OnOrder();
  1414. virtual BOOL OnInitDialog();
  1415. //}}AFX_MSG
  1416. DECLARE_MESSAGE_MAP()
  1417. };
  1418. //-----------------------------------------------------------------------------
  1419. // Purpose:
  1420. //-----------------------------------------------------------------------------
  1421. CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
  1422. {
  1423. //{{AFX_DATA_INIT(CAboutDlg)
  1424. //}}AFX_DATA_INIT
  1425. }
  1426. //-----------------------------------------------------------------------------
  1427. // Purpose:
  1428. // Input : pDX -
  1429. //-----------------------------------------------------------------------------
  1430. void CAboutDlg::DoDataExchange(CDataExchange* pDX)
  1431. {
  1432. CDialog::DoDataExchange(pDX);
  1433. //{{AFX_DATA_MAP(CAboutDlg)
  1434. DDX_Control(pDX, IDC_REDHERRING, m_cRedHerring);
  1435. DDX_Control(pDX, IDC_ORDER, m_Order);
  1436. //}}AFX_DATA_MAP
  1437. }
  1438. #include <process.h>
  1439. //-----------------------------------------------------------------------------
  1440. // Purpose:
  1441. //-----------------------------------------------------------------------------
  1442. void CAboutDlg::OnOrder()
  1443. {
  1444. char szBuf[MAX_PATH];
  1445. GetWindowsDirectory(szBuf, MAX_PATH);
  1446. strcat(szBuf, "\\notepad.exe");
  1447. _spawnl(_P_NOWAIT, szBuf, szBuf, "order.txt", NULL);
  1448. }
  1449. #define DEMO_VERSION 0
  1450. // 1, 4, 8, 17, 0, 0 // Encodes "Valve"
  1451. #if DEMO_VERSION
  1452. char gVersion[] = {
  1453. #if DEMO_VERSION==1
  1454. 7, 38, 68, 32, 4, 77, 12, 1, 0 // Encodes "PC Gamer Demo"
  1455. #elif DEMO_VERSION==2
  1456. 7, 38, 68, 32, 4, 77, 12, 0, 0 // Encodes "PC Games Demo"
  1457. #elif DEMO_VERSION==3
  1458. 20, 10, 9, 23, 16, 84, 12, 1, 0, 38, 65, 25, 6, 1, 11, 119, 50, 11, 21, 9, 68, 0 // Encodes "Computer Gaming World Demo"
  1459. #elif DEMO_VERSION==4
  1460. 25, 0, 28, 19, 72, 103, 12, 29, 69, 19, 65, 0, 6, 0, 2, 0 // Encodes "Next-Generation Demo"
  1461. #elif DEMO_VERSION==5
  1462. 20, 10, 9, 23, 16, 84, 12, 1, 0, 38, 65, 25, 10, 79, 41, 57, 17, 1, 21, 17, 65, 0, 29, 77, 4, 78, 0, 0 // Encodes "Computer Game Entertainment"
  1463. #elif DEMO_VERSION==6
  1464. 20, 10, 9, 23, 16, 84, 12, 1, 0, 0, 78, 16, 79, 33, 9, 35, 69, 52, 11, 4, 89, 12, 1, 0 // Encodes "Computer and Net Player"
  1465. #elif DEMO_VERSION==7
  1466. 50, 72, 52, 43, 36, 121, 0 // Encodes "e-PLAY"
  1467. #elif DEMO_VERSION==8
  1468. 4, 17, 22, 6, 17, 69, 14, 10, 0, 49, 76, 1, 28, 0 // Encodes "Strategy Plus"
  1469. #elif DEMO_VERSION==9
  1470. 7, 38, 68, 42, 4, 71, 8, 9, 73, 15, 69, 0 // Encodes "PC Magazine"
  1471. #elif DEMO_VERSION==10
  1472. 5, 10, 8, 11, 12, 78, 14, 83, 115, 21, 79, 26, 10, 0 // Encodes "Rolling Stone"
  1473. #elif DEMO_VERSION==11
  1474. 16, 4, 9, 2, 22, 80, 6, 7, 0 // Encodes "Gamespot"
  1475. #endif
  1476. };
  1477. static char gKey[] = "Wedge is a tool"; // Decrypt key
  1478. // XOR a string with a key
  1479. void Encode( char *pstring, char *pkey, int strLen )
  1480. {
  1481. int i, len;
  1482. len = strlen( pkey );
  1483. for ( i = 0; i < strLen; i++ )
  1484. pstring[i] ^= pkey[ i % len ];
  1485. }
  1486. #endif // DEMO_VERSION
  1487. //-----------------------------------------------------------------------------
  1488. // Purpose:
  1489. // Output : Returns TRUE on success, FALSE on failure.
  1490. //-----------------------------------------------------------------------------
  1491. BOOL CAboutDlg::OnInitDialog(void)
  1492. {
  1493. CDialog::OnInitDialog();
  1494. m_Order.SetRedraw(FALSE);
  1495. #if DEMO_VERSION
  1496. static BOOL bFirst = TRUE;
  1497. if(bFirst)
  1498. {
  1499. Encode(gVersion, gKey, sizeof(gVersion)-1);
  1500. bFirst = FALSE;
  1501. }
  1502. CString str;
  1503. str.Format("%s Demo", gVersion);
  1504. m_cRedHerring.SetWindowText(str);
  1505. #endif // DEMO_VERSION
  1506. //
  1507. // Display the build number.
  1508. //
  1509. CWnd *pWnd = GetDlgItem(IDC_BUILD_NUMBER);
  1510. if (pWnd != NULL)
  1511. {
  1512. char szTemp1[MAX_PATH];
  1513. char szTemp2[MAX_PATH];
  1514. int nBuild = build_number();
  1515. pWnd->GetWindowText(szTemp1, sizeof(szTemp1));
  1516. sprintf(szTemp2, szTemp1, nBuild);
  1517. pWnd->SetWindowText(szTemp2);
  1518. }
  1519. //
  1520. // For SDK builds, append "SDK" to the version number.
  1521. //
  1522. #ifdef SDK_BUILD
  1523. char szTemp[MAX_PATH];
  1524. GetWindowText(szTemp, sizeof(szTemp));
  1525. strcat(szTemp, " SDK");
  1526. SetWindowText(szTemp);
  1527. #endif // SDK_BUILD
  1528. return TRUE;
  1529. }
  1530. BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
  1531. //{{AFX_MSG_MAP(CAboutDlg)
  1532. ON_BN_CLICKED(IDC_ORDER, OnOrder)
  1533. //}}AFX_MSG_MAP
  1534. END_MESSAGE_MAP()
  1535. //-----------------------------------------------------------------------------
  1536. // Purpose:
  1537. //-----------------------------------------------------------------------------
  1538. void CHammer::OnAppAbout(void)
  1539. {
  1540. CAboutDlg aboutDlg;
  1541. aboutDlg.DoModal();
  1542. #ifdef VPROF_HAMMER
  1543. g_VProfCurrentProfile.OutputReport();
  1544. g_VProfCurrentProfile.Reset();
  1545. g_pMemAlloc->DumpStats();
  1546. #endif
  1547. }
  1548. //-----------------------------------------------------------------------------
  1549. // Purpose:
  1550. //-----------------------------------------------------------------------------
  1551. void CHammer::OnFileNew(void)
  1552. {
  1553. pMapDocTemplate->OpenDocumentFile(NULL);
  1554. if(Options.general.bLoadwinpos && Options.general.bIndependentwin)
  1555. {
  1556. ::GetMainWnd()->LoadWindowStates();
  1557. }
  1558. }
  1559. //-----------------------------------------------------------------------------
  1560. // Purpose:
  1561. //-----------------------------------------------------------------------------
  1562. void CHammer::OnFileOpen(void)
  1563. {
  1564. // if there is no initial directory use the one specified in the game configuration
  1565. static char szInitialDir[MAX_PATH] = "";
  1566. if (szInitialDir[0] == '\0')
  1567. {
  1568. strcpy(szInitialDir, g_pGameConfig->szMapDir);
  1569. }
  1570. OPENFILENAME ofn;
  1571. //memory buffer to contain the file name
  1572. char szFileNameBuffer[MAX_PATH];
  1573. ZeroMemory( &ofn , sizeof( ofn));
  1574. ofn.lStructSize = sizeof ( ofn );
  1575. ofn.hwndOwner = AfxGetMainWnd()->GetSafeHwnd();
  1576. ofn.lpstrFile = szFileNameBuffer;
  1577. ofn.lpstrFile[0] = '\0';
  1578. ofn.nMaxFile = sizeof( szFileNameBuffer );
  1579. ofn.lpstrFilter = "Valve Map Files (*.vmf;*.vmm)\0*.vmf;*.vmm\0Valve Map Files Autosave (*.vmf_autosave)\0*.vmf_autosave\0Worldcraft RMFs (*.rmf)\0*.rmf\0Worldcraft Maps (*.map)\0*.map\0All\0*.*\0";
  1580. ofn.nFilterIndex =1;
  1581. ofn.lpstrFileTitle = NULL ;
  1582. ofn.nMaxFileTitle = 0 ;
  1583. ofn.lpstrInitialDir=szInitialDir;
  1584. ofn.Flags = OFN_LONGNAMES | OFN_HIDEREADONLY | OFN_NOCHANGEDIR;
  1585. // if the user cancels or closes the Open dialog box or an error occurs, the return value is zero.
  1586. if (!GetOpenFileName( &ofn ) )
  1587. {
  1588. return;
  1589. }
  1590. //
  1591. // Get the directory they browsed to for next time.
  1592. //
  1593. CString str = ofn.lpstrFile;
  1594. int nSlash = str.ReverseFind('\\');
  1595. if (nSlash != -1)
  1596. {
  1597. strcpy(szInitialDir, str.Left(nSlash));
  1598. }
  1599. // add the appropriate extension (based on filter type) if it was unspecified by the user
  1600. if (str.Find('.') == -1)
  1601. {
  1602. switch (ofn.nFilterIndex)
  1603. {
  1604. case 1:
  1605. {
  1606. str += ".vmf";
  1607. break;
  1608. }
  1609. case 2:
  1610. {
  1611. str += ".vmf_autosave";
  1612. break;
  1613. }
  1614. case 3:
  1615. {
  1616. str += ".rmf";
  1617. break;
  1618. }
  1619. case 4:
  1620. {
  1621. str += ".map";
  1622. break;
  1623. }
  1624. }
  1625. }
  1626. OpenDocumentFile(str);
  1627. }
  1628. //-----------------------------------------------------------------------------
  1629. // This is the generic file open function that is called by the framework.
  1630. //-----------------------------------------------------------------------------
  1631. CDocument *CHammer::OpenDocumentFile(LPCTSTR lpszFileName)
  1632. {
  1633. CDocument *pDoc = OpenDocumentOrInstanceFile( lpszFileName );
  1634. // Do work that needs to happen after opening all instances here.
  1635. // NOTE: Make sure this work doesn't need to happen per instance!!!
  1636. return pDoc;
  1637. }
  1638. //-----------------------------------------------------------------------------
  1639. //-----------------------------------------------------------------------------
  1640. CDocument *CHammer::OpenDocumentOrInstanceFile(LPCTSTR lpszFileName)
  1641. {
  1642. // CWinApp::OnOpenRecentFile may get its file history cycled through by instances being opened, thus the pointer becomes invalid
  1643. CString SaveFileName = lpszFileName;
  1644. if(GetFileAttributes( SaveFileName ) == 0xFFFFFFFF)
  1645. {
  1646. CString Message;
  1647. Message = "The file " + SaveFileName + " does not exist.";
  1648. AfxMessageBox( Message );
  1649. return NULL;
  1650. }
  1651. CheckForFileSync( SaveFileName, CHammer::m_bIsNewDocumentVisible );
  1652. CDocument *pDoc = m_pDocManager->OpenDocumentFile( SaveFileName );
  1653. CMapDoc *pMapDoc = dynamic_cast< CMapDoc * >( pDoc );
  1654. if ( pMapDoc )
  1655. {
  1656. CMapDoc::SetActiveMapDoc( pMapDoc );
  1657. pMapDoc->CheckFileStatus();
  1658. }
  1659. if( pDoc && Options.general.bLoadwinpos && Options.general.bIndependentwin)
  1660. {
  1661. ::GetMainWnd()->LoadWindowStates();
  1662. }
  1663. if ( pMapDoc && !CHammer::IsNewDocumentVisible() )
  1664. {
  1665. pMapDoc->ShowWindow( false );
  1666. }
  1667. else
  1668. {
  1669. pMapDoc->ShowWindow( true );
  1670. }
  1671. if ( pDoc && ((CMapDoc *)pDoc)->IsAutosave() )
  1672. {
  1673. char szRenameMessage[MAX_PATH+MAX_PATH+256];
  1674. CString newMapPath = *((CMapDoc *)pDoc)->AutosavedFrom();
  1675. sprintf( szRenameMessage, "This map was loaded from an autosave file.\nWould you like to rename it from \"%s\" to \"%s\"?\nNOTE: This will not save the file with the new name; it will only rename it.", SaveFileName, newMapPath );
  1676. if ( AfxMessageBox( szRenameMessage, MB_ICONHAND | MB_YESNO ) == IDYES )
  1677. {
  1678. ((CMapDoc *)pDoc)->SetPathName( newMapPath );
  1679. }
  1680. }
  1681. else
  1682. {
  1683. if ( CHammer::m_bIsNewDocumentVisible == true )
  1684. {
  1685. pMapDoc->CheckFileStatus();
  1686. if ( pMapDoc->IsReadOnly() == true && pMapDoc->IsCheckedOut() == false )
  1687. {
  1688. if ( pMapDoc->IsVersionControlled() )
  1689. {
  1690. CUtlString dialogText;
  1691. dialogText.Format("This map is not checked out. Would you like to check it out?\n\n%s", SaveFileName );
  1692. CDialogWithCheckbox Dialog( "Checkout File", dialogText,"Check out BSP.", false, !pMapDoc->BspOkToCheckOut() );
  1693. if( Dialog.DoModal() == IDOK )
  1694. {
  1695. pMapDoc->CheckOut();
  1696. if ( pMapDoc->IsReadOnly() )
  1697. {
  1698. AfxMessageBox( "Checkout was NOT successful!", MB_OK ) ;
  1699. }
  1700. }
  1701. if ( Dialog.IsCheckboxChecked() )
  1702. {
  1703. pMapDoc->CheckOutBsp();
  1704. }
  1705. }
  1706. else
  1707. {
  1708. char szMessage[ MAX_PATH + MAX_PATH+ 256 ];
  1709. sprintf( szMessage, "This map is marked as READ ONLY. You will not be able to save this file.\n\n%s", SaveFileName );
  1710. AfxMessageBox( szMessage );
  1711. }
  1712. }
  1713. }
  1714. }
  1715. return pDoc;
  1716. }
  1717. //-----------------------------------------------------------------------------
  1718. // Returns true if this is a key message that is not a special dialog navigation message.
  1719. //-----------------------------------------------------------------------------
  1720. inline bool IsKeyStrokeMessage( MSG *pMsg )
  1721. {
  1722. if ( ( pMsg->message != WM_KEYDOWN ) && ( pMsg->message != WM_CHAR ) )
  1723. return false;
  1724. // Check for special dialog navigation characters -- they don't count
  1725. if ( ( pMsg->wParam == VK_ESCAPE ) || ( pMsg->wParam == VK_RETURN ) || ( pMsg->wParam == VK_TAB ) )
  1726. return false;
  1727. if ( ( pMsg->wParam == VK_UP ) || ( pMsg->wParam == VK_DOWN ) || ( pMsg->wParam == VK_LEFT ) || ( pMsg->wParam == VK_RIGHT ) )
  1728. return false;
  1729. return true;
  1730. }
  1731. //-----------------------------------------------------------------------------
  1732. //-----------------------------------------------------------------------------
  1733. BOOL CHammer::PreTranslateMessage(MSG* pMsg)
  1734. {
  1735. // CG: The following lines were added by the Splash Screen component.
  1736. if (CSplashWnd::PreTranslateAppMessage(pMsg))
  1737. return TRUE;
  1738. // Suppress the accelerator table for edit controls so that users can type
  1739. // uppercase characters without invoking Hammer tools.
  1740. if ( IsKeyStrokeMessage( pMsg ) )
  1741. {
  1742. char className[80];
  1743. ::GetClassNameA( pMsg->hwnd, className, sizeof( className ) );
  1744. // The classname of dialog window in the VGUI model browser and particle browser is AfxWnd80sd in Debug and AfxWnd80s in Release
  1745. // For later versions of visual studio, it is afxwnd100s and afxwnd100sd. So for future proofing this we're just gonig to check on afxwnd
  1746. if ( !V_stricmp( className, "edit" ) || !V_strnicmp( className, "afxwnd", strlen( "afxwnd" ) ) )
  1747. {
  1748. // Typing in an edit control. Don't pretranslate, just translate/dispatch.
  1749. return FALSE;
  1750. }
  1751. if ( m_CustomAcceleratorWindow != NULL )
  1752. {
  1753. if ( TranslateAccelerator( m_CustomAcceleratorWindow, m_CustomAccelerator, pMsg ) != 0 )
  1754. {
  1755. return TRUE;
  1756. }
  1757. }
  1758. }
  1759. return CWinApp::PreTranslateMessage(pMsg);
  1760. }
  1761. //-----------------------------------------------------------------------------
  1762. // Purpose:
  1763. //-----------------------------------------------------------------------------
  1764. bool CHammer::LoadSequences( const char *szSeqFileName )
  1765. {
  1766. char szRootDir[MAX_PATH];
  1767. char szFullPath[MAX_PATH];
  1768. APP()->GetDirectory(DIR_PROGRAM, szRootDir);
  1769. Q_MakeAbsolutePath( szFullPath, MAX_PATH, szSeqFileName, szRootDir );
  1770. std::ifstream file(szFullPath, std::ios::in | std::ios::binary);
  1771. if( !file.is_open() )
  1772. {
  1773. return false; // none to load
  1774. }
  1775. // skip past header & version
  1776. float fThisVersion;
  1777. file.seekg(strlen(pszSequenceHdr));
  1778. file.read((char*)&fThisVersion, sizeof fThisVersion);
  1779. // read number of sequences
  1780. DWORD dwSize;
  1781. int nSeq;
  1782. file.read((char*)&dwSize, sizeof dwSize);
  1783. nSeq = dwSize;
  1784. for(int i = 0; i < nSeq; i++)
  1785. {
  1786. CCommandSequence *pSeq = new CCommandSequence;
  1787. file.read(pSeq->m_szName, 128);
  1788. // read commands in sequence
  1789. file.read((char*)&dwSize, sizeof dwSize);
  1790. int nCmd = dwSize;
  1791. CCOMMAND cmd;
  1792. for(int iCmd = 0; iCmd < nCmd; iCmd++)
  1793. {
  1794. if(fThisVersion < 0.2f)
  1795. {
  1796. file.read((char*)&cmd, sizeof(CCOMMAND)-1);
  1797. cmd.bNoWait = FALSE;
  1798. }
  1799. else
  1800. {
  1801. file.read((char*)&cmd, sizeof(CCOMMAND));
  1802. }
  1803. pSeq->m_Commands.Add(cmd);
  1804. }
  1805. m_CmdSequences.Add(pSeq);
  1806. }
  1807. return true;
  1808. }
  1809. //-----------------------------------------------------------------------------
  1810. // Purpose:
  1811. //-----------------------------------------------------------------------------
  1812. void CHammer::SaveSequences(void)
  1813. {
  1814. char szRootDir[MAX_PATH];
  1815. char szFullPath[MAX_PATH];
  1816. APP()->GetDirectory(DIR_PROGRAM, szRootDir);
  1817. Q_MakeAbsolutePath( szFullPath, MAX_PATH, "CmdSeq.wc", szRootDir );
  1818. std::ofstream file( szFullPath, std::ios::out | std::ios::binary );
  1819. // write header
  1820. file.write(pszSequenceHdr, Q_strlen(pszSequenceHdr));
  1821. // write out version
  1822. file.write((char*)&fSequenceVersion, sizeof(float));
  1823. // write out each sequence..
  1824. int i, nSeq = m_CmdSequences.GetSize();
  1825. DWORD dwSize = nSeq;
  1826. file.write((char*)&dwSize, sizeof dwSize);
  1827. for(i = 0; i < nSeq; i++)
  1828. {
  1829. CCommandSequence *pSeq = m_CmdSequences[i];
  1830. // write name of sequence
  1831. file.write(pSeq->m_szName, 128);
  1832. // write number of commands
  1833. int nCmd = pSeq->m_Commands.GetSize();
  1834. dwSize = nCmd;
  1835. file.write((char*)&dwSize, sizeof dwSize);
  1836. // write commands ..
  1837. for(int iCmd = 0; iCmd < nCmd; iCmd++)
  1838. {
  1839. CCOMMAND &cmd = pSeq->m_Commands[iCmd];
  1840. file.write((char*)&cmd, sizeof cmd);
  1841. }
  1842. }
  1843. }
  1844. void CHammer::SetForceRenderNextFrame()
  1845. {
  1846. m_bForceRenderNextFrame = true;
  1847. }
  1848. bool CHammer::GetForceRenderNextFrame()
  1849. {
  1850. return m_bForceRenderNextFrame;
  1851. }
  1852. //-----------------------------------------------------------------------------
  1853. // Purpose:
  1854. // Input : *pDoc -
  1855. //-----------------------------------------------------------------------------
  1856. void CHammer::UpdateLighting(CMapDoc *pDoc)
  1857. {
  1858. static int lastPercent = -20000;
  1859. int curPercent = -10000;
  1860. IBSPLighting *pLighting = pDoc->GetBSPLighting();
  1861. if ( pLighting )
  1862. {
  1863. // Update 5x / second.
  1864. static DWORD lastTime = 0;
  1865. DWORD curTime = GetTickCount();
  1866. if ( curTime - lastTime < 200 )
  1867. {
  1868. curPercent = lastPercent; // no change
  1869. }
  1870. else
  1871. {
  1872. curPercent = (int)( pLighting->GetPercentComplete() * 10000.0f );
  1873. lastTime = curTime;
  1874. }
  1875. // Redraw the views when new lightmaps are ready.
  1876. if ( pLighting->CheckForNewLightmaps() )
  1877. {
  1878. SetForceRenderNextFrame();
  1879. pDoc->UpdateAllViews( MAPVIEW_UPDATE_ONLY_3D );
  1880. }
  1881. }
  1882. // Update the status text.
  1883. if ( curPercent == -10000 )
  1884. {
  1885. SetStatusText( SBI_LIGHTPROGRESS, "<->" );
  1886. }
  1887. else if( curPercent != lastPercent )
  1888. {
  1889. char str[256];
  1890. sprintf( str, "%.2f%%", curPercent / 100.0f );
  1891. SetStatusText( SBI_LIGHTPROGRESS, str );
  1892. }
  1893. lastPercent = curPercent;
  1894. }
  1895. //-----------------------------------------------------------------------------
  1896. // Purpose: Performs idle processing. Runs the frame and does MFC idle processing.
  1897. // Input : lCount - The number of times OnIdle has been called in succession,
  1898. // indicating the relative length of time the app has been idle without
  1899. // user input.
  1900. // Output : Returns TRUE if there is more idle processing to do, FALSE if not.
  1901. //-----------------------------------------------------------------------------
  1902. BOOL CHammer::OnIdle(LONG lCount)
  1903. {
  1904. UpdatePrefabs();
  1905. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  1906. if (pDoc)
  1907. {
  1908. UpdateLighting(pDoc);
  1909. }
  1910. g_Textures.UpdateFileChangeWatchers();
  1911. UpdateStudioFileChangeWatcher();
  1912. return(CWinApp::OnIdle(lCount));
  1913. }
  1914. //-----------------------------------------------------------------------------
  1915. // Purpose: Renders the realtime views.
  1916. //-----------------------------------------------------------------------------
  1917. void CHammer::RunFrame(void)
  1918. {
  1919. // Note: since hammer may well not even have a 3D window visible
  1920. // at any given time, we have to call into the material system to
  1921. // make it deal with device lost. Usually this happens during SwapBuffers,
  1922. // but we may well not call SwapBuffers at any given moment.
  1923. materials->HandleDeviceLost();
  1924. if (!IsActiveApp())
  1925. {
  1926. Sleep(50);
  1927. }
  1928. #ifdef VPROF_HAMMER
  1929. g_VProfCurrentProfile.MarkFrame();
  1930. #endif
  1931. HammerVGui()->Simulate();
  1932. if ( CMapDoc::GetActiveMapDoc() && !IsClosing() || m_bForceRenderNextFrame )
  1933. HandleLightingPreview();
  1934. // never render without document or when closing down
  1935. // usually only render when active, but not compiling a map unless forced
  1936. if ( CMapDoc::GetActiveMapDoc() && !IsClosing() &&
  1937. ( ( !IsRunningCommands() && IsActiveApp() ) || m_bForceRenderNextFrame ) &&
  1938. CMapDoc::GetActiveMapDoc()->HasInitialUpdate() )
  1939. {
  1940. CMapDoc *pMapDoc = CMapDoc::GetActiveMapDoc();
  1941. // get the time
  1942. pMapDoc->UpdateCurrentTime();
  1943. // run any animation
  1944. pMapDoc->UpdateAnimation();
  1945. // redraw the 3d views
  1946. pMapDoc->RenderAllViews();
  1947. // update the grid nav
  1948. CGridNav *pGridNav = pMapDoc->GetGridNav();
  1949. if ( pGridNav && pGridNav->IsEnabled() && pGridNav->IsPreviewActive() )
  1950. {
  1951. CMapView3D *pView = pMapDoc->GetFirst3DView();
  1952. if ( pView )
  1953. {
  1954. CCamera *pCam = pView->GetCamera();
  1955. Assert( pCam );
  1956. Vector vViewPos, vViewDir;
  1957. pCam->GetViewPoint( vViewPos );
  1958. pCam->GetViewForward( vViewDir );
  1959. pGridNav->Update( pMapDoc, vViewPos, vViewDir );
  1960. }
  1961. }
  1962. }
  1963. // No matter what, we want to keep caching in materials...
  1964. if ( IsActiveApp() )
  1965. {
  1966. g_Textures.LazyLoadTextures();
  1967. }
  1968. m_bForceRenderNextFrame = false;
  1969. }
  1970. //-----------------------------------------------------------------------------
  1971. // Purpose: Overloaded Run so that we can control the framerate for realtime
  1972. // rendering in the 3D view.
  1973. // Output : As MFC CWinApp::Run.
  1974. //-----------------------------------------------------------------------------
  1975. int CHammer::Run(void)
  1976. {
  1977. Assert(0);
  1978. return 0;
  1979. }
  1980. //-----------------------------------------------------------------------------
  1981. // Purpose: Returns true if the editor is the active app, false if not.
  1982. //-----------------------------------------------------------------------------
  1983. bool CHammer::IsActiveApp(void)
  1984. {
  1985. return m_bActiveApp;
  1986. }
  1987. //-----------------------------------------------------------------------------
  1988. // Purpose: Called from CMainFrame::OnSysCommand, this informs the app when it
  1989. // is minimized and unminimized. This allows us to stop rendering the
  1990. // 3D view when we are minimized.
  1991. // Input : bMinimized - TRUE when minmized, FALSE otherwise.
  1992. //-----------------------------------------------------------------------------
  1993. void CHammer::OnActivateApp(bool bActive)
  1994. {
  1995. // static int nCount = 0;
  1996. // if (bActive)
  1997. // DBG("ON %d\n", nCount);
  1998. // else
  1999. // DBG("OFF %d\n", nCount);
  2000. // nCount++;
  2001. m_bActiveApp = bActive;
  2002. }
  2003. //-----------------------------------------------------------------------------
  2004. // Purpose: Called from the shell to relinquish our video memory in favor of the
  2005. // engine. This is called by the engine when it starts up.
  2006. //-----------------------------------------------------------------------------
  2007. void CHammer::ReleaseVideoMemory()
  2008. {
  2009. POSITION pos = GetFirstDocTemplatePosition();
  2010. while (pos)
  2011. {
  2012. CDocTemplate* pTemplate = (CDocTemplate*)GetNextDocTemplate(pos);
  2013. POSITION pos2 = pTemplate->GetFirstDocPosition();
  2014. while (pos2)
  2015. {
  2016. CDocument * pDocument;
  2017. if ((pDocument=pTemplate->GetNextDoc(pos2)) != NULL)
  2018. {
  2019. static_cast<CMapDoc*>(pDocument)->ReleaseVideoMemory();
  2020. }
  2021. }
  2022. }
  2023. }
  2024. void CHammer::SuppressVideoAllocation( bool bSuppress )
  2025. {
  2026. m_SuppressVideoAllocation = bSuppress;
  2027. }
  2028. bool CHammer::CanAllocateVideo() const
  2029. {
  2030. return !m_SuppressVideoAllocation;
  2031. }
  2032. //-------------------------------------------------------------------------------
  2033. // Purpose: Runs through the autosave directory and fills the autosave map.
  2034. // Also sets the total amount of space used by the directory.
  2035. // Input : pFileMap - CUtlMap that will hold the list of files in the dir
  2036. // pdwTotalDirSize - pointer to the DWORD that will hold directory size
  2037. // pstrMapTitle - the name of the current map to be saved
  2038. // Output : returns an int containing the next number to use for the autosave
  2039. //-------------------------------------------------------------------------------
  2040. int CHammer::GetNextAutosaveNumber( CUtlMap<FILETIME, WIN32_FIND_DATA, int> *pFileMap, DWORD *pdwTotalDirSize, const CString *pstrMapTitle ) const
  2041. {
  2042. FILETIME oldestAutosaveTime;
  2043. oldestAutosaveTime.dwHighDateTime = 0;
  2044. oldestAutosaveTime.dwLowDateTime = 0;
  2045. char szRootDir[MAX_PATH];
  2046. APP()->GetDirectory(DIR_AUTOSAVE, szRootDir);
  2047. CString strAutosaveDirectory( szRootDir );
  2048. int nNumberActualAutosaves = 0;
  2049. int nCurrentAutosaveNumber = 1;
  2050. int nOldestAutosaveNumber = 1;
  2051. int nExpectedNextAutosaveNumber = 1;
  2052. int nLastHole = 0;
  2053. int nMaxAutosavesPerMap = Options.general.iMaxAutosavesPerMap;
  2054. WIN32_FIND_DATA fileData;
  2055. HANDLE hFile;
  2056. DWORD dwTotalAutosaveDirectorySize = 0;
  2057. hFile = FindFirstFile( strAutosaveDirectory + "*.vmf_autosave", &fileData );
  2058. if ( hFile != INVALID_HANDLE_VALUE )
  2059. {
  2060. //go through and for each file check to see if it is an autosave for this map; also keep track of total file size
  2061. //for directory.
  2062. while( GetLastError() != ERROR_NO_MORE_FILES && hFile != INVALID_HANDLE_VALUE )
  2063. {
  2064. (*pFileMap).Insert( fileData.ftLastAccessTime, fileData );
  2065. DWORD dwFileSize = fileData.nFileSizeLow;
  2066. dwTotalAutosaveDirectorySize += dwFileSize;
  2067. FILETIME fileAccessTime = fileData.ftLastAccessTime;
  2068. CString currentFilename( fileData.cFileName );
  2069. //every autosave file ends in three digits; this code separates the name from the digits
  2070. CString strMapName = currentFilename.Left( currentFilename.GetLength() - 17 );
  2071. CString strCurrentNumber = currentFilename.Mid( currentFilename.GetLength() - 16, 3 );
  2072. int nMapNumber = atoi( (char *)strCurrentNumber.GetBuffer() );
  2073. if ( strMapName.CompareNoCase( (*pstrMapTitle) ) == 0 )
  2074. {
  2075. //keep track of real number of autosaves with map name; deals with instance where older maps get deleted
  2076. //and create sequence holes in autosave map names.
  2077. nNumberActualAutosaves++;
  2078. if ( oldestAutosaveTime.dwLowDateTime == 0 )
  2079. {
  2080. //the first file is automatically the oldest
  2081. oldestAutosaveTime = fileAccessTime;
  2082. }
  2083. if ( nMapNumber != nExpectedNextAutosaveNumber )
  2084. {
  2085. //the current map number is different than what was expected
  2086. //there is a hole in the sequence
  2087. nLastHole = nMapNumber;
  2088. }
  2089. nExpectedNextAutosaveNumber = nMapNumber + 1;
  2090. if ( nExpectedNextAutosaveNumber > 999 )
  2091. {
  2092. nExpectedNextAutosaveNumber = 1;
  2093. }
  2094. if ( CompareFileTime( &fileAccessTime, &oldestAutosaveTime ) == -1 )
  2095. {
  2096. //this file is older than previous oldest file
  2097. oldestAutosaveTime = fileAccessTime;
  2098. nOldestAutosaveNumber = nMapNumber;
  2099. }
  2100. }
  2101. FindNextFile(hFile, &fileData);
  2102. }
  2103. FindClose(hFile);
  2104. }
  2105. if ( nNumberActualAutosaves < nMaxAutosavesPerMap )
  2106. {
  2107. //there are less autosaves than wanted for the map; use the larger
  2108. //of the next expected or the last found hole as the number.
  2109. nCurrentAutosaveNumber = max( nExpectedNextAutosaveNumber, nLastHole );
  2110. }
  2111. else
  2112. {
  2113. //there are no holes, use the oldest.
  2114. nCurrentAutosaveNumber = nOldestAutosaveNumber;
  2115. }
  2116. *pdwTotalDirSize = dwTotalAutosaveDirectorySize;
  2117. return nCurrentAutosaveNumber;
  2118. }
  2119. static bool LessFunc( const FILETIME &lhs, const FILETIME &rhs )
  2120. {
  2121. return CompareFileTime(&lhs, &rhs) < 0;
  2122. }
  2123. //-----------------------------------------------------------------------------
  2124. // Purpose: This is called when the autosave timer goes off. It checks to
  2125. // make sure the document has changed and handles deletion of old
  2126. // files when the total directory size is too big
  2127. //-----------------------------------------------------------------------------
  2128. void CHammer::Autosave( void )
  2129. {
  2130. if ( !Options.general.bEnableAutosave )
  2131. {
  2132. return;
  2133. }
  2134. if ( VerifyAutosaveDirectory() != true )
  2135. {
  2136. Options.general.bEnableAutosave = false;
  2137. return;
  2138. }
  2139. CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
  2140. //value from options is in megs
  2141. DWORD dwMaxAutosaveSpace = Options.general.iMaxAutosaveSpace * 1024 * 1024;
  2142. CUtlMap<FILETIME, WIN32_FIND_DATA, int> autosaveFiles;
  2143. autosaveFiles.SetLessFunc( LessFunc );
  2144. if ( pDoc && pDoc->NeedsAutosave() )
  2145. {
  2146. char szRootDir[MAX_PATH];
  2147. APP()->GetDirectory(DIR_AUTOSAVE, szRootDir);
  2148. CString strAutosaveDirectory( szRootDir );
  2149. //expand the path if $SteamUserDir etc are used for SDK users
  2150. if ( CGameConfigManager::IsSDKDeployment() )
  2151. {
  2152. EditorUtil_ConvertPath(strAutosaveDirectory, true);
  2153. }
  2154. CString strExtension = ".vmf";
  2155. //this will hold the name of the map w/o leading directory info or file extension
  2156. CString strMapTitle;
  2157. //full path of map file
  2158. CString strMapFilename = pDoc->GetPathName();
  2159. DWORD dwTotalAutosaveDirectorySize = 0;
  2160. int nCurrentAutosaveNumber = 0;
  2161. // the map hasn't been saved before and doesn't have a filename; using default: 'autosave'
  2162. if ( strMapFilename.IsEmpty() )
  2163. {
  2164. strMapTitle = "autosave";
  2165. }
  2166. // the map already has a filename
  2167. else
  2168. {
  2169. int nFilenameBeginOffset = strMapFilename.ReverseFind( '\\' ) + 1;
  2170. int nFilenameEndOffset = strMapFilename.Find( '.' );
  2171. //get the filename of the map, between the leading '\' and the '.'
  2172. strMapTitle = strMapFilename.Mid( nFilenameBeginOffset, nFilenameEndOffset - nFilenameBeginOffset );
  2173. }
  2174. nCurrentAutosaveNumber = GetNextAutosaveNumber( &autosaveFiles, &dwTotalAutosaveDirectorySize, &strMapTitle );
  2175. //creating the proper suffix for the autosave file
  2176. char szNumberChars[4];
  2177. CString strAutosaveString = itoa( nCurrentAutosaveNumber, szNumberChars, 10 );
  2178. CString strAutosaveNumber = "000";
  2179. strAutosaveNumber += strAutosaveString;
  2180. strAutosaveNumber = strAutosaveNumber.Right( 3 );
  2181. strAutosaveNumber = "_" + strAutosaveNumber;
  2182. CString strSaveName = strAutosaveDirectory + strMapTitle + strAutosaveNumber + strExtension + "_autosave";
  2183. pDoc->SaveVMF( (char *)strSaveName.GetBuffer(), SAVEFLAGS_AUTOSAVE );
  2184. //don't autosave again unless they make changes
  2185. pDoc->SetAutosaveFlag( FALSE );
  2186. //if there is too much space used for autosaves, delete the oldest file until the size is acceptable
  2187. while( dwTotalAutosaveDirectorySize > dwMaxAutosaveSpace )
  2188. {
  2189. int nFirstElementIndex = autosaveFiles.FirstInorder();
  2190. if ( !autosaveFiles.IsValidIndex( nFirstElementIndex ) )
  2191. {
  2192. Assert( false );
  2193. break;
  2194. }
  2195. WIN32_FIND_DATA fileData = autosaveFiles.Element( nFirstElementIndex );
  2196. DWORD dwOldestFileSize = fileData.nFileSizeLow;
  2197. char filename[MAX_PATH];
  2198. strcpy( filename, fileData.cFileName );
  2199. DeleteFile( strAutosaveDirectory + filename );
  2200. dwTotalAutosaveDirectorySize -= dwOldestFileSize;
  2201. autosaveFiles.RemoveAt( nFirstElementIndex );
  2202. }
  2203. autosaveFiles.RemoveAll();
  2204. }
  2205. }
  2206. //-----------------------------------------------------------------------------
  2207. // Purpose: Verifies that the autosave directory exists and attempts to create it if
  2208. // it doesn't. Also returns various failure errors.
  2209. // This function is now called at two different times: immediately after a new
  2210. // directory is entered in the options screen and during every autosave call.
  2211. // If called with a directory, the input directory is checked for correctness.
  2212. // Otherwise, the system directory DIR_AUTOSAVE is checked
  2213. //-----------------------------------------------------------------------------
  2214. bool CHammer::VerifyAutosaveDirectory( char *szAutosaveDirectory ) const
  2215. {
  2216. HANDLE hDir;
  2217. HANDLE hTestFile;
  2218. char szRootDir[MAX_PATH];
  2219. if ( szAutosaveDirectory )
  2220. {
  2221. strcpy( szRootDir, szAutosaveDirectory );
  2222. EnsureTrailingBackslash( szRootDir );
  2223. }
  2224. else
  2225. {
  2226. APP()->GetDirectory(DIR_AUTOSAVE, szRootDir);
  2227. }
  2228. if ( szRootDir[0] == 0 )
  2229. {
  2230. AfxMessageBox( "No autosave directory has been selected.\nThe autosave feature will be disabled until a directory is entered.", MB_OK );
  2231. return false;
  2232. }
  2233. CString strAutosaveDirectory( szRootDir );
  2234. if ( CGameConfigManager::IsSDKDeployment() )
  2235. {
  2236. EditorUtil_ConvertPath(strAutosaveDirectory, true);
  2237. if ( ( strAutosaveDirectory[1] != ':' ) || ( strAutosaveDirectory[2] != '\\' ) )
  2238. {
  2239. AfxMessageBox( "The current autosave directory does not have an absolute path.\nThe autosave feature will be disabled until a new directory is entered.", MB_OK );
  2240. return false;
  2241. }
  2242. }
  2243. else
  2244. {
  2245. if ( ( szRootDir[1] != ':' ) || ( szRootDir[2] != '\\' ) )
  2246. {
  2247. AfxMessageBox( "The current autosave directory does not have an absolute path.\nThe autosave feature will be disabled until a new directory is entered.", MB_OK );
  2248. return false;
  2249. }
  2250. }
  2251. hDir = CreateFile (
  2252. strAutosaveDirectory,
  2253. GENERIC_READ,
  2254. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  2255. NULL,
  2256. OPEN_EXISTING,
  2257. FILE_FLAG_BACKUP_SEMANTICS,
  2258. NULL
  2259. );
  2260. if ( hDir == INVALID_HANDLE_VALUE )
  2261. {
  2262. bool bDirResult = CreateDirectory( strAutosaveDirectory, NULL ) ? true : false;
  2263. if ( !bDirResult )
  2264. {
  2265. AfxMessageBox( "The current autosave directory does not exist and could not be created. \nThe autosave feature will be disabled until a new directory is entered.", MB_OK );
  2266. return false;
  2267. }
  2268. }
  2269. else
  2270. {
  2271. CloseHandle( hDir );
  2272. hTestFile = CreateFile( strAutosaveDirectory + "test.txt",
  2273. GENERIC_READ,
  2274. FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
  2275. NULL,
  2276. CREATE_NEW,
  2277. FILE_FLAG_BACKUP_SEMANTICS,
  2278. NULL
  2279. );
  2280. if ( hTestFile == INVALID_HANDLE_VALUE )
  2281. {
  2282. if ( GetLastError() == ERROR_ACCESS_DENIED )
  2283. {
  2284. AfxMessageBox( "The autosave directory is marked as read only. Please remove the read only attribute or select a new directory in Tools->Options->General.\nThe autosave feature will be disabled.", MB_OK );
  2285. return false;
  2286. }
  2287. else
  2288. {
  2289. AfxMessageBox( "There is a problem with the autosave directory. Please select a new directory in Tools->Options->General.\nThe autosave feature will be disabled.", MB_OK );
  2290. return false;
  2291. }
  2292. }
  2293. CloseHandle( hTestFile );
  2294. DeleteFile( strAutosaveDirectory + "test.txt" );
  2295. }
  2296. return true;
  2297. }
  2298. //-----------------------------------------------------------------------------
  2299. // Purpose: Called when Hammer starts after a crash. It loads the newest
  2300. // autosave file after Hammer has initialized.
  2301. //-----------------------------------------------------------------------------
  2302. void CHammer::LoadLastGoodSave( void )
  2303. {
  2304. CString strLastGoodSave = APP()->GetProfileString("General", "Last Good Save", "");
  2305. char szMapDir[MAX_PATH];
  2306. strcpy(szMapDir, g_pGameConfig->szMapDir);
  2307. CDocument *pCurrentDoc;
  2308. if ( !strLastGoodSave.IsEmpty() )
  2309. {
  2310. pCurrentDoc = APP()->OpenDocumentFile( strLastGoodSave );
  2311. if ( !pCurrentDoc )
  2312. {
  2313. AfxMessageBox( "There was an error loading the last saved file.", MB_OK );
  2314. return;
  2315. }
  2316. char szAutoSaveDir[MAX_PATH];
  2317. APP()->GetDirectory(DIR_AUTOSAVE, szAutoSaveDir);
  2318. if ( !((CMapDoc *)pCurrentDoc)->IsAutosave() && Q_stristr( pCurrentDoc->GetPathName(), szAutoSaveDir ) )
  2319. {
  2320. //This handles the case where someone recovers from a crash and tries to load an autosave file that doesn't have the new autosave chunk in it
  2321. //It assumes the file should go into the gameConfig map directory
  2322. char szRenameMessage[MAX_PATH+MAX_PATH+256];
  2323. char szLastSaveCopy[MAX_PATH];
  2324. Q_strcpy( szLastSaveCopy, strLastGoodSave );
  2325. char *pszFileName = Q_strrchr( strLastGoodSave, '\\') + 1;
  2326. char *pszFileNameEnd = Q_strrchr( strLastGoodSave, '_');
  2327. if ( !pszFileNameEnd )
  2328. {
  2329. pszFileNameEnd = Q_strrchr( strLastGoodSave, '.');
  2330. }
  2331. strcpy( pszFileNameEnd, ".vmf" );
  2332. CString newMapPath( szMapDir );
  2333. newMapPath.Append( "\\" );
  2334. newMapPath.Append( pszFileName );
  2335. sprintf( szRenameMessage, "The last saved map was found in the autosave directory.\nWould you like to rename it from \"%s\" to \"%s\"?\nNOTE: This will not save the file with the new name; it will only rename it.", szLastSaveCopy, newMapPath );
  2336. if ( AfxMessageBox( szRenameMessage, MB_YESNO ) == IDYES )
  2337. {
  2338. pCurrentDoc->SetPathName( newMapPath );
  2339. }
  2340. }
  2341. }
  2342. }
  2343. //-----------------------------------------------------------------------------
  2344. // Purpose: Called from the General Options dialog when the autosave timer or
  2345. // directory values have changed.
  2346. //-----------------------------------------------------------------------------
  2347. void CHammer::ResetAutosaveTimer()
  2348. {
  2349. Options.general.bEnableAutosave = true;
  2350. CMainFrame *pMainWnd = ::GetMainWnd();
  2351. if ( pMainWnd )
  2352. {
  2353. pMainWnd->ResetAutosaveTimer();
  2354. }
  2355. }
  2356. //-----------------------------------------------------------------------------
  2357. bool UTIL_IsDedicatedServer( void )
  2358. {
  2359. return false;
  2360. }