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.

1191 lines
25 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include <io.h>
  9. #include "hammer.h"
  10. #include "GlobalFunctions.h"
  11. #include "MapErrorsDlg.h"
  12. #include "Options.h"
  13. #include "MapDoc.h"
  14. #include "MapEntity.h"
  15. #include "MapFace.h"
  16. #include "MapGroup.h"
  17. #include "MapSolid.h"
  18. #include "MapStudioModel.h"
  19. #include "MapWorld.h"
  20. #include "progdlg.h"
  21. #include "TextureSystem.h"
  22. #include "MapDisp.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. #pragma optimize("g", off)
  26. #pragma warning(disable: 4748) // buffer overrung with optimizations off - remove if we turn "g" back on
  27. #define TEXTURE_NAME_LEN 128
  28. // All files are opened in binary, and we want to save CR/LF
  29. #define ENDLINE "\r\n"
  30. enum
  31. {
  32. fileOsError = -1, // big error!
  33. fileError = -2, // problem
  34. fileOk = -3, // loaded ok
  35. fileDone = -4 // got not-my-kind of line
  36. };
  37. BOOL bSaveVisiblesOnly;
  38. static BOOL bErrors;
  39. static int nInvalidSolids;
  40. static CProgressDlg *pProgDlg;
  41. static MAPFORMAT MapFormat;
  42. static BOOL bStuffed;
  43. static char szStuffed[255];
  44. static UINT uMapVersion = 0;
  45. //-----------------------------------------------------------------------------
  46. // Purpose:
  47. // Input : buf -
  48. //-----------------------------------------------------------------------------
  49. static void StuffLine(char * buf)
  50. {
  51. Assert(!bStuffed);
  52. strcpy(szStuffed, buf);
  53. bStuffed = TRUE;
  54. }
  55. //-----------------------------------------------------------------------------
  56. // Purpose:
  57. // Input : file -
  58. // buf -
  59. //-----------------------------------------------------------------------------
  60. static void GetLine(std::fstream& file, char *buf)
  61. {
  62. if(bStuffed)
  63. {
  64. if(buf)
  65. strcpy(buf, szStuffed);
  66. bStuffed = FALSE;
  67. return;
  68. }
  69. char szBuf[1024];
  70. while(1)
  71. {
  72. file >> std::ws;
  73. file.getline(szBuf, 512);
  74. if(file.eof())
  75. return;
  76. if(!strncmp(szBuf, "//", 2))
  77. continue;
  78. file >> std::ws;
  79. if(buf)
  80. {
  81. // char *p = strchr(szBuf, '\n');
  82. // if(p) p[0] = 0;
  83. // p = strchr(szBuf, '\r');
  84. // if(p) p[0] = 0;
  85. strcpy(buf, szBuf);
  86. }
  87. return;
  88. }
  89. }
  90. //-----------------------------------------------------------------------------
  91. // Purpose:
  92. // Input : pObject -
  93. // file -
  94. // pIntersecting -
  95. // Output : int
  96. //-----------------------------------------------------------------------------
  97. static int SaveSolidChildrenOf(CMapClass *pObject, std::fstream& file, BoundBox *pIntersecting = NULL)
  98. {
  99. CMapWorld *pWorld = (CMapWorld*) CMapClass::GetWorldObject(pObject);
  100. //
  101. // If we are only saving visible objects and this object isn't visible, don't save it.
  102. //
  103. if (bSaveVisiblesOnly && (pObject != pWorld) && !pObject->IsVisible())
  104. {
  105. return fileOk; // not an error - return ok
  106. }
  107. //
  108. // If we are only saving objects within a particular bounding box and this object isn't, don't save it.
  109. //
  110. if (pIntersecting && !pObject->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
  111. {
  112. return fileOk;
  113. }
  114. const CMapObjectList *pChildren = pObject->GetChildren();
  115. FOR_EACH_OBJ( *pChildren, pos )
  116. {
  117. int iRvl = -1;
  118. CMapClass *pChild = (CUtlReference< CMapClass >)pChildren->Element(pos);
  119. if (!pIntersecting || pChild->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
  120. {
  121. if (pChild->IsMapClass(MAPCLASS_TYPE(CMapSolid)))
  122. {
  123. if (!bSaveVisiblesOnly || pChild->IsVisible())
  124. {
  125. iRvl = pChild->SerializeMAP(file, TRUE);
  126. }
  127. }
  128. else if (pChild->IsMapClass(MAPCLASS_TYPE(CMapGroup)))
  129. {
  130. iRvl = SaveSolidChildrenOf(pChild, file, pIntersecting);
  131. }
  132. // return error if there is an error
  133. if (iRvl != -1 && iRvl != fileOk)
  134. {
  135. return iRvl;
  136. }
  137. }
  138. }
  139. return fileOk; // ok.
  140. }
  141. //-----------------------------------------------------------------------------
  142. // Purpose:
  143. // Input : pObject -
  144. // file -
  145. // pIntersecting -
  146. // Output : int
  147. //-----------------------------------------------------------------------------
  148. static int SaveEntityChildrenOf(CMapClass *pObject, std::fstream& file, BoundBox *pIntersecting)
  149. {
  150. CMapWorld *pWorld = (CMapWorld *)CMapClass::GetWorldObject(pObject);
  151. if (bSaveVisiblesOnly && pObject != pWorld && !pObject->IsVisible())
  152. {
  153. return fileOk; // no error
  154. }
  155. if (pIntersecting && !pObject->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
  156. {
  157. return fileOk;
  158. }
  159. const CMapObjectList *pChildren = pObject->GetChildren();
  160. FOR_EACH_OBJ( *pChildren, pos )
  161. {
  162. int iRvl = -1;
  163. CMapClass *pChild = (CUtlReference< CMapClass >)pChildren->Element(pos);
  164. if (!pIntersecting || pChild->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
  165. {
  166. if (pChild->IsMapClass(MAPCLASS_TYPE(CMapEntity)))
  167. {
  168. if (!bSaveVisiblesOnly || pChild->IsVisible())
  169. {
  170. iRvl = pChild->SerializeMAP(file, TRUE);
  171. }
  172. }
  173. else if (pChild->IsMapClass(MAPCLASS_TYPE(CMapGroup)))
  174. {
  175. iRvl = SaveEntityChildrenOf(pChild, file, pIntersecting);
  176. }
  177. // return error if there is an error
  178. if (iRvl != -1 && iRvl != fileOk)
  179. {
  180. return iRvl;
  181. }
  182. }
  183. }
  184. return fileOk; // ok.
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose:
  188. // Input : *pObject -
  189. // file -
  190. // Output : int
  191. //-----------------------------------------------------------------------------
  192. static int ReadSolids(CMapClass *pObject, std::fstream& file)
  193. {
  194. int nSolids = 0;
  195. char szBuf[128];
  196. while(1)
  197. {
  198. GetLine(file, szBuf);
  199. if(szBuf[0] != '{')
  200. {
  201. StuffLine(szBuf);
  202. break;
  203. }
  204. CMapSolid *pSolid = new CMapSolid;
  205. int iRvl = pSolid->SerializeMAP(file, FALSE);
  206. if(iRvl == fileError)
  207. {
  208. // delete the solid
  209. delete pSolid;
  210. ++nInvalidSolids;
  211. }
  212. else if(iRvl == fileOsError)
  213. {
  214. // big problem
  215. delete pSolid;
  216. return fileOsError;
  217. }
  218. else
  219. pObject->AddChild(pSolid);
  220. ++nSolids;
  221. }
  222. return nSolids;
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose:
  226. // Input : file -
  227. // Output : int
  228. //-----------------------------------------------------------------------------
  229. static int PeekChar(std::fstream& file)
  230. {
  231. if(bStuffed) // stuffed.. return first char
  232. return szStuffed[0];
  233. char szBuf[1024];
  234. szBuf[0] = 0;
  235. // get next line
  236. GetLine(file, szBuf);
  237. // still blank? return eof
  238. if(szBuf[0] == 0)
  239. return EOF;
  240. // to get it next call to getline
  241. StuffLine(szBuf);
  242. return szBuf[0];
  243. }
  244. //-----------------------------------------------------------------------------
  245. // Purpose: Sets the MAP format for saving.
  246. // Input : mf - MAP format to use when saving.
  247. //-----------------------------------------------------------------------------
  248. void SetMapFormat(MAPFORMAT mf)
  249. {
  250. Assert((mf == mfHalfLife) || (mf == mfHalfLife2));
  251. MapFormat = mf;
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose:
  255. // Input : file -
  256. // fIsStoring -
  257. // Output : int
  258. //-----------------------------------------------------------------------------
  259. int CMapClass::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  260. {
  261. // no info stored in MAPs ..
  262. return fileOk;
  263. }
  264. //-----------------------------------------------------------------------------
  265. // Purpose:
  266. // Input : file -
  267. // fIsStoring -
  268. // Output : int
  269. //-----------------------------------------------------------------------------
  270. int CMapFace::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  271. {
  272. char szBuf[512];
  273. if (fIsStoring)
  274. {
  275. char *pszTexture;
  276. char szTexture[sizeof(texture.texture)+1];
  277. szTexture[sizeof(texture.texture)] = 0;
  278. memcpy(szTexture, texture.texture, sizeof texture.texture);
  279. strlwr(szTexture);
  280. if (MapFormat == mfQuake2)
  281. {
  282. pszTexture = strstr(szTexture, ".");
  283. if (pszTexture)
  284. {
  285. *pszTexture = 0;
  286. }
  287. pszTexture = strstr(szTexture, "textures\\");
  288. if (pszTexture == NULL)
  289. {
  290. pszTexture = szTexture;
  291. }
  292. else
  293. {
  294. pszTexture += strlen("textures\\");
  295. }
  296. }
  297. else
  298. {
  299. pszTexture = szTexture;
  300. }
  301. strupr(szTexture);
  302. //
  303. // Reverse the slashes -- thank you id.
  304. //
  305. for (int i = strlen(pszTexture) - 1; i >= 0; i--)
  306. {
  307. if (pszTexture[i] == '\\')
  308. pszTexture[i] = '/';
  309. }
  310. //
  311. // Convert the plane points to integers.
  312. //
  313. for (int nPlane = 0; nPlane < 3; nPlane++)
  314. {
  315. plane.planepts[nPlane][0] = rint(plane.planepts[nPlane][0]);
  316. plane.planepts[nPlane][1] = rint(plane.planepts[nPlane][1]);
  317. plane.planepts[nPlane][2] = rint(plane.planepts[nPlane][2]);
  318. }
  319. //
  320. // Check for duplicate plane points. All three plane points must be unique
  321. // or it isn't a valid plane. Try to fix it if it isn't valid.
  322. //
  323. if (!CheckFace())
  324. {
  325. Fix();
  326. }
  327. sprintf(szBuf,
  328. "( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) "
  329. "%s "
  330. "[ %g %g %g %g ] "
  331. "[ %g %g %g %g ] "
  332. "%g %g %g ",
  333. plane.planepts[0][0], plane.planepts[0][1], plane.planepts[0][2],
  334. plane.planepts[1][0], plane.planepts[1][1], plane.planepts[1][2],
  335. plane.planepts[2][0], plane.planepts[2][1], plane.planepts[2][2],
  336. pszTexture,
  337. (double)texture.UAxis[0], (double)texture.UAxis[1], (double)texture.UAxis[2], (double)texture.UAxis[3],
  338. (double)texture.VAxis[0], (double)texture.VAxis[1], (double)texture.VAxis[2], (double)texture.VAxis[3],
  339. (double)texture.rotate,
  340. (double)texture.scale[0],
  341. (double)texture.scale[1]);
  342. file << szBuf << ENDLINE;
  343. return fileOk;
  344. }
  345. else
  346. {
  347. // load the plane
  348. GetLine(file, szBuf);
  349. if(szBuf[0] != '(')
  350. {
  351. StuffLine(szBuf);
  352. return fileDone;
  353. }
  354. char szTexName[TEXTURE_NAME_LEN];
  355. DWORD q2contents;
  356. DWORD q2surface;
  357. int nLightmapScale;
  358. int nDummy;
  359. int nRead;
  360. if( uMapVersion >= 340 )
  361. {
  362. nRead = sscanf(szBuf,
  363. "( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
  364. "%s "
  365. "[ %f %f %f %f ] "
  366. "[ %f %f %f %f ] "
  367. "%f %f %f "
  368. "%u %u %u",
  369. &plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
  370. &plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
  371. &plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
  372. &szTexName,
  373. &texture.UAxis[0], &texture.UAxis[1], &texture.UAxis[2], &texture.UAxis[3],
  374. &texture.VAxis[0], &texture.VAxis[1], &texture.VAxis[2], &texture.VAxis[3],
  375. &texture.rotate,
  376. &texture.scale[0],
  377. &texture.scale[1],
  378. &q2contents,
  379. &q2surface,
  380. &nLightmapScale);
  381. if (nRead < 21)
  382. {
  383. bErrors = TRUE;
  384. }
  385. else if (nRead == 24)
  386. {
  387. // got q2 values - set them here
  388. texture.q2contents = q2contents;
  389. texture.q2surface = q2surface;
  390. texture.nLightmapScale = nLightmapScale;
  391. if (texture.nLightmapScale == 0)
  392. {
  393. texture.nLightmapScale = g_pGameConfig->GetDefaultLightmapScale();
  394. }
  395. }
  396. //
  397. // very cheesy HACK!!! -- this will be better when we have chunks
  398. //
  399. if( uMapVersion <= 350 )
  400. {
  401. if( ( file.peek() != '(' ) && ( file.peek() != '}' ) )
  402. {
  403. EditDispHandle_t handle = EditDispMgr()->Create();
  404. SetDisp( handle );
  405. CMapDisp *pDisp = EditDispMgr()->GetDisp( handle );
  406. pDisp->SerializedLoadMAP( file, this, uMapVersion );
  407. }
  408. }
  409. }
  410. else if (uMapVersion >= 220 )
  411. {
  412. nRead = sscanf(szBuf,
  413. "( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
  414. "%s "
  415. "[ %f %f %f %f ] "
  416. "[ %f %f %f %f ] "
  417. "%f %f %f "
  418. "%u %u %u",
  419. &plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
  420. &plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
  421. &plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
  422. &szTexName,
  423. &texture.UAxis[0], &texture.UAxis[1], &texture.UAxis[2], &texture.UAxis[3],
  424. &texture.VAxis[0], &texture.VAxis[1], &texture.VAxis[2], &texture.VAxis[3],
  425. &texture.rotate,
  426. &texture.scale[0],
  427. &texture.scale[1],
  428. &q2contents,
  429. &q2surface,
  430. &nDummy); // Pre-340 didn't have lightmap scale.
  431. if (nRead < 21)
  432. {
  433. bErrors = TRUE;
  434. }
  435. else if (nRead == 24)
  436. {
  437. // got q2 values - set them here
  438. texture.q2contents = q2contents;
  439. texture.q2surface = q2surface;
  440. }
  441. }
  442. else
  443. {
  444. nRead = sscanf(szBuf,
  445. "( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
  446. "%s "
  447. "%f %f %f "
  448. "%f %f %u %u %u",
  449. &plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
  450. &plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
  451. &plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
  452. &szTexName,
  453. &texture.UAxis[3],
  454. &texture.VAxis[3],
  455. &texture.rotate,
  456. &texture.scale[0],
  457. &texture.scale[1],
  458. &q2contents,
  459. &q2surface,
  460. &nDummy); // Pre-340 didn't have lightmap scale.
  461. if (nRead < 15)
  462. {
  463. bErrors = TRUE;
  464. }
  465. else if (nRead == 18)
  466. {
  467. // got q2 values - set them here
  468. texture.q2contents = q2contents;
  469. texture.q2surface = q2surface;
  470. }
  471. }
  472. if (g_pGameConfig->GetTextureFormat() != tfVMT)
  473. {
  474. // reverse the slashes -- thank you id
  475. for (int i = strlen(szTexName) - 1; i >= 0; i--)
  476. {
  477. if (szTexName[i] == '/')
  478. szTexName[i] = '\\';
  479. }
  480. }
  481. SetTexture(szTexName);
  482. }
  483. if (file.fail())
  484. {
  485. return fileOsError;
  486. }
  487. return fileOk;
  488. }
  489. //-----------------------------------------------------------------------------
  490. // Purpose:
  491. // Input : file -
  492. // fIsStoring -
  493. // Output : int
  494. //-----------------------------------------------------------------------------
  495. int MDkeyvalue::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  496. {
  497. // load/save a keyvalue
  498. char szBuf[1024];
  499. if(fIsStoring)
  500. {
  501. // save a keyvalue
  502. sprintf( szBuf,
  503. "\"%s\" \"%s\"",
  504. Key(), Value() );
  505. file << szBuf << ENDLINE;
  506. }
  507. else
  508. {
  509. GetLine(file, szBuf);
  510. if(szBuf[0] != '\"')
  511. {
  512. StuffLine(szBuf);
  513. return fileDone;
  514. }
  515. char *p = strchr(szBuf, '\"');
  516. p = strchr(p+1, '\"');
  517. if(!p)
  518. return fileError;
  519. p[0] = 0;
  520. strcpy(szKey, szBuf+1);
  521. // advance to start of value string
  522. p = strchr(p+1, '\"');
  523. if(!p)
  524. return fileError;
  525. // ocpy in value
  526. strcpy(szValue, p+1);
  527. // kill trailing "
  528. p = strchr(szValue, '\"');
  529. if(!p)
  530. return fileError;
  531. p[0] = 0;
  532. }
  533. return file.fail() ? fileOsError : fileOk;
  534. }
  535. //-----------------------------------------------------------------------------
  536. // Purpose:
  537. // Input : file -
  538. // fIsStoring -
  539. // Output : int
  540. //-----------------------------------------------------------------------------
  541. int CMapSolid::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  542. {
  543. CMapClass::SerializeMAP(file, fIsStoring);
  544. // load/save a brush
  545. if (fIsStoring)
  546. {
  547. // save the brush
  548. file << "{" << ENDLINE;
  549. // serialize the Faces
  550. int nFaces = Faces.GetCount();
  551. for(int i = 0; i < nFaces; i++)
  552. {
  553. if(!Faces[i].Points)
  554. continue;
  555. if(Faces[i].SerializeMAP(file, fIsStoring) == fileError)
  556. return fileError;
  557. }
  558. // terminator
  559. file << "}" << ENDLINE;
  560. }
  561. else
  562. {
  563. // caller skipped delimiter
  564. Faces.SetCount(0);
  565. // read Faces
  566. for(int i = 0; ; i++)
  567. {
  568. // extract plane
  569. if (Faces[i].SerializeMAP(file, fIsStoring) == fileDone)
  570. {
  571. // when fileDone is returned, no face was loaded
  572. break;
  573. }
  574. Faces[i].CalcPlane();
  575. }
  576. GetLine(file, NULL); // ignore line
  577. if (!file.fail())
  578. {
  579. //
  580. // Create the solid using the planes that were read from the MAP file.
  581. //
  582. if (CreateFromPlanes() == FALSE)
  583. {
  584. bErrors = TRUE;
  585. return(fileError);
  586. }
  587. //
  588. // If we are reading from an old map file, the texture axes will need to be set up.
  589. // Leave the rotation and shifts alone; they were read from the MAP file.
  590. //
  591. if (uMapVersion < 220)
  592. {
  593. InitializeTextureAxes(TEXTURE_ALIGN_QUAKE, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
  594. }
  595. }
  596. else
  597. {
  598. return(fileOsError);
  599. }
  600. CalcBounds();
  601. //
  602. // Set solid type based on texture name.
  603. //
  604. m_eSolidType = HL1SolidTypeFromTextureName(Faces[0].texture.texture);
  605. }
  606. return(file.fail() ? fileOsError : fileOk);
  607. }
  608. //-----------------------------------------------------------------------------
  609. // Purpose:
  610. // Input : file -
  611. // fIsStoring -
  612. // Output : int
  613. //-----------------------------------------------------------------------------
  614. int CMapEntity::SerializeMAP(std::fstream &file, BOOL fIsStoring)
  615. {
  616. CMapClass::SerializeMAP(file, fIsStoring);
  617. // load/save an object
  618. if (fIsStoring)
  619. {
  620. //
  621. // If it's a solidentity but it doesn't have any solids,
  622. // don't save it.
  623. //
  624. if (!IsPlaceholder() && !m_Children.Count())
  625. {
  626. return(fileOk);
  627. }
  628. //
  629. // Save it.
  630. //
  631. file << "{" << ENDLINE;
  632. //
  633. // Save keyvalues & other data.
  634. //
  635. CEditGameClass::SerializeMAP(file, fIsStoring);
  636. //
  637. // If this is a placeholder and either has no class or is not a solid class,
  638. // save our origin.
  639. //
  640. if (IsPlaceholder() && (!IsClass() || !IsSolidClass()))
  641. {
  642. MDkeyvalue tmpkv;
  643. strcpy(tmpkv.szKey, "origin");
  644. Vector Origin;
  645. GetOrigin(Origin);
  646. sprintf(tmpkv.szValue, "%.0f %.0f %.0f", Origin[0], Origin[1], Origin[2]);
  647. tmpkv.SerializeMAP(file, fIsStoring);
  648. }
  649. if (!(IsPlaceholder()))
  650. {
  651. SaveSolidChildrenOf(this, file);
  652. }
  653. file << "}" << ENDLINE;
  654. }
  655. else
  656. {
  657. // load keyvalues
  658. CEditGameClass::SerializeMAP(file, fIsStoring);
  659. // solids
  660. if (!ReadSolids(this, file))
  661. {
  662. flags |= flagPlaceholder;
  663. }
  664. // skip delimiter
  665. GetLine(file, NULL);
  666. }
  667. return file.fail() ? fileOsError : fileOk;
  668. }
  669. //-----------------------------------------------------------------------------
  670. // Purpose:
  671. // Input : file -
  672. // fIsStoring -
  673. // Output : int
  674. //-----------------------------------------------------------------------------
  675. int CEditGameClass::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  676. {
  677. int iRvl;
  678. if (!fIsStoring)
  679. {
  680. // loading
  681. if (PeekChar(file) == '\"')
  682. {
  683. // read kv pairs
  684. MDkeyvalue newkv;
  685. while (1)
  686. {
  687. if (newkv.SerializeMAP(file, fIsStoring) != fileOk)
  688. {
  689. // fileDone means the keyvalue was not loaded
  690. break;
  691. }
  692. if (!strcmp(newkv.szKey, "classname"))
  693. {
  694. m_KeyValues.SetValue(newkv.szKey, newkv.szValue);
  695. }
  696. else if (!strcmp(newkv.szKey, "angle"))
  697. {
  698. ImportAngle(atoi(newkv.szValue));
  699. }
  700. else if (strcmp(newkv.szKey, "wad"))
  701. {
  702. //
  703. // All other keys are simply added to the keyvalue list.
  704. //
  705. m_KeyValues.SetValue(newkv.szKey, newkv.szValue);
  706. }
  707. }
  708. }
  709. }
  710. else
  711. {
  712. // save keyvalues
  713. MDkeyvalue tmpkv;
  714. if (GetKeyValue("classname") == NULL)
  715. {
  716. tmpkv.Set("classname", m_szClass);
  717. tmpkv.SerializeMAP(file, fIsStoring);
  718. }
  719. //
  720. // Determine whether we have a game data class. This will help us decide which keys
  721. // to write.
  722. //
  723. GDclass *pGameDataClass = NULL;
  724. if (pGD != NULL)
  725. {
  726. pGameDataClass = pGD->ClassForName(m_szClass);
  727. }
  728. //
  729. // Consider all the keyvalues in this object for serialization.
  730. //
  731. for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
  732. {
  733. MDkeyvalue &KeyValue = m_KeyValues.GetKeyValue(z);
  734. iRvl = KeyValue.SerializeMAP(file, fIsStoring);
  735. if (iRvl != fileOk)
  736. {
  737. return(iRvl);
  738. }
  739. }
  740. //
  741. // If we have a base class, for each keyvalue in the class definition, write out all keys
  742. // that are not present in the object and whose defaults are nonzero in the class definition.
  743. //
  744. if (pGameDataClass != NULL)
  745. {
  746. //
  747. // For each variable from the base class...
  748. //
  749. int nVariableCount = pGameDataClass->GetVariableCount();
  750. for (int i = 0; i < nVariableCount; i++)
  751. {
  752. GDinputvariable *pVar = pGameDataClass->GetVariableAt(i);
  753. Assert(pVar != NULL);
  754. if (pVar != NULL)
  755. {
  756. int iIndex;
  757. MDkeyvalue *pKey;
  758. LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex);
  759. //
  760. // If the variable is not present in this object, write out the default value.
  761. //
  762. if (p == NULL)
  763. {
  764. pKey = &tmpkv;
  765. pVar->ResetDefaults();
  766. pVar->ToKeyValue(pKey);
  767. //
  768. // Only write the key value if it is non-zero.
  769. //
  770. if ((pKey->szKey[0] != 0) && (pKey->szValue[0] != 0) && (stricmp(pKey->szValue, "0")))
  771. {
  772. iRvl = pKey->SerializeMAP(file, fIsStoring);
  773. if (iRvl != fileOk)
  774. {
  775. return(iRvl);
  776. }
  777. }
  778. }
  779. }
  780. }
  781. }
  782. }
  783. return(file.fail() ? fileOsError : fileOk);
  784. }
  785. //-----------------------------------------------------------------------------
  786. // Purpose:
  787. // Input : file -
  788. // fIsStoring -
  789. // pIntersecting -
  790. // Output : int
  791. //-----------------------------------------------------------------------------
  792. int CMapWorld::SerializeMAP(std::fstream &file, BOOL fIsStoring, BoundBox *pIntersecting)
  793. {
  794. int iRvl;
  795. bStuffed = FALSE;
  796. bErrors = FALSE;
  797. nInvalidSolids = 0;
  798. // load/save a world
  799. if (fIsStoring)
  800. {
  801. file << "{" << ENDLINE;
  802. // save worldobject
  803. CEditGameClass::SerializeMAP(file, fIsStoring);
  804. if (MapFormat != mfQuake2)
  805. {
  806. MDkeyvalue tmpkv;
  807. strcpy(tmpkv.szKey, "mapversion");
  808. strcpy(tmpkv.szValue, "360");
  809. tmpkv.SerializeMAP(file, fIsStoring);
  810. // Save wad file line
  811. strcpy(tmpkv.szKey, "wad");
  812. // copy all texfiles into value
  813. tmpkv.szValue[0] = 0;
  814. BOOL bFirst = TRUE;
  815. int nGraphicsFiles = g_Textures.FilesGetCount();
  816. for (int i = 0; i < nGraphicsFiles; i++)
  817. {
  818. char szFile[MAX_PATH];
  819. GRAPHICSFILESTRUCT gf;
  820. g_Textures.FilesGetInfo(&gf, i);
  821. //
  822. // Don't save WAL files - they're never used.
  823. //
  824. if (gf.format != tfWAL)
  825. {
  826. //
  827. // Also make sure this is the right kind of WAD file
  828. // based on the game we're using.
  829. //
  830. if (gf.format == g_pGameConfig->GetTextureFormat())
  831. {
  832. //
  833. // Append this WAD file to the WAD list.
  834. //
  835. strcpy(szFile, gf.filename);
  836. // dvs: Strip off the path. This crashes VIS and QRAD!!
  837. /*
  838. char *pszSlash = strrchr(szFile, '\\');
  839. if (pszSlash == NULL)
  840. {
  841. pszSlash = strrchr(szFile, '/');
  842. }
  843. if (pszSlash != NULL)
  844. {
  845. pszSlash++;
  846. }
  847. else
  848. {
  849. pszSlash = szFile;
  850. }
  851. */
  852. char *pszSlash = szFile;
  853. // Strip off any drive letter.
  854. if (pszSlash[1] == ':')
  855. {
  856. pszSlash += 2;
  857. }
  858. // WAD names are semicolon delimited.
  859. if (!bFirst)
  860. {
  861. strcat(tmpkv.szValue, ";");
  862. }
  863. strcat(tmpkv.szValue, pszSlash);
  864. bFirst = FALSE;
  865. }
  866. }
  867. }
  868. if ( tmpkv.szValue[0] != '\0' )
  869. {
  870. tmpkv.SerializeMAP(file, fIsStoring);
  871. }
  872. }
  873. //
  874. // Save the brushes.
  875. //
  876. if (SaveSolidChildrenOf(this, file, pIntersecting) == fileOsError)
  877. {
  878. goto FatalError;
  879. }
  880. file << "}" << ENDLINE;
  881. //
  882. // Save the entities.
  883. //
  884. if (SaveEntityChildrenOf(this, file, pIntersecting) == fileOsError)
  885. {
  886. goto FatalError;
  887. }
  888. //
  889. // Save paths (if paths are visible).
  890. //
  891. FOR_EACH_OBJ( m_Paths, pos )
  892. {
  893. CMapPath *pPath = m_Paths.Element(pos);
  894. pPath->SerializeMAP(file, TRUE, pIntersecting);
  895. }
  896. }
  897. else
  898. {
  899. pProgDlg = new CProgressDlg;
  900. pProgDlg->Create();
  901. pProgDlg->SetStep(1);
  902. CString caption;
  903. caption.LoadString(IDS_LOADINGFILE);
  904. pProgDlg->SetWindowText(caption);
  905. m_Render2DBox.ResetBounds();
  906. // load world
  907. GetLine(file, NULL); // ignore delimiter
  908. CEditGameClass::SerializeMAP(file, fIsStoring);
  909. const char* pszMapVersion;
  910. pszMapVersion = m_KeyValues.GetValue("mapversion");
  911. if (pszMapVersion != NULL)
  912. {
  913. uMapVersion = atoi(pszMapVersion);
  914. }
  915. else
  916. {
  917. uMapVersion = 0;
  918. }
  919. // read solids
  920. if (ReadSolids(this, file) == fileOsError)
  921. {
  922. goto FatalError;
  923. }
  924. // skip end-of-entity marker
  925. GetLine(file, NULL);
  926. char szBuf[128];
  927. // read entities
  928. while (1)
  929. {
  930. GetLine(file, szBuf);
  931. if (szBuf[0] != '{')
  932. {
  933. StuffLine(szBuf);
  934. break;
  935. }
  936. if (PeekChar(file) == EOF)
  937. {
  938. break;
  939. }
  940. CMapEntity *pEntity;
  941. pEntity = new CMapEntity;
  942. iRvl = pEntity->SerializeMAP(file, fIsStoring);
  943. AddChild(pEntity);
  944. if (iRvl == fileError)
  945. {
  946. bErrors = TRUE;
  947. }
  948. else if (iRvl == fileOsError)
  949. {
  950. goto FatalError;
  951. }
  952. }
  953. if (bErrors)
  954. {
  955. if (nInvalidSolids)
  956. {
  957. CString str;
  958. str.Format("For your information, %d solids were not loaded\n"
  959. "due to errors in the file.", nInvalidSolids);
  960. AfxMessageBox(str);
  961. }
  962. else if (AfxMessageBox("There was a problem loading the MAP file. Do you\n"
  963. "want to view the error report?", MB_YESNO) == IDYES)
  964. {
  965. CMapErrorsDlg dlg;
  966. dlg.DoModal();
  967. }
  968. }
  969. PostloadWorld();
  970. if (g_pGameConfig->GetTextureFormat() == tfVMT)
  971. {
  972. // do batch search and replace of textures from trans.txt if it exists.
  973. char translationFilename[MAX_PATH];
  974. Q_snprintf( translationFilename, sizeof( translationFilename ), "materials/trans.txt" );
  975. if( CMapDoc::GetActiveMapDoc() )
  976. {
  977. FileHandle_t searchReplaceFP = g_pFileSystem->Open( translationFilename, "r" );
  978. if( searchReplaceFP )
  979. {
  980. CMapDoc::GetActiveMapDoc()->BatchReplaceTextures( searchReplaceFP );
  981. g_pFileSystem->Close( searchReplaceFP );
  982. }
  983. }
  984. }
  985. }
  986. if (pProgDlg)
  987. {
  988. pProgDlg->DestroyWindow();
  989. delete pProgDlg;
  990. pProgDlg = NULL;
  991. }
  992. return (bErrors && fIsStoring) ? -1 : 0;
  993. FatalError:
  994. // OS error.
  995. CString str;
  996. str.Format("The OS reported an error %s the file: %s", fIsStoring ? "saving" : "loading", strerror(errno));
  997. AfxMessageBox(str);
  998. if (pProgDlg != NULL)
  999. {
  1000. pProgDlg->DestroyWindow();
  1001. delete pProgDlg;
  1002. pProgDlg = NULL;
  1003. }
  1004. return -1;
  1005. }