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.

926 lines
20 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 "MapEntity.h"
  11. #include "MapFace.h"
  12. #include "MapSolid.h"
  13. #include "MapStudioModel.h"
  14. #include "MapWorld.h"
  15. #include "GlobalFunctions.h"
  16. #include "VisGroup.h"
  17. #include "MapDoc.h"
  18. #include "MapDisp.h"
  19. // memdbgon must be the last include file in a .cpp file!!!
  20. #include <tier0/memdbgon.h>
  21. static CMapWorld *pLoadingWorld;
  22. static float fThisVersion;
  23. static BOOL bCorrupt;
  24. class COldVisGroup
  25. {
  26. public:
  27. char m_szName[128];
  28. color32 m_rgbColor;
  29. DWORD m_dwID;
  30. bool m_bVisible;
  31. };
  32. float GetFileVersion() { return fThisVersion; }
  33. static void WriteString(std::fstream& file, LPCTSTR pszString)
  34. {
  35. BYTE cLen = strlen(pszString)+1;
  36. file.write((char*)&cLen, 1);
  37. file.write(pszString, cLen);
  38. }
  39. static void ReadString(std::fstream& file, char * pszString)
  40. {
  41. BYTE cLen;
  42. file.read((char *)&cLen, 1);
  43. file.read(pszString, cLen);
  44. }
  45. //-----------------------------------------------------------------------------
  46. // Purpose: Loads a solid face from RMF.
  47. //-----------------------------------------------------------------------------
  48. int CMapFace::SerializeRMF(std::fstream& file, BOOL fIsStoring)
  49. {
  50. int iSize;
  51. if (fIsStoring)
  52. {
  53. //
  54. // After 3.3 the alignment of vec4_t's changed. We never save the new format,
  55. // since RMF is no longer being revved.
  56. //
  57. TEXTURE_33 OldTex33;
  58. memset(&OldTex33, 0, sizeof(OldTex33));
  59. memcpy(OldTex33.texture, texture.texture, sizeof(OldTex33.texture));
  60. OldTex33.UAxis[0] = texture.UAxis[0];
  61. OldTex33.UAxis[1] = texture.UAxis[1];
  62. OldTex33.UAxis[2] = texture.UAxis[2];
  63. OldTex33.UAxis[3] = texture.UAxis[3];
  64. OldTex33.VAxis[0] = texture.VAxis[0];
  65. OldTex33.VAxis[1] = texture.VAxis[1];
  66. OldTex33.VAxis[2] = texture.VAxis[2];
  67. OldTex33.VAxis[3] = texture.VAxis[3];
  68. OldTex33.rotate = texture.rotate;
  69. OldTex33.scale[0] = texture.scale[0];
  70. OldTex33.scale[1] = texture.scale[1];
  71. OldTex33.smooth = texture.smooth;
  72. OldTex33.material = texture.material;
  73. OldTex33.q2surface = texture.q2surface;
  74. OldTex33.q2contents = texture.q2contents;
  75. OldTex33.nLightmapScale = texture.nLightmapScale;
  76. file.write((char *)&OldTex33, sizeof(OldTex33));
  77. iSize = nPoints;
  78. file.write((char *)&iSize, sizeof(int));
  79. //
  80. // Save face points. We don't serialize the Vectors directly because the memory
  81. // layout changed with SSE optimizations.
  82. //
  83. float SavePoints[256][3];
  84. for (int i = 0; i < iSize; i++)
  85. {
  86. SavePoints[i][0] = Points[i].x;
  87. SavePoints[i][1] = Points[i].y;
  88. SavePoints[i][2] = Points[i].z;
  89. }
  90. file.write((char *)SavePoints, nPoints * 3 * sizeof(float));
  91. //
  92. // Save plane points. We don't serialize the Vectors directly because the memory
  93. // layout changed with SSE optimizations.
  94. //
  95. for (int i = 0; i < 3; i++)
  96. {
  97. SavePoints[i][0] = plane.planepts[i].x;
  98. SavePoints[i][1] = plane.planepts[i].y;
  99. SavePoints[i][2] = plane.planepts[i].z;
  100. }
  101. file.write((char *)SavePoints, 3 * 3 * sizeof(float));
  102. }
  103. else
  104. {
  105. // Pre-2.2 used a different texture structure format.
  106. TEXTURE_21 OldTex;
  107. memset(&OldTex, 0, sizeof(OldTex));
  108. if (fThisVersion < 0.9f)
  109. {
  110. // Read the name
  111. file.read(OldTex.texture, 16);
  112. // Ensure name is ASCIIZ
  113. OldTex.texture[16] = 0;
  114. // Read the rest - skip the name
  115. file.read((char *)&OldTex.rotate, sizeof(OldTex.rotate) + sizeof(OldTex.shift) + sizeof(OldTex.scale));
  116. }
  117. else if (fThisVersion < 1.2f)
  118. {
  119. // Didn't have smooth/material groups:
  120. file.read((char *)&OldTex, 40);
  121. file.read((char *)&OldTex, sizeof(OldTex.texture) - (MAX_PATH) + sizeof(OldTex.rotate) + sizeof(OldTex.shift) + sizeof(OldTex.scale));
  122. }
  123. else if (fThisVersion < 1.7f)
  124. {
  125. // No quake2 fields yet and smaller texture size.
  126. file.read((char *)&OldTex, 40);
  127. file.read((char *)&OldTex.rotate, sizeof(OldTex) - (sizeof(int) * 3) - MAX_PATH);
  128. }
  129. else if (fThisVersion < 1.8f)
  130. {
  131. // Texture name field changed from 40 to MAX_PATH in size.
  132. file.read((char *)&OldTex, 40);
  133. file.read((char *)&OldTex.rotate, sizeof(OldTex) - MAX_PATH);
  134. }
  135. else if (fThisVersion < 2.2f)
  136. {
  137. file.read((char *)&OldTex, sizeof(OldTex));
  138. }
  139. else
  140. {
  141. //
  142. // After 3.3 the alignment of vec4_t's changed. We never save the new format,
  143. // since RMF is no longer being revved.
  144. //
  145. TEXTURE_33 OldTex33;
  146. memset(&OldTex33, 0, sizeof(OldTex33));
  147. file.read((char *)&OldTex33, sizeof(OldTex33));
  148. memcpy(texture.texture, OldTex33.texture, sizeof(texture.texture));
  149. texture.UAxis[0] = OldTex33.UAxis[0];
  150. texture.UAxis[1] = OldTex33.UAxis[1];
  151. texture.UAxis[2] = OldTex33.UAxis[2];
  152. texture.UAxis[3] = OldTex33.UAxis[3];
  153. texture.VAxis[0] = OldTex33.VAxis[0];
  154. texture.VAxis[1] = OldTex33.VAxis[1];
  155. texture.VAxis[2] = OldTex33.VAxis[2];
  156. texture.VAxis[3] = OldTex33.VAxis[3];
  157. texture.rotate = OldTex33.rotate;
  158. texture.scale[0] = OldTex33.scale[0];
  159. texture.scale[1] = OldTex33.scale[1];
  160. texture.smooth = OldTex33.smooth;
  161. texture.material = OldTex33.material;
  162. texture.q2surface = OldTex33.q2surface;
  163. texture.q2contents = OldTex33.q2contents;
  164. texture.nLightmapScale = OldTex33.nLightmapScale;
  165. if (texture.nLightmapScale == 0)
  166. {
  167. texture.nLightmapScale = g_pGameConfig->GetDefaultLightmapScale();
  168. }
  169. }
  170. // If reading from a pre-2.2 RMF file, copy the texture info from the old format.
  171. if (fThisVersion < 2.2f)
  172. {
  173. memcpy(texture.texture, OldTex.texture, sizeof(texture.texture));
  174. memcpy(texture.scale, OldTex.scale, sizeof(texture.scale));
  175. texture.rotate = OldTex.rotate;
  176. texture.smooth = OldTex.smooth;
  177. texture.material = OldTex.material;
  178. texture.q2surface = OldTex.q2surface;
  179. texture.q2contents = OldTex.q2contents;
  180. texture.UAxis[3] = OldTex.shift[0];
  181. texture.VAxis[3] = OldTex.shift[1];
  182. }
  183. if (fThisVersion < 1.8f)
  184. {
  185. texture.texture[40] = 0;
  186. }
  187. //
  188. // Reverse forward slashes if we are not using materials.
  189. //
  190. if (g_pGameConfig->GetTextureFormat() != tfVMT)
  191. {
  192. for (int i = strlen(texture.texture) - 1; i >= 0; i--)
  193. {
  194. if (texture.texture[i] == '/')
  195. {
  196. texture.texture[i] = '\\';
  197. }
  198. }
  199. }
  200. if (texture.texture[1] == ':')
  201. {
  202. char szBuf[MAX_PATH];
  203. char *psz;
  204. strcpy(szBuf, texture.texture);
  205. psz = strstr(szBuf, "textures\\");
  206. if (psz)
  207. {
  208. memset(texture.texture, 0, sizeof(texture.texture));
  209. psz += strlen("textures\\");
  210. strcpy(texture.texture, psz);
  211. }
  212. }
  213. if (fThisVersion < 0.6f)
  214. {
  215. float light;
  216. file.read((char*) &light, sizeof(light));
  217. }
  218. //
  219. // Load the points into an array of float[3]'s and transfer them into
  220. // an array of Vectors which will be used for face creation. We can't
  221. // load directly into the Vectors because the memory layout changed
  222. // when SSE optimizations were added.
  223. //
  224. float LoadPoints[256][3];
  225. file.read((char *)&iSize, sizeof(int));
  226. file.read((char *)&LoadPoints, iSize * 3 * sizeof(float));
  227. Vector CreatePoints[256];
  228. for (int i = 0; i < iSize; i++)
  229. {
  230. CreatePoints[i].x = LoadPoints[i][0];
  231. CreatePoints[i].y = LoadPoints[i][1];
  232. CreatePoints[i].z = LoadPoints[i][2];
  233. //
  234. // Negate Z for older RMF files.
  235. //
  236. if (fThisVersion < 0.5f)
  237. {
  238. CreatePoints[i].z = -CreatePoints[i].z;
  239. }
  240. }
  241. if (fThisVersion < 2.2f)
  242. {
  243. CreateFace(CreatePoints, iSize);
  244. }
  245. //
  246. // Load the plane points. We don't really need them, but they can fix the face if, somehow, it
  247. // was saved without any points. RMF could have been smaller if we only saved these plane points.
  248. //
  249. if (fThisVersion >= 0.7f)
  250. {
  251. //
  252. // Load the points into an array of float[3]'s and transfer them into
  253. // the array of Vectors. We can't load directly into the Vectors because the memory
  254. // layout changed when SSE optimizations were added.
  255. //
  256. float LoadPlanePoints[3][3];
  257. file.read((char *)LoadPlanePoints, sizeof(LoadPlanePoints));
  258. for (int i = 0; i < 3; i++)
  259. {
  260. plane.planepts[i].x = LoadPlanePoints[i][0];
  261. plane.planepts[i].y = LoadPlanePoints[i][1];
  262. plane.planepts[i].z = LoadPlanePoints[i][2];
  263. }
  264. CalcPlane();
  265. // If reading from an older RMF file, set up the texture axes Quake-style.
  266. if (fThisVersion < 2.2f)
  267. {
  268. InitializeTextureAxes(TEXTURE_ALIGN_QUAKE, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
  269. }
  270. }
  271. if( fThisVersion < 2.2f )
  272. {
  273. SetTexture(texture.texture);
  274. }
  275. //
  276. // version 3.4 -- added displacement info to faces
  277. //
  278. if( ( fThisVersion >= 3.4f ) && ( fThisVersion <= 3.6f ) )
  279. {
  280. bool bHasMapDisp;
  281. if( fThisVersion >= 3.5f )
  282. {
  283. int nLoadHasMapDisp;
  284. // check displacement mapping flag
  285. file.read( ( char* )&nLoadHasMapDisp, sizeof( int ) );
  286. bHasMapDisp = nLoadHasMapDisp != 0;
  287. }
  288. else
  289. {
  290. // check displacement mapping flag
  291. file.read( ( char* )&bHasMapDisp, sizeof( bool ) );
  292. }
  293. if( bHasMapDisp )
  294. {
  295. EditDispHandle_t handle = EditDispMgr()->Create();
  296. SetDisp( handle );
  297. CMapDisp *pDisp = EditDispMgr()->GetDisp( handle );
  298. pDisp->SetParent( this );
  299. pDisp->SerializedLoadRMF( file, this, fThisVersion );
  300. }
  301. }
  302. if (fThisVersion >= 2.2f)
  303. {
  304. CreateFace(CreatePoints, iSize);
  305. SetTexture(texture.texture);
  306. }
  307. }
  308. if (file.bad())
  309. {
  310. return(-1);
  311. }
  312. return(0);
  313. }
  314. int MDkeyvalue::SerializeRMF(std::fstream& file, BOOL fIsStoring)
  315. {
  316. // load/save a keyvalue
  317. if( fIsStoring )
  318. {
  319. WriteString(file, szKey);
  320. WriteString(file, szValue);
  321. }
  322. else
  323. {
  324. ReadString(file, szKey);
  325. ReadString(file, szValue);
  326. }
  327. if( file.bad() )
  328. return -1;
  329. return 0;
  330. }
  331. int CMapSolid::SerializeRMF(std::fstream& file, BOOL fIsStoring)
  332. {
  333. int iRvl, iSize;
  334. // load/save children
  335. CMapClass::SerializeRMF(file, fIsStoring);
  336. // load/save a brush
  337. if(fIsStoring)
  338. {
  339. // serialize the Faces
  340. iSize = Faces.GetCount();
  341. file.write((char*) &iSize, sizeof(int));
  342. for(int i = 0; i < iSize; i++)
  343. {
  344. iRvl = Faces[i].SerializeRMF(file, fIsStoring);
  345. if(iRvl < 0)
  346. return iRvl;
  347. }
  348. }
  349. else
  350. {
  351. // There once was a bug that caused black solids. Fix it here.
  352. if ((r == 0) && (g == 0) || (b == 0))
  353. {
  354. PickRandomColor();
  355. }
  356. // read Faces
  357. file.read((char*) &iSize, sizeof(int));
  358. Faces.SetCount(iSize);
  359. for(int i = 0; i < iSize; i++)
  360. {
  361. // extract face
  362. iRvl = Faces[i].SerializeRMF(file, fIsStoring);
  363. if (iRvl < 0)
  364. {
  365. return(iRvl);
  366. }
  367. Faces[i].SetRenderColor(r, g, b);
  368. Faces[i].SetParent(this);
  369. }
  370. CalcBounds();
  371. //
  372. // Set solid type based on texture name.
  373. //
  374. m_eSolidType = HL1SolidTypeFromTextureName(Faces[0].texture.texture);
  375. }
  376. if (file.bad())
  377. {
  378. return -1;
  379. }
  380. return 0;
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Purpose:
  384. // Input : file -
  385. // fIsStoring -
  386. // Output : int
  387. //-----------------------------------------------------------------------------
  388. int CEditGameClass::SerializeRMF(std::fstream& file, BOOL fIsStoring)
  389. {
  390. int iSize, iRvl;
  391. int iAngle = 0;
  392. if (fIsStoring)
  393. {
  394. // save data
  395. WriteString(file, GetClassName());
  396. file.write((char*) &iAngle, sizeof(iAngle));
  397. int nSpawnFlags = GetSpawnFlags();
  398. file.write((char *)&nSpawnFlags, sizeof(nSpawnFlags));
  399. //
  400. // Write the number of keyvalues.
  401. //
  402. iSize = 0;
  403. for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
  404. {
  405. iSize++;
  406. }
  407. file.write((char*) &iSize, sizeof(int));
  408. //
  409. // Write the keyvalues.
  410. //
  411. for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
  412. {
  413. MDkeyvalue KeyValue = m_KeyValues.GetKeyValue(z);
  414. iRvl = KeyValue.SerializeRMF(file, fIsStoring);
  415. if (iRvl < 0)
  416. {
  417. return iRvl;
  418. }
  419. }
  420. //
  421. // Save dummy timeline info.
  422. //
  423. BOOL bTimeline = FALSE;
  424. int nTime = 0;
  425. file.write((char*) &bTimeline, sizeof bTimeline);
  426. file.write((char*) &nTime, sizeof nTime);
  427. file.write((char*) &nTime, sizeof nTime);
  428. }
  429. else
  430. {
  431. char buf[128];
  432. ReadString(file, buf);
  433. file.read((char*) &iAngle, sizeof(iAngle));
  434. int nSpawnFlags;
  435. file.read((char *)&nSpawnFlags, sizeof(nSpawnFlags));
  436. Assert(buf[0]);
  437. CEditGameClass::SetClass(buf, true);
  438. //
  439. // Read the keyvalues.
  440. //
  441. file.read((char *) &iSize, sizeof(int));
  442. for (int i = 0; i < iSize; i++ )
  443. {
  444. MDkeyvalue KeyValue;
  445. iRvl = KeyValue.SerializeRMF(file, fIsStoring);
  446. if (iRvl < 0)
  447. {
  448. return iRvl;
  449. }
  450. m_KeyValues.SetValue(KeyValue.szKey, KeyValue.szValue);
  451. }
  452. SetSpawnFlags(nSpawnFlags);
  453. m_KeyValues.SetValue("classname", buf);
  454. // backwards compatibility for old iAngle
  455. if (iAngle)
  456. {
  457. ImportAngle(iAngle);
  458. }
  459. //
  460. // Dummy timeline information - unused.
  461. //
  462. if (fThisVersion >= 1.5f)
  463. {
  464. BOOL bTimeline;
  465. int nTime;
  466. file.read((char*) &bTimeline, sizeof bTimeline);
  467. file.read((char*) &nTime, sizeof nTime);
  468. file.read((char*) &nTime, sizeof nTime);
  469. }
  470. }
  471. return file.bad() ? -1 : 0;
  472. }
  473. int CMapClass::SerializeRMF(std::fstream& file, BOOL fIsStoring)
  474. {
  475. int iSize, iRvl;
  476. if(fIsStoring)
  477. {
  478. // write type
  479. WriteString(file, GetType());
  480. //
  481. // Write the visgroup ID (zero if none).
  482. //
  483. DWORD dwID = 0;
  484. /*if (m_pVisGroup)
  485. {
  486. // visgroupfixme: how to handle saving RMF? save the first group??
  487. dwID = m_pVisGroup->GetID();
  488. }*/
  489. file.write((char *)&dwID, sizeof(dwID));
  490. //
  491. // Write the object color.
  492. //
  493. file.write((char *)&r, sizeof(BYTE));
  494. file.write((char *)&g, sizeof(BYTE));
  495. file.write((char *)&b, sizeof(BYTE));
  496. //
  497. // Save children.
  498. //
  499. int nChildCount = 0;
  500. FOR_EACH_OBJ( m_Children, pos )
  501. {
  502. CMapClass *pChild = m_Children.Element(pos);
  503. if (pChild->ShouldSerialize())
  504. {
  505. nChildCount++;
  506. }
  507. }
  508. file.write((char *)&nChildCount, sizeof(int));
  509. FOR_EACH_OBJ( m_Children, pos )
  510. {
  511. CMapClass *pChild = m_Children.Element(pos);
  512. if (pChild->ShouldSerialize())
  513. {
  514. iRvl = pChild->SerializeRMF(file, fIsStoring);
  515. if (iRvl < 0)
  516. {
  517. return iRvl;
  518. }
  519. }
  520. }
  521. }
  522. else
  523. {
  524. // read our stuff
  525. if(fThisVersion < 1.0f)
  526. {
  527. // kill group information .. unfortunate
  528. file.read((char*) &iSize, sizeof(int));
  529. file.seekg(iSize, std::ios::cur);
  530. }
  531. else
  532. {
  533. // just read the visgroup ID but ignore it
  534. DWORD dwGroupID;
  535. file.read((char*) &dwGroupID, sizeof(DWORD));
  536. }
  537. //
  538. // Read the object color.
  539. //
  540. file.read((char *)&r, sizeof(BYTE));
  541. file.read((char *)&g, sizeof(BYTE));
  542. file.read((char *)&b, sizeof(BYTE));
  543. // load children
  544. file.read((char*) &iSize, sizeof(int));
  545. for(int i = 0; i < iSize; i++)
  546. {
  547. char buf[128];
  548. ReadString(file, buf);
  549. CMapClass *pChild = CMapClassManager::CreateObject(buf);
  550. if(!pChild)
  551. {
  552. bCorrupt = TRUE;
  553. return -1;
  554. }
  555. iRvl = pChild->SerializeRMF(file, fIsStoring);
  556. if(iRvl < 0)
  557. return iRvl;
  558. AddChild(pChild);
  559. }
  560. }
  561. return file.bad() ? -1 : 0;
  562. }
  563. //-----------------------------------------------------------------------------
  564. // Purpose:
  565. // Input : file -
  566. // fIsStoring -
  567. // Output :
  568. //-----------------------------------------------------------------------------
  569. int CMapEntity::SerializeRMF(std::fstream &file, BOOL fIsStoring)
  570. {
  571. int iSize;
  572. Vector Origin;
  573. //
  574. // Read/write base class.
  575. //
  576. CMapClass::SerializeRMF(file, fIsStoring);
  577. CEditGameClass::SerializeRMF(file, fIsStoring);
  578. if (fIsStoring)
  579. {
  580. // Write flags
  581. file.write((char*) &flags, sizeof(flags));
  582. // Write origin
  583. GetOrigin(Origin);
  584. file.write((char *)Origin.Base(), 3 * sizeof(float));
  585. // Save padding for unused "complex" field
  586. iSize = 0;
  587. file.write((char*) &iSize, sizeof(int));
  588. }
  589. else
  590. {
  591. // Read flags
  592. file.read((char *)&flags, sizeof(flags));
  593. // Read origin
  594. file.read((char *)Origin.Base(), 3 * sizeof(float));
  595. SetOrigin(Origin);
  596. if (IsClass())
  597. {
  598. // Known class. Determine flags based on the class.
  599. flags = IsSolidClass() ? (flags & ~flagPlaceholder) : (flags | flagPlaceholder);
  600. }
  601. else
  602. {
  603. // Unknown class. Determine flags by looking for children (only solid ents have children at this point).
  604. flags = (m_Children.Count() > 0) ? (flags & ~flagPlaceholder) : (flags | flagPlaceholder);
  605. }
  606. if (!(IsPlaceholder()))
  607. {
  608. CMapPoint::SetOrigin(Vector(0, 0, 0));
  609. }
  610. GetOrigin(Origin);
  611. // import for previous to 0.5
  612. if (fThisVersion < 0.5f)
  613. {
  614. Origin.z = -Origin.z;
  615. }
  616. // load unused "complex" field
  617. file.read((char *)&iSize, sizeof(int));
  618. SetOrigin(Origin);
  619. //
  620. // HACK: Set our class to NULL so that it is properly set from our "classname"
  621. // key in PostloadWorld.
  622. //
  623. m_szClass[0] = '\0';
  624. CalcBounds(TRUE);
  625. }
  626. if (file.bad())
  627. {
  628. return -1;
  629. }
  630. return 0;
  631. }
  632. int CMapWorld::SerializeRMF(std::fstream &file, BOOL fIsStoring)
  633. {
  634. float fVersion = 3.7f;
  635. float fLastCompat = 0.3f;
  636. int nSolids = 0;
  637. int iSize;
  638. pLoadingWorld = this;
  639. bCorrupt = FALSE;
  640. // load/save a world
  641. if(fIsStoring)
  642. {
  643. // write version
  644. file.write((char*) &fVersion, sizeof(fVersion));
  645. file.write("RMF", 3);
  646. // we don't save vis groups
  647. iSize = 0;
  648. file.write((char*) &iSize, sizeof(int));
  649. // save children & local data
  650. if(CMapClass::SerializeRMF(file, fIsStoring) == -1)
  651. goto FatalError;
  652. // save ceditgameclass
  653. if(CEditGameClass::SerializeRMF(file, fIsStoring) == -1)
  654. goto FatalError;
  655. // save paths
  656. iSize = m_Paths.Count();
  657. file.write((char*) &iSize, sizeof(iSize));
  658. FOR_EACH_OBJ( m_Paths, pos )
  659. {
  660. CMapPath *pPath = m_Paths.Element(pos);
  661. pPath->SerializeRMF(file, TRUE);
  662. }
  663. if(file.bad())
  664. goto FatalError;
  665. }
  666. else
  667. {
  668. // read & check version
  669. file.read((char*) &fThisVersion, sizeof(fThisVersion));
  670. if(fThisVersion < fLastCompat || fThisVersion > fVersion)
  671. {
  672. CString str;
  673. str.Format("Oops! SerializeRMF() v%1.1f tried to load a file v%1.1f. Aborting.",
  674. fVersion, fThisVersion);
  675. AfxMessageBox(str);
  676. return -1;
  677. }
  678. char buf[128];
  679. if(fThisVersion >= 0.8f)
  680. {
  681. file.read(buf, 3);
  682. if(strncmp(buf, "RMF", 3))
  683. {
  684. AfxMessageBox("Invalid file type.");
  685. return -1;
  686. }
  687. }
  688. // load groups
  689. if (fThisVersion >= 1.0f)
  690. {
  691. file.read((char*) &iSize, sizeof(int));
  692. for ( int i = 0; i < iSize; i++)
  693. {
  694. // just skip vis groups
  695. COldVisGroup oldVisGroup;
  696. file.read((char*) &oldVisGroup, sizeof(COldVisGroup));
  697. }
  698. }
  699. m_Render2DBox.ResetBounds();
  700. // make sure it's a CMapWorld
  701. ReadString(file, buf);
  702. if(strcmp(buf, GetType()))
  703. {
  704. AfxMessageBox("Invalid file type.");
  705. return -1;
  706. }
  707. // load children & local data
  708. if(CMapClass::SerializeRMF(file, fIsStoring) == -1)
  709. goto FatalError;
  710. // load ceditgameclass & CMapClass
  711. if(CEditGameClass::SerializeRMF(file, fIsStoring) == -1)
  712. goto FatalError;
  713. if(fThisVersion < 1.0f)
  714. {
  715. const int old_group_bytes = 134;
  716. file.read((char*) &iSize, sizeof(int));
  717. file.seekg(old_group_bytes * iSize, std::ios::cur);
  718. }
  719. // load paths
  720. if(fThisVersion >= 1.1f)
  721. {
  722. file.read((char*) &iSize, sizeof iSize);
  723. for(int i = 0; i < iSize; i++)
  724. {
  725. CMapPath *pPath = new CMapPath;
  726. pPath->SerializeRMF(file, FALSE);
  727. if(pPath->GetNodeCount() == 0)
  728. {
  729. delete pPath;
  730. continue; // no add dead paths
  731. }
  732. m_Paths.AddToTail(pPath);
  733. }
  734. }
  735. // read camera
  736. if(fThisVersion < 1.4f)
  737. {
  738. float unused[3];
  739. file.read((char*) unused, sizeof(float)*3);
  740. file.read((char*) unused, sizeof(float)*3);
  741. }
  742. if(file.bad())
  743. goto FatalError;
  744. PostloadWorld();
  745. if (g_pGameConfig->GetTextureFormat() == tfVMT)
  746. {
  747. // do batch search and replace of textures from trans.txt if it exists.
  748. char translationFilename[MAX_PATH];
  749. Q_snprintf( translationFilename, sizeof( translationFilename ), "materials/trans.txt" );
  750. FileHandle_t searchReplaceFP = fopen( translationFilename, "r" );
  751. if( searchReplaceFP )
  752. {
  753. CMapDoc::GetActiveMapDoc()->BatchReplaceTextures( searchReplaceFP );
  754. g_pFileSystem->Close( searchReplaceFP );
  755. }
  756. }
  757. }
  758. return nSolids;
  759. FatalError:
  760. CString str;
  761. if(bCorrupt)
  762. {
  763. // file-is-corrupt error
  764. str.Format("The file is corrupt.");
  765. AfxMessageBox(str);
  766. return -1;
  767. }
  768. // OS error.
  769. str.Format("The OS reported an error %s the file: %s",
  770. fIsStoring ? "saving" : "loading", strerror(errno));
  771. AfxMessageBox(str);
  772. return -1;
  773. }