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.

547 lines
17 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Handles parsing and routing of shell commands to their handlers.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "MainFrm.h"
  9. #include "MapDoc.h"
  10. #include "MapEntity.h"
  11. #include "Shell.h"
  12. #include "hammer.h"
  13. #include "filesystem_helpers.h"
  14. // memdbgon must be the last include file in a .cpp file!!!
  15. #include <tier0/memdbgon.h>
  16. //-----------------------------------------------------------------------------
  17. // Shell command handler function pointer.
  18. //-----------------------------------------------------------------------------
  19. typedef bool (CShell::*ShellHandlerFunc_t)(const char *pszCommand, const char *pszArguments);
  20. //-----------------------------------------------------------------------------
  21. // Dispatch table entry.
  22. //-----------------------------------------------------------------------------
  23. struct ShellDispatchTable_t
  24. {
  25. const char *pszCommand; // Name of command associated with this entry.
  26. ShellHandlerFunc_t pfnHandler; // Function handler for the command.
  27. };
  28. //-----------------------------------------------------------------------------
  29. // Dispatch table for shell commands.
  30. //-----------------------------------------------------------------------------
  31. ShellDispatchTable_t CShell::m_DispatchTable[] =
  32. {
  33. { "session_begin", &CShell::BeginSession },
  34. { "session_end", &CShell::EndSession },
  35. { "entity_create", &CShell::EntityCreate },
  36. { "entity_delete", &CShell::EntityDelete },
  37. { "entity_set_keyvalue", &CShell::EntitySetKeyValue },
  38. { "entity_rotate_incremental", &CShell::EntityRotateIncremental },
  39. { "map_check_version", &CShell::CheckMapVersion },
  40. { "node_create", &CShell::NodeCreate },
  41. { "node_delete", &CShell::NodeDelete },
  42. { "nodelink_create", &CShell::NodeLinkCreate },
  43. { "nodelink_delete", &CShell::NodeLinkDelete },
  44. { "release_video_memory", &CShell::ReleaseVideoMemory },
  45. { "grab_video_memory", &CShell::GrabVideoMemory },
  46. };
  47. //-----------------------------------------------------------------------------
  48. // Purpose: Constructor.
  49. //-----------------------------------------------------------------------------
  50. CShell::CShell(void)
  51. {
  52. m_pDoc = NULL;
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Destructor.
  56. //-----------------------------------------------------------------------------
  57. CShell::~CShell(void)
  58. {
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose: Initiates a shell editing session.
  62. // Input : pszCommand - Should be "session_begin".
  63. // pszArguments - Filename and file version in the engine.
  64. // Output : Returns true on success, false on failure.
  65. //-----------------------------------------------------------------------------
  66. bool CShell::BeginSession(const char *pszCommand, const char *pszArguments)
  67. {
  68. if ((m_pDoc != NULL) && !m_pDoc->IsShellSessionActive())
  69. {
  70. if (DoVersionCheck(pszArguments))
  71. {
  72. m_pDoc->BeginShellSession();
  73. return(true);
  74. }
  75. }
  76. return(false);
  77. }
  78. //-----------------------------------------------------------------------------
  79. // Purpose: Verifies that the map begine edited in the engine is the same name
  80. // and version as the active document. This prevents problems with
  81. // editing out of sync versions of the map via the engine.
  82. // Input : pszCommand - Should be "map_check_version".
  83. // pszArguments - Filename and file version in the engine.
  84. // Output : Returns true on success, false on failure.
  85. //-----------------------------------------------------------------------------
  86. bool CShell::CheckMapVersion(const char *pszCommand, const char *pszArguments)
  87. {
  88. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  89. {
  90. return(DoVersionCheck(pszArguments));
  91. }
  92. return(false);
  93. }
  94. //-----------------------------------------------------------------------------
  95. // Purpose: Verifies that the map being edited in the engine is the same name
  96. // and version as the active document. This prevents problems with
  97. // editing out of sync versions of the map via the engine.
  98. // Input : pszCommand -
  99. // pszArguments -
  100. // Output : Returns true on success, false on failure.
  101. //-----------------------------------------------------------------------------
  102. bool CShell::DoVersionCheck(const char *pszArguments)
  103. {
  104. if (m_pDoc != NULL)
  105. {
  106. char szEngineMapPath[MAX_PATH];
  107. int nEngineMapVersion;
  108. if (sscanf(pszArguments, "%s %d", szEngineMapPath, &nEngineMapVersion) == 2)
  109. {
  110. char szEngineMapName[MAX_PATH];
  111. _splitpath(szEngineMapPath, NULL, NULL, szEngineMapName, NULL);
  112. char szDocName[MAX_PATH];
  113. _splitpath(m_pDoc->GetPathName(), NULL, NULL, szDocName, NULL);
  114. int nDocVersion = m_pDoc->GetDocVersion();
  115. if (!stricmp(szDocName, szEngineMapName) && (nDocVersion == nEngineMapVersion))
  116. {
  117. return(true);
  118. }
  119. }
  120. }
  121. return(false);
  122. }
  123. //-----------------------------------------------------------------------------
  124. // Purpose: Verifies that the map begine edited in the engine is the same name
  125. // and version as the active document. This prevents problems with
  126. // editing out of sync versions of the map via the engine.
  127. // Input : pszCommand - Should be "session_end".
  128. // pszArguments - Filename and file version in the engine.
  129. // Output : Returns true on success, false on failure.
  130. //-----------------------------------------------------------------------------
  131. bool CShell::EndSession(const char *pszCommand, const char *pszArguments)
  132. {
  133. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  134. {
  135. m_pDoc->EndShellSession();
  136. return(true);
  137. }
  138. return(false);
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose: Creates an entity of a given class at a specified location.
  142. // Input : pszCommand - Should be "entity_create".
  143. // pszArguments - Class name of entity and x, y, z coordinate at which
  144. // to create it.
  145. // Output : Returns true on success, false on failure.
  146. //-----------------------------------------------------------------------------
  147. bool CShell::EntityCreate(const char *pszCommand, const char *pszArguments)
  148. {
  149. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  150. {
  151. float x;
  152. float y;
  153. float z;
  154. char szClassName[MAX_PATH];
  155. if (sscanf(pszArguments, "%s %f %f %f", szClassName, &x, &y, &z) == 4)
  156. {
  157. bool bCreated = (m_pDoc->CreateEntity(szClassName, x, y, z) != NULL);
  158. return(bCreated);
  159. }
  160. }
  161. return(false);
  162. }
  163. //-----------------------------------------------------------------------------
  164. // Purpose: Deletes an entity by class name and origin.
  165. // Input : pszCommand - Should be "entity_delete".
  166. // pszArguments - Class name of entity and x, y, z coordinates.
  167. // Output : Returns true on success, false on failure.
  168. //-----------------------------------------------------------------------------
  169. bool CShell::EntityDelete(const char *pszCommand, const char *pszArguments)
  170. {
  171. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  172. {
  173. float x;
  174. float y;
  175. float z;
  176. char szClassName[MAX_PATH];
  177. if (sscanf(pszArguments, "%s %f %f %f", szClassName, &x, &y, &z) == 4)
  178. {
  179. bool bDeleted = m_pDoc->DeleteEntity(szClassName, x, y, z);
  180. return(bDeleted);
  181. }
  182. }
  183. return(false);
  184. }
  185. static void RotateMapEntity( CMapEntity *pEntity, const QAngle &rotation )
  186. {
  187. Vector origin;
  188. pEntity->GetOrigin( origin );
  189. QAngle hammerRotate;
  190. hammerRotate.Init( rotation.z, -rotation.x, rotation.y );
  191. pEntity->TransRotate( origin, hammerRotate );
  192. }
  193. bool CShell::EntityRotateIncremental(const char *pszCommand, const char *pszArguments)
  194. {
  195. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  196. {
  197. const int NUM_ROTATE_INCREMENTAL_ARGS = 7;
  198. float x;
  199. float y;
  200. float z;
  201. QAngle rotation;
  202. char szArgs[NUM_ROTATE_INCREMENTAL_ARGS][512]; // classname, x, y, z, ax, ay, az
  203. char token[1024];
  204. const char *pBuffer = pszArguments;
  205. int arg = 0;
  206. while ( pBuffer && arg < NUM_ROTATE_INCREMENTAL_ARGS )
  207. {
  208. pBuffer = ParseFile( pBuffer, token, NULL );
  209. if ( pBuffer )
  210. {
  211. Q_strncpy( szArgs[arg], token, ARRAYSIZE(szArgs[arg]) );
  212. arg++;
  213. }
  214. }
  215. if ( arg == NUM_ROTATE_INCREMENTAL_ARGS )
  216. {
  217. x = atof(szArgs[1]);
  218. y = atof(szArgs[2]);
  219. z = atof(szArgs[3]);
  220. CMapEntity *pEntity = m_pDoc->FindEntity(szArgs[0], x, y, z);
  221. if (pEntity != NULL)
  222. {
  223. rotation.x = atof(szArgs[4]);
  224. rotation.y = atof(szArgs[5]);
  225. rotation.z = atof(szArgs[6]);
  226. RotateMapEntity( pEntity, rotation );
  227. return true;
  228. }
  229. }
  230. }
  231. return false;
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Purpose: Sets a keyvalue on an entity, searching by classname & origin
  235. // Input : pszCommand - Should be "entity_delete".
  236. // pszArguments - Class name of entity and x, y, z coordinates.
  237. // Output : Returns true on success, false on failure.
  238. //-----------------------------------------------------------------------------
  239. bool CShell::EntitySetKeyValue(const char *pszCommand, const char *pszArguments)
  240. {
  241. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  242. {
  243. const int NUM_KEY_VALUE_ARGS = 6;
  244. float x;
  245. float y;
  246. float z;
  247. char szArgs[NUM_KEY_VALUE_ARGS][512]; // classname, x, y, z, key, value
  248. char token[1024];
  249. const char *pBuffer = pszArguments;
  250. int arg = 0;
  251. while ( pBuffer && arg < NUM_KEY_VALUE_ARGS )
  252. {
  253. pBuffer = ParseFile( pBuffer, token, NULL );
  254. if ( pBuffer )
  255. {
  256. Q_strncpy( szArgs[arg], token, ARRAYSIZE(szArgs[arg]) );
  257. arg++;
  258. }
  259. }
  260. if ( arg == NUM_KEY_VALUE_ARGS )
  261. {
  262. x = atof(szArgs[1]);
  263. y = atof(szArgs[2]);
  264. z = atof(szArgs[3]);
  265. CMapEntity *pEntity = m_pDoc->FindEntity(szArgs[0], x, y, z);
  266. if (pEntity != NULL)
  267. {
  268. if ( !Q_stricmp( szArgs[4], "origin" ) )
  269. {
  270. Vector origin;
  271. sscanf(szArgs[5], "%f %f %f", &origin[0], &origin[1], &origin[2]);
  272. Vector oldOrigin;
  273. pEntity->GetOrigin( oldOrigin );
  274. pEntity->TransMove(origin - oldOrigin);
  275. }
  276. else if ( pEntity->IsSolidClass() && !Q_stricmp( szArgs[4], "angles" ) )
  277. {
  278. QAngle angles;
  279. sscanf(szArgs[5], "%f %f %f", &angles[0], &angles[1], &angles[2]);
  280. // build a relative transform from the previous state to the current state
  281. // NOTE: This only works once since solid classes destructively modify transform info (GetAngles always returns identity)
  282. // NOTE: Use rotateIncremental instead!
  283. QAngle oldAngles;
  284. pEntity->GetAngles( oldAngles );
  285. if ( oldAngles != angles )
  286. {
  287. QAngle xformAngles;
  288. RotationDelta( oldAngles, angles, &xformAngles );
  289. RotateMapEntity( pEntity, xformAngles );
  290. }
  291. }
  292. else
  293. {
  294. pEntity->SetKeyValue( szArgs[4], szArgs[5] );
  295. }
  296. return true;
  297. }
  298. }
  299. }
  300. return false;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // Purpose: Creates a navigation node of a given class at a specified location.
  304. // Input : pszCommand - Should be "node_create".
  305. // pszArguments - Class name of node to create, ID to assign it, and
  306. // x, y, z coordinate at which to create the node.
  307. // Output : Returns true on success, false on failure.
  308. //-----------------------------------------------------------------------------
  309. bool CShell::NodeCreate(const char *pszCommand, const char *pszArguments)
  310. {
  311. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  312. {
  313. float x;
  314. float y;
  315. float z;
  316. int nID;
  317. char szClassName[MAX_PATH];
  318. if (sscanf(pszArguments, "%s %d %f %f %f", szClassName, &nID, &x, &y, &z) == 5)
  319. {
  320. m_pDoc->SetNextNodeID(nID);
  321. m_pDoc->CreateEntity(szClassName, x, y, z);
  322. return(true);
  323. }
  324. }
  325. return(false);
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Purpose: Deletes a navigation node by ID.
  329. // Input : pszCommand - Should be "node_delete".
  330. // pszArguments - Unique node ID of node to delete.
  331. // Output : Returns true on success, false on failure.
  332. //-----------------------------------------------------------------------------
  333. bool CShell::NodeDelete(const char *pszCommand, const char *pszArguments)
  334. {
  335. bool bFound = false;
  336. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  337. {
  338. char szID[80];
  339. if (sscanf(pszArguments, "%s", szID) == 1)
  340. {
  341. CMapEntityList Found;
  342. if (m_pDoc->FindEntitiesByKeyValue(Found, "nodeid", szID, false))
  343. {
  344. FOR_EACH_OBJ( Found, pos )
  345. {
  346. CMapEntity *pEntity = Found.Element(pos);
  347. m_pDoc->DeleteObject(pEntity);
  348. bFound = true;
  349. }
  350. }
  351. }
  352. }
  353. return(bFound);
  354. }
  355. //-----------------------------------------------------------------------------
  356. // Purpose: Creates a navigation node of a given class at a specified location.
  357. // Input : pszCommand - Should be "nodelink_create".
  358. // pszArguments - Node ids of start and end nodes, space delimited.
  359. // Output : Returns true on success, false on failure.
  360. //-----------------------------------------------------------------------------
  361. bool CShell::NodeLinkCreate(const char *pszCommand, const char *pszArguments)
  362. {
  363. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  364. {
  365. char szIDStart[80];
  366. char szIDEnd[80];
  367. if (sscanf(pszArguments, "%s %s", szIDStart, szIDEnd) == 2)
  368. {
  369. //
  370. // It doesn't matter where we place it because it will move to the midpoint of the
  371. // start and end entities.
  372. //
  373. CMapEntity *pEntity = m_pDoc->CreateEntity("info_node_link", 0, 0, 0);
  374. if (pEntity != NULL)
  375. {
  376. pEntity->SetKeyValue("startnode", szIDStart);
  377. pEntity->SetKeyValue("endnode", szIDEnd);
  378. return(true);
  379. }
  380. }
  381. }
  382. return(false);
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: Deletes a navigation node by class name and ID.
  386. // Input : pszCommand - Should be "node_delete".
  387. // pszArguments - Class name of node and unique node ID.
  388. // Output : Returns true on success, false on failure.
  389. //-----------------------------------------------------------------------------
  390. bool CShell::NodeLinkDelete(const char *pszCommand, const char *pszArguments)
  391. {
  392. bool bFound = false;
  393. if ((m_pDoc != NULL) && m_pDoc->IsShellSessionActive())
  394. {
  395. char szIDStart[80];
  396. char szIDEnd[80];
  397. if (sscanf(pszArguments, "%s %s", szIDStart, szIDEnd) == 2)
  398. {
  399. //
  400. // Look for info_node_link entities with the appropriate start/end keys.
  401. //
  402. CMapEntityList Found;
  403. if (m_pDoc->FindEntitiesByClassName(Found, "info_node_link", false))
  404. {
  405. FOR_EACH_OBJ( Found, pos )
  406. {
  407. CMapEntity *pEntity = Found.Element(pos);
  408. const char *pszNode1 = pEntity->GetKeyValue("startnode");
  409. const char *pszNode2 = pEntity->GetKeyValue("endnode");
  410. if ((pszNode1 != NULL) && (pszNode2 != NULL))
  411. {
  412. if (((!stricmp(pszNode1, szIDStart)) && (!stricmp(pszNode2, szIDEnd))) ||
  413. ((!stricmp(pszNode1, szIDEnd)) && (!stricmp(pszNode2, szIDStart))))
  414. {
  415. m_pDoc->DeleteObject(pEntity);
  416. bFound = true;
  417. }
  418. }
  419. }
  420. }
  421. }
  422. }
  423. return(bFound);
  424. }
  425. //-----------------------------------------------------------------------------
  426. // Purpose: Releases all video memory
  427. // Input : pszCommand - Should be "release_video_memory".
  428. // pszArguments - None.
  429. // Output : Returns true on success, false on failure.
  430. //-----------------------------------------------------------------------------
  431. bool CShell::ReleaseVideoMemory(const char *pszCommand, const char *pszArguments)
  432. {
  433. APP()->ReleaseVideoMemory();
  434. APP()->SuppressVideoAllocation(true);
  435. return(true);
  436. }
  437. //-----------------------------------------------------------------------------
  438. // Purpose: Indicates it's safe to grab video memory
  439. // Input : pszCommand - Should be "grab_video_memory".
  440. // pszArguments - None.
  441. // Output : Returns true on success, false on failure.
  442. //-----------------------------------------------------------------------------
  443. bool CShell::GrabVideoMemory(const char *pszCommand, const char *pszArguments)
  444. {
  445. APP()->SuppressVideoAllocation(false);
  446. return(true);
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: Attempts to fund a command in the dispatch table, then routes the
  450. // command and its arguments to the handler, if found.
  451. // Input : pszCommand - Command and arguments.
  452. // Output : Returns true on success, false on failure.
  453. //-----------------------------------------------------------------------------
  454. bool CShell::RunCommand(const char *pszCommand)
  455. {
  456. for (int nCommand = 0; nCommand < sizeof(m_DispatchTable) / sizeof(m_DispatchTable[0]); nCommand++)
  457. {
  458. int nCommandLen = strlen(m_DispatchTable[nCommand].pszCommand);
  459. if (!_strnicmp(pszCommand, m_DispatchTable[nCommand].pszCommand, nCommandLen))
  460. {
  461. return((this->*m_DispatchTable[nCommand].pfnHandler)(m_DispatchTable[nCommand].pszCommand, &pszCommand[nCommandLen]));
  462. }
  463. }
  464. return(false);
  465. }
  466. //-----------------------------------------------------------------------------
  467. // Purpose: Sets the map document that this shell should operate on.
  468. // Input : pDoc - Pointer to document.
  469. //-----------------------------------------------------------------------------
  470. void CShell::SetDocument(CMapDoc *pDoc)
  471. {
  472. m_pDoc = pDoc;
  473. }