Team Fortress 2 Source Code as on 22/4/2020
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.

1200 lines
26 KiB

  1. //========= Copyright 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. V_strcpy_safe(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 = 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 = 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] = V_rint(plane.planepts[nPlane][0]);
  316. plane.planepts[nPlane][1] = V_rint(plane.planepts[nPlane][1]);
  317. plane.planepts[nPlane][2] = V_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. COMPILE_TIME_ASSERT( ARRAYSIZE(szTexName) == 128 );
  363. nRead = sscanf(szBuf,
  364. "( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
  365. "%127s "
  366. "[ %f %f %f %f ] "
  367. "[ %f %f %f %f ] "
  368. "%f %f %f "
  369. "%u %u %u",
  370. &plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
  371. &plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
  372. &plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
  373. szTexName,
  374. &texture.UAxis[0], &texture.UAxis[1], &texture.UAxis[2], &texture.UAxis[3],
  375. &texture.VAxis[0], &texture.VAxis[1], &texture.VAxis[2], &texture.VAxis[3],
  376. &texture.rotate,
  377. &texture.scale[0],
  378. &texture.scale[1],
  379. &q2contents,
  380. &q2surface,
  381. &nLightmapScale);
  382. // Guarantee null-termination to keep /analyze happy.
  383. szTexName[ ARRAYSIZE(szTexName) - 1 ] = 0;
  384. if (nRead < 21)
  385. {
  386. bErrors = TRUE;
  387. }
  388. else if (nRead == 24)
  389. {
  390. // got q2 values - set them here
  391. texture.q2contents = q2contents;
  392. texture.q2surface = q2surface;
  393. texture.nLightmapScale = nLightmapScale;
  394. if (texture.nLightmapScale == 0)
  395. {
  396. texture.nLightmapScale = g_pGameConfig->GetDefaultLightmapScale();
  397. }
  398. }
  399. //
  400. // very cheesy HACK!!! -- this will be better when we have chunks
  401. //
  402. if( uMapVersion <= 350 )
  403. {
  404. if( ( file.peek() != '(' ) && ( file.peek() != '}' ) )
  405. {
  406. EditDispHandle_t handle = EditDispMgr()->Create();
  407. SetDisp( handle );
  408. CMapDisp *pDisp = EditDispMgr()->GetDisp( handle );
  409. pDisp->SerializedLoadMAP( file, this, uMapVersion );
  410. }
  411. }
  412. }
  413. else if (uMapVersion >= 220 )
  414. {
  415. COMPILE_TIME_ASSERT( ARRAYSIZE(szTexName) == 128 );
  416. nRead = sscanf(szBuf,
  417. "( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
  418. "%127s "
  419. "[ %f %f %f %f ] "
  420. "[ %f %f %f %f ] "
  421. "%f %f %f "
  422. "%u %u %u",
  423. &plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
  424. &plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
  425. &plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
  426. szTexName,
  427. &texture.UAxis[0], &texture.UAxis[1], &texture.UAxis[2], &texture.UAxis[3],
  428. &texture.VAxis[0], &texture.VAxis[1], &texture.VAxis[2], &texture.VAxis[3],
  429. &texture.rotate,
  430. &texture.scale[0],
  431. &texture.scale[1],
  432. &q2contents,
  433. &q2surface,
  434. &nDummy); // Pre-340 didn't have lightmap scale.
  435. // Guarantee null-termination to keep /analyze happy.
  436. szTexName[ ARRAYSIZE(szTexName) - 1 ] = 0;
  437. if (nRead < 21)
  438. {
  439. bErrors = TRUE;
  440. }
  441. else if (nRead == 24)
  442. {
  443. // got q2 values - set them here
  444. texture.q2contents = q2contents;
  445. texture.q2surface = q2surface;
  446. }
  447. }
  448. else
  449. {
  450. COMPILE_TIME_ASSERT( ARRAYSIZE(szTexName) == 128 );
  451. nRead = sscanf(szBuf,
  452. "( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
  453. "%127s "
  454. "%f %f %f "
  455. "%f %f %u %u %u",
  456. &plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
  457. &plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
  458. &plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
  459. szTexName,
  460. &texture.UAxis[3],
  461. &texture.VAxis[3],
  462. &texture.rotate,
  463. &texture.scale[0],
  464. &texture.scale[1],
  465. &q2contents,
  466. &q2surface,
  467. &nDummy); // Pre-340 didn't have lightmap scale.
  468. // Guarantee null-termination to keep /analyze happy.
  469. szTexName[ ARRAYSIZE(szTexName) - 1 ] = 0;
  470. if (nRead < 15)
  471. {
  472. bErrors = TRUE;
  473. }
  474. else if (nRead == 18)
  475. {
  476. // got q2 values - set them here
  477. texture.q2contents = q2contents;
  478. texture.q2surface = q2surface;
  479. }
  480. }
  481. if (g_pGameConfig->GetTextureFormat() != tfVMT)
  482. {
  483. // reverse the slashes -- thank you id
  484. for (int i = strlen(szTexName) - 1; i >= 0; i--)
  485. {
  486. if (szTexName[i] == '/')
  487. szTexName[i] = '\\';
  488. }
  489. }
  490. SetTexture(szTexName);
  491. }
  492. if (file.fail())
  493. {
  494. return fileOsError;
  495. }
  496. return fileOk;
  497. }
  498. //-----------------------------------------------------------------------------
  499. // Purpose:
  500. // Input : file -
  501. // fIsStoring -
  502. // Output : int
  503. //-----------------------------------------------------------------------------
  504. int MDkeyvalue::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  505. {
  506. // load/save a keyvalue
  507. char szBuf[1024];
  508. if(fIsStoring)
  509. {
  510. // save a keyvalue
  511. sprintf( szBuf,
  512. "\"%s\" \"%s\"",
  513. Key(), Value() );
  514. file << szBuf << ENDLINE;
  515. }
  516. else
  517. {
  518. GetLine(file, szBuf);
  519. if(szBuf[0] != '\"')
  520. {
  521. StuffLine(szBuf);
  522. return fileDone;
  523. }
  524. char *p = strchr(szBuf, '\"');
  525. p = strchr(p+1, '\"');
  526. if(!p)
  527. return fileError;
  528. p[0] = 0;
  529. V_strcpy_safe(szKey, szBuf+1);
  530. // advance to start of value string
  531. p = strchr(p+1, '\"');
  532. if(!p)
  533. return fileError;
  534. // ocpy in value
  535. strcpy(szValue, p+1);
  536. // kill trailing "
  537. p = strchr(szValue, '\"');
  538. if(!p)
  539. return fileError;
  540. p[0] = 0;
  541. }
  542. return file.fail() ? fileOsError : fileOk;
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Purpose:
  546. // Input : file -
  547. // fIsStoring -
  548. // Output : int
  549. //-----------------------------------------------------------------------------
  550. int CMapSolid::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  551. {
  552. CMapClass::SerializeMAP(file, fIsStoring);
  553. // load/save a brush
  554. if (fIsStoring)
  555. {
  556. // save the brush
  557. file << "{" << ENDLINE;
  558. // serialize the Faces
  559. int nFaces = Faces.GetCount();
  560. for(int i = 0; i < nFaces; i++)
  561. {
  562. if(!Faces[i].Points)
  563. continue;
  564. if(Faces[i].SerializeMAP(file, fIsStoring) == fileError)
  565. return fileError;
  566. }
  567. // terminator
  568. file << "}" << ENDLINE;
  569. }
  570. else
  571. {
  572. // caller skipped delimiter
  573. Faces.SetCount(0);
  574. // read Faces
  575. for(int i = 0; ; i++)
  576. {
  577. // extract plane
  578. if (Faces[i].SerializeMAP(file, fIsStoring) == fileDone)
  579. {
  580. // when fileDone is returned, no face was loaded
  581. break;
  582. }
  583. Faces[i].CalcPlane();
  584. }
  585. GetLine(file, NULL); // ignore line
  586. if (!file.fail())
  587. {
  588. //
  589. // Create the solid using the planes that were read from the MAP file.
  590. //
  591. if (CreateFromPlanes() == FALSE)
  592. {
  593. bErrors = TRUE;
  594. return(fileError);
  595. }
  596. //
  597. // If we are reading from an old map file, the texture axes will need to be set up.
  598. // Leave the rotation and shifts alone; they were read from the MAP file.
  599. //
  600. if (uMapVersion < 220)
  601. {
  602. InitializeTextureAxes(TEXTURE_ALIGN_QUAKE, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
  603. }
  604. }
  605. else
  606. {
  607. return(fileOsError);
  608. }
  609. CalcBounds();
  610. //
  611. // Set solid type based on texture name.
  612. //
  613. m_eSolidType = HL1SolidTypeFromTextureName(Faces[0].texture.texture);
  614. }
  615. return(file.fail() ? fileOsError : fileOk);
  616. }
  617. //-----------------------------------------------------------------------------
  618. // Purpose:
  619. // Input : file -
  620. // fIsStoring -
  621. // Output : int
  622. //-----------------------------------------------------------------------------
  623. int CMapEntity::SerializeMAP(std::fstream &file, BOOL fIsStoring)
  624. {
  625. CMapClass::SerializeMAP(file, fIsStoring);
  626. // load/save an object
  627. if (fIsStoring)
  628. {
  629. //
  630. // If it's a solidentity but it doesn't have any solids,
  631. // don't save it.
  632. //
  633. if (!IsPlaceholder() && !m_Children.Count())
  634. {
  635. return(fileOk);
  636. }
  637. //
  638. // Save it.
  639. //
  640. file << "{" << ENDLINE;
  641. //
  642. // Save keyvalues & other data.
  643. //
  644. CEditGameClass::SerializeMAP(file, fIsStoring);
  645. //
  646. // If this is a placeholder and either has no class or is not a solid class,
  647. // save our origin.
  648. //
  649. if (IsPlaceholder() && (!IsClass() || !IsSolidClass()))
  650. {
  651. MDkeyvalue tmpkv;
  652. strcpy(tmpkv.szKey, "origin");
  653. Vector Origin;
  654. GetOrigin(Origin);
  655. sprintf(tmpkv.szValue, "%.0f %.0f %.0f", Origin[0], Origin[1], Origin[2]);
  656. tmpkv.SerializeMAP(file, fIsStoring);
  657. }
  658. if (!(IsPlaceholder()))
  659. {
  660. SaveSolidChildrenOf(this, file);
  661. }
  662. file << "}" << ENDLINE;
  663. }
  664. else
  665. {
  666. // load keyvalues
  667. CEditGameClass::SerializeMAP(file, fIsStoring);
  668. // solids
  669. if (!ReadSolids(this, file))
  670. {
  671. flags |= flagPlaceholder;
  672. }
  673. // skip delimiter
  674. GetLine(file, NULL);
  675. }
  676. return file.fail() ? fileOsError : fileOk;
  677. }
  678. //-----------------------------------------------------------------------------
  679. // Purpose:
  680. // Input : file -
  681. // fIsStoring -
  682. // Output : int
  683. //-----------------------------------------------------------------------------
  684. int CEditGameClass::SerializeMAP(std::fstream& file, BOOL fIsStoring)
  685. {
  686. int iRvl;
  687. if (!fIsStoring)
  688. {
  689. // loading
  690. if (PeekChar(file) == '\"')
  691. {
  692. // read kv pairs
  693. MDkeyvalue newkv;
  694. while (1)
  695. {
  696. if (newkv.SerializeMAP(file, fIsStoring) != fileOk)
  697. {
  698. // fileDone means the keyvalue was not loaded
  699. break;
  700. }
  701. if (!strcmp(newkv.szKey, "classname"))
  702. {
  703. m_KeyValues.SetValue(newkv.szKey, newkv.szValue);
  704. }
  705. else if (!strcmp(newkv.szKey, "angle"))
  706. {
  707. ImportAngle(atoi(newkv.szValue));
  708. }
  709. else if (strcmp(newkv.szKey, "wad"))
  710. {
  711. //
  712. // All other keys are simply added to the keyvalue list.
  713. //
  714. m_KeyValues.SetValue(newkv.szKey, newkv.szValue);
  715. }
  716. }
  717. }
  718. }
  719. else
  720. {
  721. // save keyvalues
  722. MDkeyvalue tmpkv;
  723. if (GetKeyValue("classname") == NULL)
  724. {
  725. tmpkv.Set("classname", m_szClass);
  726. tmpkv.SerializeMAP(file, fIsStoring);
  727. }
  728. //
  729. // Determine whether we have a game data class. This will help us decide which keys
  730. // to write.
  731. //
  732. GDclass *pGameDataClass = NULL;
  733. if (pGD != NULL)
  734. {
  735. pGameDataClass = pGD->ClassForName(m_szClass);
  736. }
  737. //
  738. // Consider all the keyvalues in this object for serialization.
  739. //
  740. for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
  741. {
  742. MDkeyvalue &KeyValue = m_KeyValues.GetKeyValue(z);
  743. iRvl = KeyValue.SerializeMAP(file, fIsStoring);
  744. if (iRvl != fileOk)
  745. {
  746. return(iRvl);
  747. }
  748. }
  749. //
  750. // If we have a base class, for each keyvalue in the class definition, write out all keys
  751. // that are not present in the object and whose defaults are nonzero in the class definition.
  752. //
  753. if (pGameDataClass != NULL)
  754. {
  755. //
  756. // For each variable from the base class...
  757. //
  758. int nVariableCount = pGameDataClass->GetVariableCount();
  759. for (int i = 0; i < nVariableCount; i++)
  760. {
  761. GDinputvariable *pVar = pGameDataClass->GetVariableAt(i);
  762. Assert(pVar != NULL);
  763. if (pVar != NULL)
  764. {
  765. int iIndex;
  766. MDkeyvalue *pKey;
  767. LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex);
  768. //
  769. // If the variable is not present in this object, write out the default value.
  770. //
  771. if (p == NULL)
  772. {
  773. pKey = &tmpkv;
  774. pVar->ResetDefaults();
  775. pVar->ToKeyValue(pKey);
  776. //
  777. // Only write the key value if it is non-zero.
  778. //
  779. if ((pKey->szKey[0] != 0) && (pKey->szValue[0] != 0) && (stricmp(pKey->szValue, "0")))
  780. {
  781. iRvl = pKey->SerializeMAP(file, fIsStoring);
  782. if (iRvl != fileOk)
  783. {
  784. return(iRvl);
  785. }
  786. }
  787. }
  788. }
  789. }
  790. }
  791. }
  792. return(file.fail() ? fileOsError : fileOk);
  793. }
  794. //-----------------------------------------------------------------------------
  795. // Purpose:
  796. // Input : file -
  797. // fIsStoring -
  798. // pIntersecting -
  799. // Output : int
  800. //-----------------------------------------------------------------------------
  801. int CMapWorld::SerializeMAP(std::fstream &file, BOOL fIsStoring, BoundBox *pIntersecting)
  802. {
  803. int iRvl;
  804. bStuffed = FALSE;
  805. bErrors = FALSE;
  806. nInvalidSolids = 0;
  807. // load/save a world
  808. if (fIsStoring)
  809. {
  810. file << "{" << ENDLINE;
  811. // save worldobject
  812. CEditGameClass::SerializeMAP(file, fIsStoring);
  813. if (MapFormat != mfQuake2)
  814. {
  815. MDkeyvalue tmpkv;
  816. strcpy(tmpkv.szKey, "mapversion");
  817. strcpy(tmpkv.szValue, "360");
  818. tmpkv.SerializeMAP(file, fIsStoring);
  819. // Save wad file line
  820. strcpy(tmpkv.szKey, "wad");
  821. // copy all texfiles into value
  822. tmpkv.szValue[0] = 0;
  823. BOOL bFirst = TRUE;
  824. int nGraphicsFiles = g_Textures.FilesGetCount();
  825. for (int i = 0; i < nGraphicsFiles; i++)
  826. {
  827. char szFile[MAX_PATH];
  828. GRAPHICSFILESTRUCT gf;
  829. g_Textures.FilesGetInfo(&gf, i);
  830. //
  831. // Don't save WAL files - they're never used.
  832. //
  833. if (gf.format != tfWAL)
  834. {
  835. //
  836. // Also make sure this is the right kind of WAD file
  837. // based on the game we're using.
  838. //
  839. if (gf.format == g_pGameConfig->GetTextureFormat())
  840. {
  841. //
  842. // Append this WAD file to the WAD list.
  843. //
  844. strcpy(szFile, gf.filename);
  845. // dvs: Strip off the path. This crashes VIS and QRAD!!
  846. /*
  847. char *pszSlash = strrchr(szFile, '\\');
  848. if (pszSlash == NULL)
  849. {
  850. pszSlash = strrchr(szFile, '/');
  851. }
  852. if (pszSlash != NULL)
  853. {
  854. pszSlash++;
  855. }
  856. else
  857. {
  858. pszSlash = szFile;
  859. }
  860. */
  861. char *pszSlash = szFile;
  862. // Strip off any drive letter.
  863. if (pszSlash[1] == ':')
  864. {
  865. pszSlash += 2;
  866. }
  867. // WAD names are semicolon delimited.
  868. if (!bFirst)
  869. {
  870. strcat(tmpkv.szValue, ";");
  871. }
  872. strcat(tmpkv.szValue, pszSlash);
  873. bFirst = FALSE;
  874. }
  875. }
  876. }
  877. if ( tmpkv.szValue[0] != '\0' )
  878. {
  879. tmpkv.SerializeMAP(file, fIsStoring);
  880. }
  881. }
  882. //
  883. // Save the brushes.
  884. //
  885. if (SaveSolidChildrenOf(this, file, pIntersecting) == fileOsError)
  886. {
  887. goto FatalError;
  888. }
  889. file << "}" << ENDLINE;
  890. //
  891. // Save the entities.
  892. //
  893. if (SaveEntityChildrenOf(this, file, pIntersecting) == fileOsError)
  894. {
  895. goto FatalError;
  896. }
  897. //
  898. // Save paths (if paths are visible).
  899. //
  900. FOR_EACH_OBJ( m_Paths, pos )
  901. {
  902. CMapPath *pPath = m_Paths.Element(pos);
  903. pPath->SerializeMAP(file, TRUE, pIntersecting);
  904. }
  905. }
  906. else
  907. {
  908. pProgDlg = new CProgressDlg;
  909. pProgDlg->Create();
  910. pProgDlg->SetStep(1);
  911. CString caption;
  912. caption.LoadString(IDS_LOADINGFILE);
  913. pProgDlg->SetWindowText(caption);
  914. m_Render2DBox.ResetBounds();
  915. // load world
  916. GetLine(file, NULL); // ignore delimiter
  917. CEditGameClass::SerializeMAP(file, fIsStoring);
  918. const char* pszMapVersion;
  919. pszMapVersion = m_KeyValues.GetValue("mapversion");
  920. if (pszMapVersion != NULL)
  921. {
  922. uMapVersion = atoi(pszMapVersion);
  923. }
  924. else
  925. {
  926. uMapVersion = 0;
  927. }
  928. // read solids
  929. if (ReadSolids(this, file) == fileOsError)
  930. {
  931. goto FatalError;
  932. }
  933. // skip end-of-entity marker
  934. GetLine(file, NULL);
  935. char szBuf[128];
  936. // read entities
  937. while (1)
  938. {
  939. GetLine(file, szBuf);
  940. if (szBuf[0] != '{')
  941. {
  942. StuffLine(szBuf);
  943. break;
  944. }
  945. if (PeekChar(file) == EOF)
  946. {
  947. break;
  948. }
  949. CMapEntity *pEntity;
  950. pEntity = new CMapEntity;
  951. iRvl = pEntity->SerializeMAP(file, fIsStoring);
  952. AddChild(pEntity);
  953. if (iRvl == fileError)
  954. {
  955. bErrors = TRUE;
  956. }
  957. else if (iRvl == fileOsError)
  958. {
  959. goto FatalError;
  960. }
  961. }
  962. if (bErrors)
  963. {
  964. if (nInvalidSolids)
  965. {
  966. CString str;
  967. str.Format("For your information, %d solids were not loaded\n"
  968. "due to errors in the file.", nInvalidSolids);
  969. AfxMessageBox(str);
  970. }
  971. else if (AfxMessageBox("There was a problem loading the MAP file. Do you\n"
  972. "want to view the error report?", MB_YESNO) == IDYES)
  973. {
  974. CMapErrorsDlg dlg;
  975. dlg.DoModal();
  976. }
  977. }
  978. PostloadWorld();
  979. if (g_pGameConfig->GetTextureFormat() == tfVMT)
  980. {
  981. // do batch search and replace of textures from trans.txt if it exists.
  982. char translationFilename[MAX_PATH];
  983. Q_snprintf( translationFilename, sizeof( translationFilename ), "materials/trans.txt" );
  984. if( CMapDoc::GetActiveMapDoc() )
  985. {
  986. FileHandle_t searchReplaceFP = g_pFileSystem->Open( translationFilename, "r" );
  987. if( searchReplaceFP )
  988. {
  989. CMapDoc::GetActiveMapDoc()->BatchReplaceTextures( searchReplaceFP );
  990. g_pFileSystem->Close( searchReplaceFP );
  991. }
  992. }
  993. }
  994. }
  995. if (pProgDlg)
  996. {
  997. pProgDlg->DestroyWindow();
  998. delete pProgDlg;
  999. pProgDlg = NULL;
  1000. }
  1001. return (bErrors && fIsStoring) ? -1 : 0;
  1002. FatalError:
  1003. // OS error.
  1004. CString str;
  1005. str.Format("The OS reported an error %s the file: %s", fIsStoring ? "saving" : "loading", strerror(errno));
  1006. AfxMessageBox(str);
  1007. if (pProgDlg != NULL)
  1008. {
  1009. pProgDlg->DestroyWindow();
  1010. delete pProgDlg;
  1011. pProgDlg = NULL;
  1012. }
  1013. return -1;
  1014. }