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.

1085 lines
28 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ====
  2. //
  3. //=============================================================================
  4. #ifndef POSIX
  5. #include <windows.h>
  6. #include <tier0/dbg.h>
  7. #include <io.h>
  8. #endif
  9. #include "worldsize.h"
  10. #include "fgdlib/gamedata.h"
  11. #include "fgdlib/helperinfo.h"
  12. #include "keyvalues.h"
  13. #include "filesystem_tools.h"
  14. #include "tier1/strtools.h"
  15. #include "utlmap.h"
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. #pragma warning(disable:4244)
  19. const int MAX_ERRORS = 5;
  20. static GameDataMessageFunc_t g_pMsgFunc = NULL;
  21. //-----------------------------------------------------------------------------
  22. // Sets the function used for emitting error messages while loading gamedata files.
  23. //-----------------------------------------------------------------------------
  24. void GDSetMessageFunc(GameDataMessageFunc_t pFunc)
  25. {
  26. g_pMsgFunc = pFunc;
  27. }
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Fetches the next token from the file.
  30. // Input : tr -
  31. // ppszStore - Destination buffer, one of the following:
  32. // pointer to NULL - token will be placed in an allocated buffer
  33. // pointer to non-NULL buffer - token will be placed in buffer
  34. // ttexpecting -
  35. // pszExpecting -
  36. // Output :
  37. //-----------------------------------------------------------------------------
  38. static bool DoGetToken(TokenReader &tr, char **ppszStore, int nSize, trtoken_t ttexpecting, const char *pszExpecting)
  39. {
  40. trtoken_t ttype;
  41. if (*ppszStore != NULL)
  42. {
  43. // Reads the token into the given buffer.
  44. ttype = tr.NextToken(*ppszStore, nSize);
  45. }
  46. else
  47. {
  48. // Allocates a buffer to hold the token.
  49. ttype = tr.NextTokenDynamic(ppszStore);
  50. }
  51. if (ttype == TOKENSTRINGTOOLONG)
  52. {
  53. GDError(tr, "unterminated string or string too long");
  54. return false;
  55. }
  56. //
  57. // Check for a bad token type.
  58. //
  59. char *pszStore = *ppszStore;
  60. bool bBadTokenType = false;
  61. if ((ttype != ttexpecting) && (ttexpecting != TOKENNONE))
  62. {
  63. //
  64. // If we were expecting a string and got an integer, don't worry about it.
  65. // We can translate from integer to string.
  66. //
  67. if (!((ttexpecting == STRING) && (ttype == INTEGER)))
  68. {
  69. bBadTokenType = true;
  70. }
  71. }
  72. if (bBadTokenType && (pszExpecting == NULL))
  73. {
  74. //
  75. // We didn't get the expected token type but no expected
  76. // string was specified.
  77. //
  78. char *pszTokenName;
  79. switch (ttexpecting)
  80. {
  81. case IDENT:
  82. {
  83. pszTokenName = "identifier";
  84. break;
  85. }
  86. case INTEGER:
  87. {
  88. pszTokenName = "integer";
  89. break;
  90. }
  91. case STRING:
  92. {
  93. pszTokenName = "string";
  94. break;
  95. }
  96. case OPERATOR:
  97. default:
  98. {
  99. pszTokenName = "symbol";
  100. break;
  101. }
  102. }
  103. GDError(tr, "expecting %s", pszTokenName);
  104. return false;
  105. }
  106. else if (bBadTokenType || ((pszExpecting != NULL) && !IsToken(pszStore, pszExpecting)))
  107. {
  108. //
  109. // An expected string was specified, and we got either the wrong type or
  110. // the right type but the wrong string,
  111. //
  112. GDError(tr, "expecting '%s', but found '%s'", pszExpecting, pszStore);
  113. return false;
  114. }
  115. return true;
  116. }
  117. //-----------------------------------------------------------------------------
  118. // Purpose:
  119. // Input : tr -
  120. // error -
  121. // Output :
  122. //-----------------------------------------------------------------------------
  123. bool GDError(TokenReader &tr, char *error, ...)
  124. {
  125. char szBuf[128];
  126. va_list vl;
  127. va_start(vl, error);
  128. vsprintf(szBuf, error, vl);
  129. va_end(vl);
  130. if (g_pMsgFunc)
  131. {
  132. // HACK: should use an enumeration for error level
  133. g_pMsgFunc(1, tr.Error(szBuf));
  134. }
  135. if (tr.GetErrorCount() >= MAX_ERRORS)
  136. {
  137. if (g_pMsgFunc)
  138. {
  139. // HACK: should use an enumeration for error level
  140. g_pMsgFunc(1, " - too many errors; aborting.");
  141. }
  142. return false;
  143. }
  144. return true;
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Purpose: Fetches the next token from the file.
  148. // Input : tr - The token reader object with which to fetch the token.
  149. // pszStore - Buffer in which to place the token, NULL to discard the token.
  150. // ttexpecting - The token type that we are expecting. If this is not TOKENNONE
  151. // and token type read is different, the operation will fail.
  152. // pszExpecting - The token string that we are expecting. If this string
  153. // is not NULL and the token string read is different, the operation will fail.
  154. // Output : Returns TRUE if the operation succeeded, FALSE if there was an error.
  155. // If there was an error, the error will be reported in the message window.
  156. //-----------------------------------------------------------------------------
  157. bool GDGetToken(TokenReader &tr, char *pszStore, int nSize, trtoken_t ttexpecting, const char *pszExpecting)
  158. {
  159. Assert(pszStore != NULL);
  160. if (pszStore != NULL)
  161. {
  162. return DoGetToken(tr, &pszStore, nSize, ttexpecting, pszExpecting);
  163. }
  164. return false;
  165. }
  166. //-----------------------------------------------------------------------------
  167. // Purpose: Fetches the next token from the file.
  168. // Input : tr - The token reader object with which to fetch the token.
  169. // pszStore - Buffer in which to place the token, NULL to discard the token.
  170. // ttexpecting - The token type that we are expecting. If this is not TOKENNONE
  171. // and token type read is different, the operation will fail.
  172. // pszExpecting - The token string that we are expecting. If this string
  173. // is not NULL and the token string read is different, the operation will fail.
  174. // Output : Returns TRUE if the operation succeeded, FALSE if there was an error.
  175. // If there was an error, the error will be reported in the message window.
  176. //-----------------------------------------------------------------------------
  177. bool GDSkipToken(TokenReader &tr, trtoken_t ttexpecting, const char *pszExpecting)
  178. {
  179. //
  180. // Read the next token into a buffer and discard it.
  181. //
  182. char szDiscardBuf[MAX_TOKEN];
  183. char *pszDiscardBuf = szDiscardBuf;
  184. return DoGetToken(tr, &pszDiscardBuf, sizeof(szDiscardBuf), ttexpecting, pszExpecting);
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: Fetches the next token from the file, allocating a buffer exactly
  188. // large enough to hold the token.
  189. // Input : tr -
  190. // ppszStore -
  191. // ttexpecting -
  192. // pszExpecting -
  193. // Output :
  194. //-----------------------------------------------------------------------------
  195. bool GDGetTokenDynamic(TokenReader &tr, char **ppszStore, trtoken_t ttexpecting, const char *pszExpecting)
  196. {
  197. if (ppszStore == NULL)
  198. {
  199. return false;
  200. }
  201. *ppszStore = NULL;
  202. return DoGetToken(tr, ppszStore, -1, ttexpecting, pszExpecting);
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose: Constructor.
  206. //-----------------------------------------------------------------------------
  207. GameData::GameData(void) :
  208. m_NodeRemap( DefLessFunc( int ) )
  209. {
  210. m_nMaxMapCoord = 8192;
  211. m_nMinMapCoord = -8192;
  212. m_InstanceClass = NULL;
  213. m_bGridNavActive = false;
  214. m_nGridNavEdgeSize = 0;
  215. m_nGridNavOffsetX = 0;
  216. m_nGridNavOffsetY = 0;
  217. m_nTraceHeight = 0;
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose: Destructor.
  221. //-----------------------------------------------------------------------------
  222. GameData::~GameData(void)
  223. {
  224. ClearData();
  225. }
  226. //-----------------------------------------------------------------------------
  227. // Purpose:
  228. //-----------------------------------------------------------------------------
  229. void GameData::ClearData(void)
  230. {
  231. // delete classes.
  232. int nCount = m_Classes.Count();
  233. for (int i = 0; i < nCount; i++)
  234. {
  235. GDclass *pm = m_Classes.Element(i);
  236. delete pm;
  237. }
  238. m_Classes.RemoveAll();
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Loads a gamedata (FGD) file into this object.
  242. // Input : pszFilename -
  243. // Output : Returns TRUE on success, FALSE on failure.
  244. //-----------------------------------------------------------------------------
  245. BOOL GameData::Load(const char *pszFilename)
  246. {
  247. TokenReader tr;
  248. #ifndef POSIX
  249. if(GetFileAttributes(pszFilename) == 0xffffffff)
  250. return FALSE;
  251. #endif
  252. if(!tr.Open(pszFilename))
  253. return FALSE;
  254. trtoken_t ttype;
  255. char szToken[128];
  256. while (1)
  257. {
  258. if (tr.GetErrorCount() >= MAX_ERRORS)
  259. {
  260. break;
  261. }
  262. ttype = tr.NextToken(szToken, sizeof(szToken));
  263. if(ttype == TOKENEOF)
  264. break;
  265. if(ttype != OPERATOR || !IsToken(szToken, "@"))
  266. {
  267. if(!GDError(tr, "expected @"))
  268. return FALSE;
  269. }
  270. // check what kind it is, and parse a new object
  271. if (tr.NextToken(szToken, sizeof(szToken)) != IDENT)
  272. {
  273. if(!GDError(tr, "expected identifier after @"))
  274. return FALSE;
  275. }
  276. if (IsToken(szToken, "baseclass") || IsToken(szToken, "pointclass") || IsToken(szToken, "solidclass") || IsToken(szToken, "keyframeclass") ||
  277. IsToken(szToken, "moveclass") || IsToken(szToken, "npcclass") || IsToken(szToken, "filterclass"))
  278. {
  279. //
  280. // New class.
  281. //
  282. GDclass *pNewClass = new GDclass;
  283. if (!pNewClass->InitFromTokens(tr, this))
  284. {
  285. tr.IgnoreTill(OPERATOR, "@"); // go to next section
  286. delete pNewClass;
  287. }
  288. else
  289. {
  290. if (IsToken(szToken, "baseclass")) // Not directly available to user.
  291. {
  292. pNewClass->SetBaseClass(true);
  293. }
  294. else if (IsToken(szToken, "pointclass")) // Generic point class.
  295. {
  296. pNewClass->SetPointClass(true);
  297. }
  298. else if (IsToken(szToken, "solidclass")) // Tied to solids.
  299. {
  300. pNewClass->SetSolidClass(true);
  301. }
  302. else if (IsToken(szToken, "npcclass")) // NPC class - can be spawned by npc_maker.
  303. {
  304. pNewClass->SetPointClass(true);
  305. pNewClass->SetNPCClass(true);
  306. }
  307. else if (IsToken(szToken, "filterclass")) // Filter class - can be used as a filter
  308. {
  309. pNewClass->SetPointClass(true);
  310. pNewClass->SetFilterClass(true);
  311. }
  312. else if (IsToken(szToken, "moveclass")) // Animating
  313. {
  314. pNewClass->SetMoveClass(true);
  315. pNewClass->SetPointClass(true);
  316. }
  317. else if (IsToken(szToken, "keyframeclass")) // Animation keyframes
  318. {
  319. pNewClass->SetKeyFrameClass(true);
  320. pNewClass->SetPointClass(true);
  321. }
  322. // Check and see if this new class matches an existing one. If so we will override the previous definition.
  323. int nExistingClassIndex = 0;
  324. GDclass *pExistingClass = ClassForName(pNewClass->GetName(), &nExistingClassIndex);
  325. if (NULL != pExistingClass)
  326. {
  327. m_Classes.InsertAfter(nExistingClassIndex, pNewClass);
  328. m_Classes.Remove(nExistingClassIndex);
  329. }
  330. else
  331. {
  332. m_Classes.AddToTail(pNewClass);
  333. }
  334. }
  335. }
  336. else if (IsToken(szToken, "include"))
  337. {
  338. if (GDGetToken(tr, szToken, sizeof(szToken), STRING))
  339. {
  340. // Let's assume it's in the same directory.
  341. char justPath[MAX_PATH], loadFilename[MAX_PATH];
  342. if ( Q_ExtractFilePath( pszFilename, justPath, sizeof( justPath ) ) )
  343. {
  344. Q_snprintf( loadFilename, sizeof( loadFilename ), "%s%s", justPath, szToken );
  345. }
  346. else
  347. {
  348. Q_strncpy( loadFilename, szToken, sizeof( loadFilename ) );
  349. }
  350. // First try our fully specified directory
  351. if (!Load(loadFilename))
  352. {
  353. // Failing that, try our start directory
  354. if (!Load(szToken))
  355. {
  356. GDError(tr, "error including file: %s", szToken);
  357. }
  358. }
  359. }
  360. }
  361. else if (IsToken(szToken, "mapsize"))
  362. {
  363. if (!ParseMapSize(tr))
  364. {
  365. // Error in map size specifier, skip to next @ sign.
  366. tr.IgnoreTill(OPERATOR, "@");
  367. }
  368. }
  369. else if (IsToken(szToken, "gridnav"))
  370. {
  371. if (!ParseGridNav(tr))
  372. {
  373. // Error in grid nav specifier, skip to next @ sign.
  374. tr.IgnoreTill(OPERATOR, "@");
  375. }
  376. }
  377. else if ( IsToken( szToken, "materialexclusion" ) )
  378. {
  379. if ( !LoadFGDMaterialExclusions( tr ) )
  380. {
  381. // FGD exclusions not defined; skip to next @ sign.
  382. tr.IgnoreTill(OPERATOR, "@");
  383. }
  384. }
  385. else if ( IsToken( szToken, "autovisgroup" ) )
  386. {
  387. if ( !LoadFGDAutoVisGroups( tr ) )
  388. {
  389. // FGD AutoVisGroups not defined; skip to next @ sign.
  390. tr.IgnoreTill(OPERATOR, "@");
  391. }
  392. }
  393. else
  394. {
  395. GDError(tr, "unrecognized section name %s", szToken);
  396. tr.IgnoreTill(OPERATOR, "@");
  397. }
  398. }
  399. if (tr.GetErrorCount() > 0)
  400. {
  401. return FALSE;
  402. }
  403. tr.Close();
  404. return TRUE;
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose: Parses the "mapsize" specifier, which should be of the form:
  408. //
  409. // mapsize(min, max)
  410. //
  411. // ex: mapsize(-8192, 8192)
  412. //
  413. // Input : tr -
  414. // Output : Returns true on success, false on failure.
  415. //-----------------------------------------------------------------------------
  416. bool GameData::ParseMapSize(TokenReader &tr)
  417. {
  418. if (!GDSkipToken(tr, OPERATOR, "("))
  419. {
  420. return false;
  421. }
  422. char szToken[128];
  423. if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
  424. {
  425. return false;
  426. }
  427. int nMin = atoi(szToken);
  428. if (!GDSkipToken(tr, OPERATOR, ","))
  429. {
  430. return false;
  431. }
  432. if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
  433. {
  434. return false;
  435. }
  436. int nMax = atoi(szToken);
  437. if (nMin != nMax)
  438. {
  439. m_nMinMapCoord = Min(nMin, nMax);
  440. m_nMaxMapCoord = Max(nMin, nMax);
  441. }
  442. if (!GDSkipToken(tr, OPERATOR, ")"))
  443. {
  444. return false;
  445. }
  446. return true;
  447. }
  448. //-----------------------------------------------------------------------------
  449. // Purpose: Parses the "gridnav" specifier, which should be of the form:
  450. //
  451. // gridnav(edgesize, offsetx, offsety, pickheight)
  452. //
  453. // ex: gridnav(64, 0, 0, 1000)
  454. //
  455. // Input : tr -
  456. // Output : Returns true on success, false on failure.
  457. //-----------------------------------------------------------------------------
  458. bool GameData::ParseGridNav(TokenReader &tr)
  459. {
  460. if (!GDSkipToken(tr, OPERATOR, "("))
  461. {
  462. return false;
  463. }
  464. char szToken[128];
  465. if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
  466. {
  467. return false;
  468. }
  469. int nEdgeSize = atoi(szToken);
  470. if (!GDSkipToken(tr, OPERATOR, ","))
  471. {
  472. return false;
  473. }
  474. if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
  475. {
  476. return false;
  477. }
  478. int nOffsetX = atoi(szToken);
  479. if (!GDSkipToken(tr, OPERATOR, ","))
  480. {
  481. return false;
  482. }
  483. if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
  484. {
  485. return false;
  486. }
  487. int nOffsetY = atoi(szToken);
  488. if (!GDSkipToken(tr, OPERATOR, ","))
  489. {
  490. return false;
  491. }
  492. if (!GDGetToken(tr, szToken, sizeof(szToken), INTEGER))
  493. {
  494. return false;
  495. }
  496. int nTraceHeight = atoi(szToken);
  497. if (!GDSkipToken(tr, OPERATOR, ")"))
  498. {
  499. return false;
  500. }
  501. m_bGridNavActive = true;
  502. m_nGridNavEdgeSize = nEdgeSize;
  503. m_nGridNavOffsetX = nOffsetX;
  504. m_nGridNavOffsetY = nOffsetY;
  505. m_nTraceHeight = nTraceHeight;
  506. return true;
  507. }
  508. //-----------------------------------------------------------------------------
  509. // Purpose:
  510. // Input : pszName -
  511. // piIndex -
  512. // Output :
  513. //-----------------------------------------------------------------------------
  514. GDclass *GameData::ClassForName(const char *pszName, int *piIndex)
  515. {
  516. int nCount = m_Classes.Count();
  517. for (int i = 0; i < nCount; i++)
  518. {
  519. GDclass *mp = m_Classes.Element(i);
  520. if(!strcmp(mp->GetName(), pszName))
  521. {
  522. if(piIndex)
  523. piIndex[0] = i;
  524. return mp;
  525. }
  526. }
  527. return NULL;
  528. }
  529. void GameData::BeginInstancing( int nPass )
  530. {
  531. m_nRemapStage = nPass;
  532. switch( m_nRemapStage )
  533. {
  534. case 2:
  535. m_nNextNodeID = 1;
  536. break;
  537. }
  538. }
  539. void GameData::BeginMapInstance( )
  540. {
  541. m_NodeRemap.RemoveAll();
  542. }
  543. // These are 'standard' keys that every entity uses, but they aren't specified that way in the .fgd
  544. static const char *RequiredKeys[] =
  545. {
  546. "Origin",
  547. "Angles",
  548. NULL
  549. };
  550. //-----------------------------------------------------------------------------
  551. // Purpose: this function will set up the initial class about to be instanced
  552. // Input : pszClassName - the class name of the entity to be instanced
  553. // pszInstancePrefix - the prefix to be used for all name fields
  554. // Origin - the origin offset of the instance
  555. // Angles - the angle rotation of the instance
  556. // Output : if successful, will return the game data class of the class name
  557. //-----------------------------------------------------------------------------
  558. GDclass *GameData::BeginInstanceRemap( const char *pszClassName, const char *pszInstancePrefix, Vector &Origin, QAngle &Angle )
  559. {
  560. m_InstanceOrigin = Origin;
  561. m_InstanceAngle = Angle;
  562. AngleMatrix( m_InstanceAngle, m_InstanceOrigin, m_InstanceMat );
  563. strcpy( m_InstancePrefix, pszInstancePrefix );
  564. if ( m_InstanceClass )
  565. {
  566. delete m_InstanceClass;
  567. m_InstanceClass = NULL;
  568. }
  569. if ( strcmpi( pszClassName, "info_overlay_accessor" ) == 0 )
  570. { // yucky hack for a made up entity in the bsp process
  571. pszClassName = "info_overlay";
  572. }
  573. GDclass *BaseClass = ClassForName( pszClassName );
  574. if ( BaseClass )
  575. {
  576. m_InstanceClass = new GDclass();
  577. m_InstanceClass->Parent = this;
  578. m_InstanceClass->AddBase( BaseClass );
  579. for( int i = 0; RequiredKeys[ i ]; i++ )
  580. {
  581. if ( m_InstanceClass->VarForName( RequiredKeys[ i ] ) == NULL )
  582. {
  583. BaseClass = ClassForName( RequiredKeys[ i ] );
  584. if ( BaseClass )
  585. {
  586. m_InstanceClass->AddBase( BaseClass );
  587. }
  588. }
  589. }
  590. }
  591. else
  592. {
  593. m_InstanceClass = NULL;
  594. }
  595. return m_InstanceClass;
  596. }
  597. enum tRemapOperation
  598. {
  599. REMAP_NAME = 0,
  600. REMAP_POSITION,
  601. REMAP_ANGLE,
  602. REMAP_ANGLE_NEGATIVE_PITCH,
  603. REMAP_NODE,
  604. REMAP_INSTANCE_VARIABLE,
  605. };
  606. static CUtlMap< GDIV_TYPE, tRemapOperation > RemapOperation;
  607. //-----------------------------------------------------------------------------
  608. // Purpose: function to sort the class type for the RemapOperations map
  609. // Input : type1 - the first type to compare against
  610. // type2 - the second type to compare against
  611. // Output : returns true if the first type is less than the second one
  612. //-----------------------------------------------------------------------------
  613. static bool CUtlType_LessThan( const GDIV_TYPE &type1, const GDIV_TYPE &type2 )
  614. {
  615. return ( type1 < type2 );
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose: this function will attempt to remap a key's value
  619. // Input : pszKey - the name of the key
  620. // pszInvalue - the original value
  621. // AllowNameRemapping - only do name remapping if this parameter is true.
  622. // this is generally only false on the instance level.
  623. // Output : returns true if the value changed
  624. // pszOutValue - the new value if changed
  625. //-----------------------------------------------------------------------------
  626. bool GameData::RemapKeyValue( const char *pszKey, const char *pszInValue, char *pszOutValue, TNameFixup NameFixup )
  627. {
  628. if ( RemapOperation.Count() == 0 )
  629. {
  630. RemapOperation.SetLessFunc( &CUtlType_LessThan );
  631. RemapOperation.Insert( ivAngle, REMAP_ANGLE );
  632. RemapOperation.Insert( ivStringInstanced, REMAP_NAME );
  633. RemapOperation.Insert( ivTargetDest, REMAP_NAME );
  634. RemapOperation.Insert( ivTargetSrc, REMAP_NAME );
  635. RemapOperation.Insert( ivFilterClass, REMAP_NAME );
  636. RemapOperation.Insert( ivOrigin, REMAP_POSITION );
  637. RemapOperation.Insert( ivVecLine, REMAP_POSITION );
  638. RemapOperation.Insert( ivAxis, REMAP_ANGLE );
  639. RemapOperation.Insert( ivAngleNegativePitch, REMAP_ANGLE_NEGATIVE_PITCH );
  640. RemapOperation.Insert( ivNodeDest, REMAP_NODE );
  641. RemapOperation.Insert( ivNodeID, REMAP_NODE );
  642. RemapOperation.Insert( ivInstanceVariable, REMAP_INSTANCE_VARIABLE );
  643. }
  644. if ( !m_InstanceClass )
  645. {
  646. return false;
  647. }
  648. GDinputvariable *KVVar = m_InstanceClass->VarForName( pszKey );
  649. if ( !KVVar )
  650. {
  651. return false;
  652. }
  653. GDIV_TYPE KVType = KVVar->GetType();
  654. int KVRemapIndex = RemapOperation.Find( KVType );
  655. if ( KVRemapIndex == RemapOperation.InvalidIndex() )
  656. {
  657. return false;
  658. }
  659. strcpy( pszOutValue, pszInValue );
  660. switch( RemapOperation[ KVRemapIndex ] )
  661. {
  662. case REMAP_NAME:
  663. if ( m_nRemapStage != 1 )
  664. {
  665. return false;
  666. }
  667. if ( KVType != ivInstanceVariable )
  668. {
  669. RemapNameField( pszInValue, pszOutValue, NameFixup );
  670. }
  671. break;
  672. case REMAP_POSITION:
  673. {
  674. if ( m_nRemapStage != 1 )
  675. {
  676. return false;
  677. }
  678. Vector inPoint( 0.0f, 0.0f, 0.0f ), outPoint;
  679. sscanf ( pszInValue, "%f %f %f", &inPoint.x, &inPoint.y, &inPoint.z );
  680. VectorTransform( inPoint, m_InstanceMat, outPoint );
  681. sprintf( pszOutValue, "%g %g %g", outPoint.x, outPoint.y, outPoint.z );
  682. }
  683. break;
  684. case REMAP_ANGLE:
  685. if ( m_nRemapStage != 1 )
  686. {
  687. return false;
  688. }
  689. if ( m_InstanceAngle.x != 0.0f || m_InstanceAngle.y != 0.0f || m_InstanceAngle.z != 0.0f )
  690. {
  691. QAngle inAngles( 0.0f, 0.0f, 0.0f ), outAngles;
  692. matrix3x4_t angToWorld, localMatrix;
  693. sscanf ( pszInValue, "%f %f %f", &inAngles.x, &inAngles.y, &inAngles.z );
  694. AngleMatrix( inAngles, angToWorld );
  695. MatrixMultiply( m_InstanceMat, angToWorld, localMatrix );
  696. MatrixAngles( localMatrix, outAngles );
  697. sprintf( pszOutValue, "%g %g %g", outAngles.x, outAngles.y, outAngles.z );
  698. }
  699. break;
  700. case REMAP_ANGLE_NEGATIVE_PITCH:
  701. if ( m_nRemapStage != 1 )
  702. {
  703. return false;
  704. }
  705. if ( m_InstanceAngle.x != 0.0f || m_InstanceAngle.y != 0.0f || m_InstanceAngle.z != 0.0f )
  706. {
  707. QAngle inAngles( 0.0f, 0.0f, 0.0f ), outAngles;
  708. matrix3x4_t angToWorld, localMatrix;
  709. sscanf ( pszInValue, "%f", &inAngles.x ); // just the pitch
  710. inAngles.x = -inAngles.x;
  711. AngleMatrix( inAngles, angToWorld );
  712. MatrixMultiply( m_InstanceMat, angToWorld, localMatrix );
  713. MatrixAngles( localMatrix, outAngles );
  714. sprintf( pszOutValue, "%g", -outAngles.x ); // just the pitch
  715. }
  716. break;
  717. case REMAP_NODE:
  718. {
  719. int nFromNode = atoi( pszInValue );
  720. int nToNode;
  721. int nIndex = m_NodeRemap.Find( nFromNode );
  722. if ( m_NodeRemap.IsValidIndex( nIndex ) == false )
  723. {
  724. nToNode = m_nNextNodeID;
  725. m_nNextNodeID++;
  726. m_NodeRemap.Insert( atoi( pszInValue ), nToNode );
  727. }
  728. else
  729. {
  730. nToNode = m_NodeRemap.Element( nIndex );
  731. }
  732. sprintf( pszOutValue, "%d", nToNode );
  733. }
  734. break;
  735. case REMAP_INSTANCE_VARIABLE:
  736. if ( m_nRemapStage != 1 )
  737. {
  738. return false;
  739. }
  740. RemapInstanceField( pszInValue, pszOutValue, NameFixup );
  741. break;
  742. }
  743. return ( strcmpi( pszInValue, pszOutValue ) != 0 );
  744. }
  745. //-----------------------------------------------------------------------------
  746. // Purpose: this function will attempt to remap a name field.
  747. // Input : pszInvalue - the original value
  748. // AllowNameRemapping - only do name remapping if this parameter is true.
  749. // this is generally only false on the instance level.
  750. // Output : returns true if the value changed
  751. // pszOutValue - the new value if changed
  752. //-----------------------------------------------------------------------------
  753. bool GameData::RemapNameField( const char *pszInValue, char *pszOutValue, TNameFixup NameFixup )
  754. {
  755. strcpy( pszOutValue, pszInValue );
  756. if ( pszInValue[ 0 ] && pszInValue[ 0 ] != '@' && pszInValue[ 0 ] != '!' )
  757. { // ! or @ at the start of a value means it is global and should not be remapped
  758. switch( NameFixup )
  759. {
  760. case NAME_FIXUP_PREFIX:
  761. sprintf( pszOutValue, "%s-%s", m_InstancePrefix, pszInValue );
  762. break;
  763. case NAME_FIXUP_POSTFIX:
  764. sprintf( pszOutValue, "%s-%s", pszInValue, m_InstancePrefix );
  765. break;
  766. }
  767. }
  768. return ( strcmpi( pszInValue, pszOutValue ) != 0 );
  769. }
  770. //-----------------------------------------------------------------------------
  771. // Purpose: this function will attempt to remap a instance field.
  772. // Input : pszInvalue - the original value
  773. // Output : returns true if the value changed
  774. // pszOutValue - the new value if changed
  775. //-----------------------------------------------------------------------------
  776. bool GameData::RemapInstanceField( const char *pszInValue, char *pszOutValue, TNameFixup NameFixup )
  777. {
  778. strcpy( pszOutValue, pszInValue );
  779. const char *pszInEdit = strchr( pszInValue, ' ' );
  780. char *pchOutEdit = strchr( pszOutValue, ' ' );
  781. if ( pszInEdit && pchOutEdit )
  782. {
  783. pszInEdit++;
  784. pchOutEdit++;
  785. if ( pszInEdit[ 0 ] && pszInEdit[ 0 ] != '@' && pszInEdit[ 0 ] != '!' &&
  786. pszInEdit[ 0 ] != '-' && pszInEdit[ 0 ] != '.' &&
  787. !( pszInEdit[ 0 ] >= '0' && pszInEdit[ 0 ] <= '9' ) )
  788. { // ! or @ at the start of a value or a number means it is global and should not be remapped
  789. switch( NameFixup )
  790. {
  791. case NAME_FIXUP_PREFIX:
  792. sprintf( pchOutEdit, "%s-%s", m_InstancePrefix, pszInEdit );
  793. break;
  794. case NAME_FIXUP_POSTFIX:
  795. sprintf( pchOutEdit, "%s-%s", pszInEdit, m_InstancePrefix );
  796. break;
  797. }
  798. }
  799. }
  800. return ( strcmpi( pszInValue, pszOutValue ) != 0 );
  801. }
  802. //-----------------------------------------------------------------------------
  803. // Purpose: Gathers any FGD-defined material directory exclusions
  804. // Input :
  805. // Output :
  806. //-----------------------------------------------------------------------------
  807. bool GameData::LoadFGDMaterialExclusions( TokenReader &tr )
  808. {
  809. if ( !GDSkipToken( tr, OPERATOR, "[" ) )
  810. {
  811. return false;
  812. }
  813. while ( 1 )
  814. {
  815. char szToken[128];
  816. bool bMatchFound = false;
  817. if ( tr.PeekTokenType( szToken, sizeof( szToken ) ) == OPERATOR )
  818. {
  819. break;
  820. }
  821. else if ( GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
  822. {
  823. // Make sure we haven't loaded this from another FGD
  824. for ( int i = 0; i < m_FGDMaterialExclusions.Count(); i++ )
  825. {
  826. if ( !stricmp( szToken, m_FGDMaterialExclusions[i].szDirectory ) )
  827. {
  828. bMatchFound = true;
  829. break;
  830. }
  831. }
  832. // Parse the string
  833. if ( bMatchFound == false )
  834. {
  835. int index = m_FGDMaterialExclusions.AddToTail();
  836. Q_strncpy( m_FGDMaterialExclusions[index].szDirectory, szToken, sizeof( m_FGDMaterialExclusions[index].szDirectory ) );
  837. m_FGDMaterialExclusions[index].bUserGenerated = false;
  838. }
  839. }
  840. }
  841. //
  842. // Closing square brace.
  843. //
  844. if ( !GDSkipToken( tr, OPERATOR, "]" ) )
  845. {
  846. return( FALSE );
  847. }
  848. return true;
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Purpose: Gathers any FGD-defined Auto VisGroups
  852. // Input :
  853. // Output :
  854. //-----------------------------------------------------------------------------
  855. bool GameData::LoadFGDAutoVisGroups( TokenReader &tr )
  856. {
  857. int gindex = 0; // Index of AutoVisGroups
  858. int cindex = 0; // Index of Classes
  859. char szToken[128];
  860. // Handle the Parent -- World Geometry, Entities, World Detail
  861. if ( GDSkipToken( tr, OPERATOR, "=" ) )
  862. {
  863. // We expect a name
  864. if ( !GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
  865. {
  866. return( FALSE );
  867. }
  868. gindex = m_FGDAutoVisGroups.AddToTail();
  869. Q_strncpy( m_FGDAutoVisGroups[gindex].szParent, szToken, sizeof( m_FGDAutoVisGroups[gindex].szParent ) );
  870. // We expect a Class
  871. if ( !GDSkipToken( tr, OPERATOR, "[" ) )
  872. {
  873. return( FALSE );
  874. }
  875. }
  876. // Handle the Class(es) -- Brush Entities, Occluders, Lights
  877. while ( 1 )
  878. {
  879. if ( GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
  880. {
  881. cindex = m_FGDAutoVisGroups[gindex].m_Classes.AddToTail();
  882. Q_strncpy( m_FGDAutoVisGroups[gindex].m_Classes[cindex].szClass, szToken, sizeof( m_FGDAutoVisGroups[gindex].m_Classes[cindex].szClass ) );
  883. if ( !GDSkipToken( tr, OPERATOR, "[" ) )
  884. {
  885. return( FALSE );
  886. }
  887. // Parse objects/entities -- func_detail, point_template, light_spot
  888. while ( 1 )
  889. {
  890. if ( tr.PeekTokenType( szToken, sizeof( szToken ) ) == OPERATOR )
  891. {
  892. break;
  893. }
  894. if ( !GDGetToken( tr, szToken, sizeof( szToken ), STRING ) )
  895. {
  896. return( FALSE );
  897. }
  898. m_FGDAutoVisGroups[gindex].m_Classes[cindex].szEntities.CopyAndAddToTail( szToken );
  899. }
  900. if ( !GDSkipToken( tr, OPERATOR, "]" ) )
  901. {
  902. return( FALSE );
  903. }
  904. // See if we have another Class coming up
  905. if ( tr.PeekTokenType( szToken, sizeof( szToken ) ) == STRING )
  906. {
  907. continue;
  908. }
  909. // If no more Classes, we now expect a terminating ']'
  910. if ( !GDSkipToken( tr, OPERATOR, "]" ) )
  911. {
  912. return( FALSE );
  913. }
  914. // We're done
  915. return true;
  916. }
  917. // We don't have another Class; look for a terminating brace
  918. else
  919. {
  920. if ( !GDSkipToken( tr, OPERATOR, "]" ) )
  921. {
  922. return( FALSE );
  923. }
  924. }
  925. }
  926. // Safety net
  927. GDError( tr, "Malformed AutoVisGroup -- Last processed: %s", szToken );
  928. return( FALSE );
  929. }
  930. // memdbgon must be the last include file in a .cpp file!!!
  931. #include "tier0/memdbgoff.h"