home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / idlib / mapfile.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  34KB  |  1,367 lines

  1.  
  2. #include "precompiled.h"
  3. #pragma hdrstop
  4.  
  5. #ifdef _XENON
  6. #define PACIFIER_UPDATE session->PacifierUpdate()
  7. #else
  8. #define PACIFIER_UPDATE
  9. #endif
  10.  
  11.  
  12. /*
  13. ===============
  14. FloatCRC
  15. ===============
  16. */
  17. ID_INLINE unsigned int FloatCRC( float f ) {
  18.     return *(unsigned int *)&f;
  19. }
  20.  
  21. /*
  22. ===============
  23. StringCRC
  24. ===============
  25. */
  26. ID_INLINE unsigned int StringCRC( const char *str ) {
  27.     unsigned int i, crc;
  28.     const unsigned char *ptr;
  29.  
  30.     crc = 0;
  31.     ptr = reinterpret_cast<const unsigned char*>(str);
  32.     for ( i = 0; str[i]; i++ ) {
  33.         crc ^= str[i] << (i & 3);
  34.     }
  35.     return crc;
  36. }
  37.  
  38. /*
  39. =================
  40. ComputeAxisBase
  41.  
  42. WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0
  43. rotation by (0,RotY,RotZ) assigns X to normal
  44. =================
  45. */
  46. static void ComputeAxisBase( const idVec3 &normal, idVec3 &texS, idVec3 &texT ) {
  47.     float RotY, RotZ;
  48.     idVec3 n;
  49.  
  50.     // do some cleaning
  51.     n[0] = ( idMath::Fabs( normal[0] ) < 1e-6f ) ? 0.0f : normal[0];
  52.     n[1] = ( idMath::Fabs( normal[1] ) < 1e-6f ) ? 0.0f : normal[1];
  53.     n[2] = ( idMath::Fabs( normal[2] ) < 1e-6f ) ? 0.0f : normal[2];
  54.  
  55.     RotY = -idMath::ATan( n[2], idMath::Sqrt( n[1] * n[1] + n[0] * n[0]) );
  56.     RotZ = idMath::ATan( n[1], n[0] );
  57.     // rotate (0,1,0) and (0,0,1) to compute texS and texT
  58. // RAVEN BEGIN
  59. // jscott: routed thru idMath
  60.     texS[0] = -idMath::Sin( RotZ );
  61.     texS[1] = idMath::Cos( RotZ );
  62.     texS[2] = 0;
  63.     // the texT vector is along -Z ( T texture coorinates axis )
  64.     texT[0] = -idMath::Sin( RotY ) * idMath::Cos( RotZ );
  65.     texT[1] = -idMath::Sin( RotY ) * idMath::Sin( RotZ );
  66.     texT[2] = -idMath::Cos( RotY );
  67. // RAVEN END
  68. }
  69.  
  70. // RAVEN BEGIN
  71. // rhummer: Trying to debug why func_groups disappear when editing a map when they shouldn't.
  72. idMapFile::~idMapFile( void ) 
  73.     entities.DeleteContents( true ); 
  74.     if ( cvarSystem->GetCVarBool( "developer" ) ) {
  75.         common->Printf( "^2done with .map file ^0%s\n", name.c_str() );
  76.     }
  77. }
  78. // RAVEN END
  79.  
  80. /*
  81. =================
  82. idMapBrushSide::GetTextureVectors
  83. =================
  84. */
  85. void idMapBrushSide::GetTextureVectors( idVec4 v[2] ) const {
  86.     int i;
  87.     idVec3 texX, texY;
  88.  
  89.     ComputeAxisBase( plane.Normal(), texX, texY );
  90.     for ( i = 0; i < 2; i++ ) {
  91.         v[i][0] = texX[0] * texMat[i][0] + texY[0] * texMat[i][1];
  92.         v[i][1] = texX[1] * texMat[i][0] + texY[1] * texMat[i][1];
  93.         v[i][2] = texX[2] * texMat[i][0] + texY[2] * texMat[i][1];
  94.         v[i][3] = texMat[i][2] + ( origin * v[i].ToVec3() );
  95.     }
  96. }
  97.  
  98. /*
  99. =================
  100. idMapPatch::Parse
  101. =================
  102. */
  103. // RAVEN BEGIN
  104. // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
  105. idMapPatch *idMapPatch::Parse( Lexer &src, const idVec3 &origin, bool patchDef3, int version ) {
  106. // RAVEN END
  107.     
  108.     TIME_THIS_SCOPE( __FUNCLINE__);    
  109.     
  110.     float        info[7];
  111.     idDrawVert *vert;
  112.     idToken        token;
  113.     int            i, j;
  114.  
  115.     if ( !src.ExpectTokenString( "{" ) ) {
  116.         return NULL;
  117.     }
  118.  
  119.     // read the material (we had an implicit 'textures/' in the old format...)
  120.     if ( !src.ReadToken( &token ) ) {
  121.         src.Error( "idMapPatch::Parse: unexpected EOF" );
  122.         return NULL;
  123.     }
  124.  
  125.     // Parse it
  126.     if (patchDef3) {
  127.         if ( !src.Parse1DMatrix( 7, info ) ) {
  128.             src.Error( "idMapPatch::Parse: unable to Parse patchDef3 info" );
  129.             return NULL;
  130.         }
  131.     } else {
  132.         if ( !src.Parse1DMatrix( 5, info ) ) {
  133.             src.Error( "idMapPatch::Parse: unable to parse patchDef2 info" );
  134.             return NULL;
  135.         }
  136.     }
  137.  
  138.     idMapPatch *patch = new idMapPatch( info[0], info[1] );
  139.     patch->SetSize( info[0], info[1] );
  140.     if ( version < 2 ) {
  141.         patch->SetMaterial( "textures/" + token );
  142.     } else {
  143.         patch->SetMaterial( token );
  144.     }
  145.  
  146.     if ( patchDef3 ) {
  147.         patch->SetHorzSubdivisions( info[2] );
  148.         patch->SetVertSubdivisions( info[3] );
  149.         patch->SetExplicitlySubdivided( true );
  150.     }
  151.  
  152.     if ( patch->GetWidth() < 0 || patch->GetHeight() < 0 ) {
  153.         src.Error( "idMapPatch::Parse: bad size" );
  154.         delete patch;
  155.         return NULL;
  156.     }
  157.  
  158.     // these were written out in the wrong order, IMHO
  159.     if ( !src.ExpectTokenString( "(" ) ) {
  160.         src.Error( "idMapPatch::Parse: bad patch vertex data" );
  161.         delete patch;
  162.         return NULL;
  163.     }
  164.     for ( j = 0; j < patch->GetWidth(); j++ ) {
  165.         if ( !src.ExpectTokenString( "(" ) ) {
  166.             src.Error( "idMapPatch::Parse: bad vertex row data" );
  167.             delete patch;
  168.             return NULL;
  169.         }
  170.         for ( i = 0; i < patch->GetHeight(); i++ ) {
  171.             float v[5];
  172.  
  173.             if ( !src.Parse1DMatrix( 5, v ) ) {
  174.                 src.Error( "idMapPatch::Parse: bad vertex column data" );
  175.                 delete patch;
  176.                 return NULL;
  177.             }
  178.  
  179.             vert = &((*patch)[i * patch->GetWidth() + j]);
  180.             vert->xyz[0] = v[0] - origin[0];
  181.             vert->xyz[1] = v[1] - origin[1];
  182.             vert->xyz[2] = v[2] - origin[2];
  183.             vert->st[0] = v[3];
  184.             vert->st[1] = v[4];
  185.         }
  186.         if ( !src.ExpectTokenString( ")" ) ) {
  187.             delete patch;
  188.             src.Error( "idMapPatch::Parse: unable to parse patch control points" );
  189.             return NULL;
  190.         }
  191.     }
  192.     if ( !src.ExpectTokenString( ")" ) ||
  193.             !src.ExpectTokenString( "}" ) ||
  194.                 !src.ExpectTokenString( "}" ) ) {
  195.         src.Error( "idMapPatch::Parse: unable to parse patch control points, no closure" );
  196.         delete patch;
  197.         return NULL;
  198.     }
  199.  
  200.     return patch;
  201. }
  202.  
  203. /*
  204. ============
  205. idMapPatch::Write
  206. ============
  207. */
  208. bool idMapPatch::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const {
  209.     int i, j;
  210.     const idDrawVert *v;
  211.  
  212.     if ( GetExplicitlySubdivided() ) {
  213.         fp->WriteFloatString( "// primitive %d\n{\n patchDef3\n {\n", primitiveNum );
  214.         fp->WriteFloatString( "  \"%s\"\n  ( %d %d %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight(), GetHorzSubdivisions(), GetVertSubdivisions());
  215.     } else {
  216.         fp->WriteFloatString( "// primitive %d\n{\n patchDef2\n {\n", primitiveNum );
  217.         fp->WriteFloatString( "  \"%s\"\n  ( %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight());
  218.     }
  219.  
  220.     fp->WriteFloatString( "  (\n" );
  221.     for ( i = 0; i < GetWidth(); i++ ) {
  222.         fp->WriteFloatString( "   ( " );
  223.         for ( j = 0; j < GetHeight(); j++ ) {
  224.             v = &verts[ j * GetWidth() + i ];
  225.             fp->WriteFloatString( " ( %f %f %f %f %f )", v->xyz[0] + origin[0],
  226.                                 v->xyz[1] + origin[1], v->xyz[2] + origin[2], v->st[0], v->st[1] );
  227.         }
  228.         fp->WriteFloatString( " )\n" );
  229.     }
  230.     fp->WriteFloatString( "  )\n }\n}\n" );
  231.  
  232.     return true;
  233. }
  234.  
  235. // RAVEN BEGIN
  236. // rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data
  237. /*
  238. ===============
  239. idMapPatch::AdjustOrigin
  240. ===============
  241. */
  242. void idMapPatch::AdjustOrigin( idVec3 &delta ) {
  243.     TranslateSelf( delta );
  244. }
  245. // RAVEN END
  246.  
  247. /*
  248. ===============
  249. idMapPatch::GetGeometryCRC
  250. ===============
  251. */
  252. unsigned int idMapPatch::GetGeometryCRC( void ) const {
  253.     int i, j;
  254.     unsigned int crc;
  255.  
  256.     crc = GetHorzSubdivisions() ^ GetVertSubdivisions();
  257.     for ( i = 0; i < GetWidth(); i++ ) {
  258.         for ( j = 0; j < GetHeight(); j++ ) {
  259.             crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.x );
  260.             crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.y );
  261.             crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.z );
  262.         }
  263.     }
  264.  
  265.     crc ^= StringCRC( GetMaterial() );
  266.  
  267.     return crc;
  268. }
  269.  
  270. /*
  271. =================
  272. idMapBrush::Parse
  273. =================
  274. */
  275. // RAVEN BEGIN
  276. // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
  277. idMapBrush *idMapBrush::Parse( Lexer &src, const idVec3 &origin, bool newFormat, int version ) {
  278. // RAVEN END
  279.     
  280.     TIME_THIS_SCOPE( __FUNCLINE__);    
  281.     
  282.     int i;
  283.     idVec3 planepts[3];
  284.     idToken token;
  285.     idList<idMapBrushSide*> sides;
  286.     idMapBrushSide    *side;
  287.     idDict epairs;
  288.  
  289.     if ( !src.ExpectTokenString( "{" ) ) {
  290.         return NULL;
  291.     }
  292.  
  293.     do {
  294.         if ( !src.ReadToken( &token ) ) {
  295.             src.Error( "idMapBrush::Parse: unexpected EOF" );
  296.             sides.DeleteContents( true );
  297.             return NULL;
  298.         }
  299.         if ( token == "}" ) {
  300.             break;
  301.         }
  302.  
  303.         // here we may have to jump over brush epairs ( only used in editor )
  304.         do {
  305.             // if token is a brace
  306.             if ( token == "(" ) {
  307.                 break;
  308.             }
  309.             // the token should be a key string for a key/value pair
  310.             if ( token.type != TT_STRING ) {
  311.                 src.Error( "idMapBrush::Parse: unexpected %s, expected ( or epair key string", token.c_str() );
  312.                 sides.DeleteContents( true );
  313.                 return NULL;
  314.             }
  315.  
  316.             idStr key = token;
  317.  
  318.             if ( !src.ReadTokenOnLine( &token ) || token.type != TT_STRING ) {
  319.                 src.Error( "idMapBrush::Parse: expected epair value string not found" );
  320.                 sides.DeleteContents( true );
  321.                 return NULL;
  322.             }
  323.  
  324.             epairs.Set( key, token );
  325.  
  326.             // try to read the next key
  327.             if ( !src.ReadToken( &token ) ) {
  328.                 src.Error( "idMapBrush::Parse: unexpected EOF" );
  329.                 sides.DeleteContents( true );
  330.                 return NULL;
  331.             }
  332.         } while (1);
  333.  
  334.         src.UnreadToken( &token );
  335.  
  336.         side = new idMapBrushSide();
  337.         sides.Append(side);
  338.  
  339.         if ( newFormat ) {
  340.             if ( !src.Parse1DMatrix( 4, side->plane.ToFloatPtr() ) ) {
  341.                 src.Error( "idMapBrush::Parse: unable to read brush side plane definition" );
  342.                 sides.DeleteContents( true );
  343.                 return NULL;
  344.             }
  345.         } else {
  346.             // read the three point plane definition
  347.             if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) ||
  348.                 !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) ||
  349.                 !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) {
  350.                 src.Error( "idMapBrush::Parse: unable to read brush side plane definition" );
  351.                 sides.DeleteContents( true );
  352.                 return NULL;
  353.             }
  354.  
  355.             planepts[0] -= origin;
  356.             planepts[1] -= origin;
  357.             planepts[2] -= origin;
  358.  
  359.             side->plane.FromPoints( planepts[0], planepts[1], planepts[2] );
  360.         }
  361.  
  362.         // read the texture matrix
  363.         // this is odd, because the texmat is 2D relative to default planar texture axis
  364.         if ( !src.Parse2DMatrix( 2, 3, side->texMat[0].ToFloatPtr() ) ) {
  365.             src.Error( "idMapBrush::Parse: unable to read brush side texture matrix" );
  366.             sides.DeleteContents( true );
  367.             return NULL;
  368.         }
  369.         side->origin = origin;
  370.         
  371.         // read the material
  372.         if ( !src.ReadTokenOnLine( &token ) ) {
  373.             src.Error( "idMapBrush::Parse: unable to read brush side material" );
  374.             sides.DeleteContents( true );
  375.             return NULL;
  376.         }
  377.  
  378.         // we had an implicit 'textures/' in the old format...
  379.         if ( version < 2 ) {
  380.             side->material = "textures/" + token;
  381.         } else {
  382.             side->material = token;
  383.         }
  384.  
  385. // RAVEN BEGIN
  386. // jscott: make sure the material is properly parsed
  387.         declManager->FindMaterial( token );
  388. // RAVEN END
  389.  
  390. // RAVEN BEGIN
  391. // jscott: removed these in later versions
  392.         if( version < 3 ) {
  393. // RAVEN END
  394.             // Q2 allowed override of default flags and values, but we don't any more
  395.             if ( src.ReadTokenOnLine( &token ) ) {
  396.                 if ( src.ReadTokenOnLine( &token ) ) {
  397.                     if ( src.ReadTokenOnLine( &token ) ) {
  398.                     }
  399.                 }
  400.             }
  401.         }
  402.     } while( 1 );
  403.  
  404.     if ( !src.ExpectTokenString( "}" ) ) {
  405.         sides.DeleteContents( true );
  406.         return NULL;
  407.     }
  408.  
  409.     idMapBrush *brush = new idMapBrush();
  410.     for ( i = 0; i < sides.Num(); i++ ) {
  411.         brush->AddSide( sides[i] );
  412.     }
  413.  
  414.     brush->epairs = epairs;
  415.  
  416.     return brush;
  417. }
  418.  
  419. /*
  420. =================
  421. idMapBrush::ParseQ3
  422. =================
  423. */
  424. // RAVEN BEGIN
  425. // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
  426. idMapBrush *idMapBrush::ParseQ3( Lexer &src, const idVec3 &origin ) {
  427. // RAVEN END
  428.     int i, shift[2], rotate;
  429.     float scale[2];
  430.     idVec3 planepts[3];
  431.     idToken token;
  432.     idList<idMapBrushSide*> sides;
  433.     idMapBrushSide    *side;
  434.     idDict epairs;
  435.  
  436.     do {
  437.         if ( src.CheckTokenString( "}" ) ) {
  438.             break;
  439.         }
  440.  
  441.         side = new idMapBrushSide();
  442.         sides.Append( side );
  443.  
  444.         // read the three point plane definition
  445.         if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) ||
  446.             !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) ||
  447.             !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) {
  448.             src.Error( "idMapBrush::ParseQ3: unable to read brush side plane definition" );
  449.             sides.DeleteContents( true );
  450.             return NULL;
  451.         }
  452.  
  453.         planepts[0] -= origin;
  454.         planepts[1] -= origin;
  455.         planepts[2] -= origin;
  456.  
  457.         side->plane.FromPoints( planepts[0], planepts[1], planepts[2] );
  458.  
  459.         // read the material
  460.         if ( !src.ReadTokenOnLine( &token ) ) {
  461.             src.Error( "idMapBrush::ParseQ3: unable to read brush side material" );
  462.             sides.DeleteContents( true );
  463.             return NULL;
  464.         }
  465.  
  466.         // we have an implicit 'textures/' in the old format
  467.         side->material = "textures/" + token;
  468.  
  469.         // read the texture shift, rotate and scale
  470.         shift[0] = src.ParseInt();
  471.         shift[1] = src.ParseInt();
  472.         rotate = src.ParseInt();
  473.         scale[0] = src.ParseFloat();
  474.         scale[1] = src.ParseFloat();
  475.         side->texMat[0] = idVec3( 0.03125f, 0.0f, 0.0f );
  476.         side->texMat[1] = idVec3( 0.0f, 0.03125f, 0.0f );
  477.         side->origin = origin;
  478.         
  479.         // Q2 allowed override of default flags and values, but we don't any more
  480.         if ( src.ReadTokenOnLine( &token ) ) {
  481.             if ( src.ReadTokenOnLine( &token ) ) {
  482.                 if ( src.ReadTokenOnLine( &token ) ) {
  483.                 }
  484.             }
  485.         }
  486.     } while( 1 );
  487.  
  488.     idMapBrush *brush = new idMapBrush();
  489.     for ( i = 0; i < sides.Num(); i++ ) {
  490.         brush->AddSide( sides[i] );
  491.     }
  492.  
  493.     brush->epairs = epairs;
  494.  
  495.     return brush;
  496. }
  497.  
  498. /*
  499. ============
  500. idMapBrush::Write
  501. ============
  502. */
  503. bool idMapBrush::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const {
  504.     int i;
  505.     idMapBrushSide *side;
  506.  
  507.     fp->WriteFloatString( "// primitive %d\n{\n brushDef3\n {\n", primitiveNum );
  508.  
  509.     // write brush epairs
  510.     for ( i = 0; i < epairs.GetNumKeyVals(); i++) {
  511.         fp->WriteFloatString( "  \"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str());
  512.     }
  513.  
  514.     // write brush sides
  515.     for ( i = 0; i < GetNumSides(); i++ ) {
  516.         side = GetSide( i );
  517.         fp->WriteFloatString( "  ( %f %f %f %f ) ", side->plane[0], side->plane[1], side->plane[2], side->plane[3] );
  518. // RAVEN BEGIN
  519.         fp->WriteFloatString( "( ( %f %f %f ) ( %f %f %f ) ) \"%s\"\n",
  520. // RAVEN END
  521.                             side->texMat[0][0], side->texMat[0][1], side->texMat[0][2],
  522.                                 side->texMat[1][0], side->texMat[1][1], side->texMat[1][2],
  523.                                     side->material.c_str() );
  524.     }
  525.  
  526.     fp->WriteFloatString( " }\n}\n" );
  527.  
  528.     return true;
  529. }
  530.  
  531. /*
  532. ===============
  533. idMapBrush::GetGeometryCRC
  534. ===============
  535. */
  536. unsigned int idMapBrush::GetGeometryCRC( void ) const {
  537.     int i, j;
  538.     idMapBrushSide *mapSide;
  539.     unsigned int crc;
  540.  
  541.     crc = 0;
  542.     for ( i = 0; i < GetNumSides(); i++ ) {
  543.         mapSide = GetSide(i);
  544.         for ( j = 0; j < 4; j++ ) {
  545.             crc ^= FloatCRC( mapSide->GetPlane()[j] );
  546.         }
  547.         crc ^= StringCRC( mapSide->GetMaterial() );
  548.     }
  549.  
  550.     return crc;
  551. }
  552.  
  553. // RAVEN BEGIN
  554. // rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data
  555.  
  556. // This is taken from tools/radiant/EditorBrushPrimit.cpp
  557. float SarrusDet(idVec3 a, idVec3 b, idVec3 c) {
  558.     return (float)a[0] * (float)b[1] * (float)c[2] + (float)b[0] * (float)c[1] * (float)a[2] + (float)c[0] * (float)a[1] * (float)b[2] - (float)c[0] * (float)b[1] * (float)a[2] - (float)a[1] * (float)b[0] * (float)c[2] -    (float)a[0] * (float)b[2] * (float)c[1];
  559. }
  560.  
  561. /*
  562. ===============
  563. idMapBrush::AdjustOrigin
  564. ===============
  565. */
  566. void idMapBrush::AdjustOrigin( idVec3 &delta ) {
  567.     int                i;
  568.     idMapBrushSide    *mapSide;
  569.  
  570.     for ( i = 0; i < GetNumSides(); i++ ) {
  571.         mapSide = GetSide(i);
  572.  
  573.         mapSide->SetPlane( mapSide->GetPlane().Translate( delta ) );
  574.  
  575.         // This is taken from Face_MoveTexture_BrushPrimit() in tools/radiant/EditorBrushPrimit.cpp
  576.         idVec3    texS, texT;
  577.         float    tx, ty;
  578.         idVec3    M[3];    // columns of the matrix .. easier that way
  579.         float    det;
  580.         idVec3    D[2];
  581.  
  582.         // compute plane axis base ( doesn't change with translation )
  583.         ComputeAxisBase( mapSide->GetPlane().Normal(), texS, texT);
  584.  
  585.         // compute translation vector in plane axis base
  586.         tx = DotProduct(delta, texS);
  587.         ty = DotProduct(delta, texT);
  588.  
  589.         // fill the data vectors
  590.         M[0][0] = tx;
  591.         M[0][1] = 1.0f + tx;
  592.         M[0][2] = tx;
  593.         M[1][0] = ty;
  594.         M[1][1] = ty;
  595.         M[1][2] = 1.0f + ty;
  596.         M[2][0] = 1.0f;
  597.         M[2][1] = 1.0f;
  598.         M[2][2] = 1.0f;
  599.  
  600.         idVec3    tm[2];
  601.         mapSide->GetTextureMatrix( tm[0], tm[1] );
  602.  
  603.         D[0][0] = tm[0][2];
  604.         D[0][1] = tm[0][0] + tm[0][2];
  605.         D[0][2] = tm[0][1] + tm[0][2];
  606.         D[1][0] = tm[1][2];
  607.         D[1][1] = tm[1][0] + tm[1][2];
  608.         D[1][2] = tm[1][1] + tm[1][2];
  609.  
  610.         // solve
  611.         det = SarrusDet(M[0], M[1], M[2]);
  612.         if ( det != 0. ) {
  613.             tm[0][0] = SarrusDet(D[0], M[1], M[2]) / det;
  614.             tm[0][1] = SarrusDet(M[0], D[0], M[2]) / det;
  615.             tm[0][2] = SarrusDet(M[0], M[1], D[0]) / det;
  616.             tm[1][0] = SarrusDet(D[1], M[1], M[2]) / det;
  617.             tm[1][1] = SarrusDet(M[0], D[1], M[2]) / det;
  618.             tm[1][2] = SarrusDet(M[0], M[1], D[1]) / det;
  619.             mapSide->SetTextureMatrix(tm);
  620.         }
  621.     }
  622. }
  623. // RAVEN END
  624.  
  625. /*
  626. ================
  627. idMapEntity::Parse
  628. ================
  629. */
  630. // RAVEN BEGIN
  631. // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files
  632. idMapEntity *idMapEntity::Parse( Lexer &src, bool worldSpawn, int version ) {
  633. // RAVEN END
  634.     
  635.     TIME_THIS_SCOPE( __FUNCLINE__);    
  636.     
  637.     idToken    token;
  638.     idMapEntity *mapEnt;
  639.     idMapPatch *mapPatch;
  640.     idMapBrush *mapBrush;
  641.     bool worldent;
  642.     idVec3 origin;
  643.     double v1, v2, v3;
  644.  
  645.     if ( !src.ReadToken(&token) ) {
  646.         return NULL;
  647.     }
  648.  
  649.     if ( token != "{" ) {
  650.         src.Error( "idMapEntity::Parse: { not found, found %s", token.c_str() );
  651.         return NULL;
  652.     }
  653.  
  654.     mapEnt = new idMapEntity();
  655.  
  656.     if ( worldSpawn ) {
  657.         mapEnt->primitives.Resize( 1024, 256 );
  658.     }
  659.  
  660.     origin.Zero();
  661.     worldent = false;
  662.     do {
  663.         if ( !src.ReadToken(&token) ) {
  664.             src.Error( "idMapEntity::Parse: EOF without closing brace" );
  665.             return NULL;
  666.         }
  667.         if ( token == "}" ) {
  668.             break;
  669.         }
  670.  
  671.         if ( token == "{" ) {
  672.             // parse a brush or patch
  673.             if ( !src.ReadToken( &token ) ) {
  674.                 src.Error( "idMapEntity::Parse: unexpected EOF" );
  675.                 return NULL;
  676.             }
  677.  
  678.             if ( worldent ) {
  679.                 origin.Zero();
  680.             }
  681.  
  682.             // if is it a brush: brush, brushDef, brushDef2, brushDef3
  683.             if ( token.Icmpn( "brush", 5 ) == 0 ) {
  684.                 mapBrush = idMapBrush::Parse( src, origin, ( !token.Icmp( "brushDef2" ) || !token.Icmp( "brushDef3" ) ), version );
  685.                 if ( !mapBrush ) {
  686.                     return NULL;
  687.                 }
  688.                 mapEnt->AddPrimitive( mapBrush );
  689.             }
  690.             // if is it a patch: patchDef2, patchDef3
  691.             else if ( token.Icmpn( "patch", 5 ) == 0 ) {
  692.                 mapPatch = idMapPatch::Parse( src, origin, !token.Icmp( "patchDef3" ), version );
  693.                 if ( !mapPatch ) {
  694.                     return NULL;
  695.                 }
  696.                 mapEnt->AddPrimitive( mapPatch );
  697.             }
  698.             // assume it's a brush in Q3 or older style
  699.             else {
  700.                 src.UnreadToken( &token );
  701.                 mapBrush = idMapBrush::ParseQ3( src, origin );
  702.                 if ( !mapBrush ) {
  703.                     return NULL;
  704.                 }
  705.                 mapEnt->AddPrimitive( mapBrush );
  706.             }
  707.         } else {
  708.             idStr key, value;
  709.  
  710.             // parse a key / value pair
  711.             key = token;
  712.             src.ReadTokenOnLine( &token );
  713.             value = token;
  714.  
  715.             // strip trailing spaces that sometimes get accidentally
  716.             // added in the editor
  717.             value.StripTrailingWhitespace();
  718.             key.StripTrailingWhitespace();
  719.  
  720.             mapEnt->epairs.Set( key, value );
  721.  
  722.             if ( !idStr::Icmp( key, "origin" ) ) {
  723.                 // scanf into doubles, then assign, so it is idVec size independent
  724.                 v1 = v2 = v3 = 0;
  725.                 sscanf( value, "%lf %lf %lf", &v1, &v2, &v3 );
  726.                 origin.x = v1;
  727.                 origin.y = v2;
  728.                 origin.z = v3;
  729.             }
  730.             else if ( !idStr::Icmp( key, "classname" ) && !idStr::Icmp( value, "worldspawn" ) ) {
  731.                 worldent = true;
  732.             }
  733.         }
  734.     } while( 1 );
  735.  
  736.     return mapEnt;
  737. }
  738.  
  739. /*
  740. ============
  741. idMapEntity::Write
  742. ============
  743. */
  744. bool idMapEntity::Write( idFile *fp, int entityNum ) const {
  745.     int i;
  746.     idMapPrimitive *mapPrim;
  747.     idVec3 origin;
  748.  
  749.     fp->WriteFloatString( "// entity %d\n{\n", entityNum );
  750.  
  751.     // write entity epairs
  752.     for ( i = 0; i < epairs.GetNumKeyVals(); i++) {
  753.         fp->WriteFloatString( "\"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str());
  754.     }
  755.  
  756.     epairs.GetVector( "origin", "0 0 0", origin );
  757.  
  758.     // write pritimives
  759.     for ( i = 0; i < GetNumPrimitives(); i++ ) {
  760.         mapPrim = GetPrimitive( i );
  761.  
  762.         switch( mapPrim->GetType() ) {
  763.             case idMapPrimitive::TYPE_BRUSH:
  764.                 static_cast<idMapBrush*>(mapPrim)->Write( fp, i, origin );
  765.                 break;
  766.             case idMapPrimitive::TYPE_PATCH:
  767.                 static_cast<idMapPatch*>(mapPrim)->Write( fp, i, origin );
  768.                 break;
  769.         }
  770.     }
  771.  
  772.     fp->WriteFloatString( "}\n" );
  773.  
  774.     return true;
  775. }
  776.  
  777. /*
  778. ===============
  779. idMapEntity::RemovePrimitiveData
  780. ===============
  781. */
  782. void idMapEntity::RemovePrimitiveData() {
  783.     primitives.DeleteContents(true);
  784. }
  785.  
  786. /*
  787. ===============
  788. idMapEntity::GetGeometryCRC
  789. ===============
  790. */
  791. unsigned int idMapEntity::GetGeometryCRC( void ) const {
  792.     int i;
  793.     unsigned int crc;
  794.     idMapPrimitive    *mapPrim;
  795.  
  796.     crc = 0;
  797.     for ( i = 0; i < GetNumPrimitives(); i++ ) {
  798.         mapPrim = GetPrimitive( i );
  799.  
  800.         switch( mapPrim->GetType() ) {
  801.             case idMapPrimitive::TYPE_BRUSH:
  802.                 crc ^= static_cast<idMapBrush*>(mapPrim)->GetGeometryCRC();
  803.                 if ( epairs.GetString( "model" ) ) {
  804.                     crc ^= StringCRC( epairs.GetString( "model" ) );
  805.                 }
  806.                 break;
  807.             case idMapPrimitive::TYPE_PATCH:
  808.                 crc ^= static_cast<idMapPatch*>(mapPrim)->GetGeometryCRC();
  809.                 if ( epairs.GetString( "model" ) ) {
  810.                     crc ^= StringCRC( epairs.GetString( "model" ) );
  811.                 }
  812.                 break;
  813.         }
  814.     }
  815.  
  816.     return crc;
  817. }
  818.  
  819. /*
  820. ===============
  821. idMapFile::Parse
  822. ===============
  823. */
  824. bool idMapFile::Parse( const char *filename, bool ignoreRegion, bool osPath ) {
  825.     // no string concatenation for epairs and allow path names for materials
  826. // RAVEN BEGIN
  827. // jsinger: Done this way to reduce the amount of code change.  The auto pointer will
  828. // delete the lexer when this method exits
  829.     idAutoPtr<Lexer> lexer(LexerFactory::MakeLexer( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ));
  830.     Lexer &src(*lexer);
  831. // RAVEN END
  832.     idToken token;
  833.     idStr fullName;
  834.     idMapEntity *mapEnt;
  835.     
  836.     TIME_THIS_SCOPE( __FUNCLINE__);    
  837.  
  838.     name = filename;
  839.     name.StripFileExtension();
  840.     fullName = name;
  841.     hasPrimitiveData = false;
  842.  
  843.     if ( !ignoreRegion ) {
  844.         // try loading a .reg file first
  845.         fullName.SetFileExtension( "reg" );
  846.         src.LoadFile( fullName, osPath );
  847.     }
  848.  
  849.     if ( !src.IsLoaded() ) {
  850.  
  851.         PACIFIER_UPDATE;
  852.         
  853.         // now try a .map file
  854.         fullName.SetFileExtension( "map" );
  855.         src.LoadFile( fullName, osPath );
  856.  
  857.         if ( !src.IsLoaded() ) {
  858.             // didn't get anything at all
  859.             return false;
  860.         }
  861.     }
  862.  
  863.     version = OLD_MAP_VERSION;
  864.     fileTime = src.GetFileTime();
  865.     entities.DeleteContents( true );
  866.  
  867.     if ( src.CheckTokenString( "Version" ) ) {
  868.         src.ReadTokenOnLine( &token );
  869.         version = token.GetIntValue();
  870.     }
  871.  
  872.     while( 1 ) {
  873.         PACIFIER_UPDATE;
  874.         mapEnt = idMapEntity::Parse( src, ( entities.Num() == 0 ), version );
  875.         if ( !mapEnt ) {
  876.             break;
  877.         }
  878. // RAVEN BEGIN
  879. // rhummer: Check to see if there are func_groups in the map file.
  880.         if ( !mHasFuncGroups && !idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) ) {
  881.             mHasFuncGroups = true;
  882.         }
  883. // RAVEN END
  884.         entities.Append( mapEnt );
  885.     }
  886.  
  887.     PACIFIER_UPDATE;
  888.     ParseExport(filename, osPath);
  889.  
  890.     SetGeometryCRC();
  891. // RAVEN BEGIN
  892. // rhummer: Trying to debug why func_groups disappear when editing a map when they shouldn't.
  893.     if ( cvarSystem->GetCVarBool( "developer" ) ) {
  894.         common->Printf( "^2loaded .map file ^0%s\n", filename );
  895.     }
  896.     mHasBeenResolved = false;
  897. // RAVEN END
  898.  
  899.     hasPrimitiveData = true;
  900.     return true;
  901. }
  902.  
  903. // RAVEN BEGIN
  904. // rjohnson: added resolve for handling func_groups and other aspects.  Before, radiant would do this processing on a map destroying the original data
  905. void idMapFile::Resolve( void )
  906. {
  907.     int i, j, k;
  908.     idMapEntity *mapEnt;
  909.  
  910.     if ( !hasPrimitiveData ) {
  911.         return;
  912.     }
  913.  
  914.     // if the map has a worldspawn
  915.     if ( entities.Num() ) {
  916.  
  917.         // "removeEntities" "classname" can be set in the worldspawn to remove all entities with the given classname
  918.         const idKeyValue *removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", NULL );
  919.         while ( removeEntities ) {
  920.             RemoveEntities( removeEntities->GetValue() );
  921.             removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", removeEntities );
  922.         }
  923.  
  924.         // "overrideMaterial" "material" can be set in the worldspawn to reset all materials
  925.         idStr material;
  926.         if ( entities[0]->epairs.GetString( "overrideMaterial", "", material ) ) {
  927.             for ( i = 0; i < entities.Num(); i++ ) {
  928.                 mapEnt = entities[i];
  929.                 for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) {
  930.                     idMapPrimitive *mapPrimitive = mapEnt->GetPrimitive( j );
  931.                     switch( mapPrimitive->GetType() ) {
  932.                         case idMapPrimitive::TYPE_BRUSH: {
  933.                             idMapBrush *mapBrush = static_cast<idMapBrush *>(mapPrimitive);
  934.                             for ( k = 0; k < mapBrush->GetNumSides(); k++ ) {
  935.                                 mapBrush->GetSide( k )->SetMaterial( material );
  936.                             }
  937.                             break;
  938.                         }
  939.                         case idMapPrimitive::TYPE_PATCH: {
  940.                             static_cast<idMapPatch *>(mapPrimitive)->SetMaterial( material );
  941.                             break;
  942.                         }
  943.                     }
  944.                 }
  945.             }
  946.         }
  947.  
  948.         // force all entities to have a name key/value pair
  949.         if ( entities[0]->epairs.GetBool( "forceEntityNames" ) ) {
  950.             for ( i = 1; i < entities.Num(); i++ ) {
  951.                 mapEnt = entities[i];
  952.                 if ( !mapEnt->epairs.FindKey( "name" ) ) {
  953.                     mapEnt->epairs.Set( "name", va( "%s%d", mapEnt->epairs.GetString( "classname", "forcedName" ), i ) );
  954.                 }
  955.             }
  956.         }
  957.  
  958.         // move the primitives of any func_group entities to the worldspawn
  959.         for ( i = 1; i < entities.Num(); i++ ) {
  960.             mapEnt = entities[i];
  961.             if ( idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) == 0 ) {
  962.                 idVec3    delta;
  963.                 
  964.                 mapEnt->epairs.GetVector( "origin", "0 0 0", delta );
  965.  
  966.                 for( j = 0; j < mapEnt->primitives.Num(); j++ ) {
  967.                     idMapPrimitive    *mapPrim = mapEnt->primitives[j];
  968.  
  969.                     mapPrim->AdjustOrigin( delta );
  970.                 }
  971.                 entities[0]->primitives.Append( mapEnt->primitives );
  972.                 mapEnt->primitives.Clear();
  973.             }
  974.         }
  975.         RemoveEntities( "func_group" );
  976.     }
  977. // rhummer:
  978.     mHasBeenResolved = true;
  979.     if ( cvarSystem->GetCVarBool( "developer" ) ) {
  980.         common->Printf( "^2idMapFile::Resolve has been run on ^0%s^2 so no func_groups exist.\n", name.c_str() );
  981.     }
  982. }
  983.  
  984. // rjohnson: added export
  985. /*
  986. ============
  987. idMapFile::Write
  988. ============
  989. */
  990. bool idMapFile::Write( const char *fileName, const char *ext, bool fromBasePath, bool exportOnly ) {
  991.     int i;
  992.     idStr qpath;
  993.     idFile *fp;
  994. // RAVEN BEGIN
  995. // rhummer: Used to verify func_groups didn't disappear somehow.
  996.     bool funcGroupFound = false;
  997. // RAVEN END
  998.  
  999.     qpath = fileName;
  1000.     qpath.SetFileExtension( ext );
  1001.  
  1002.     idLib::common->Printf( "writing %s...\n", qpath.c_str() );
  1003.  
  1004.     if ( fromBasePath ) {
  1005.         fp = idLib::fileSystem->OpenFileWrite( qpath, "fs_devpath" );
  1006.     }
  1007.     else {
  1008.         fp = idLib::fileSystem->OpenExplicitFileWrite( qpath );
  1009.     }
  1010.  
  1011.     if ( !fp ) {
  1012.         idLib::common->Warning( "Couldn't open %s\n", qpath.c_str() );
  1013.         return false;
  1014.     }
  1015.  
  1016.     fp->WriteFloatString( "Version %d\n", CURRENT_MAP_VERSION );
  1017.  
  1018.     if (exportOnly)
  1019.     {
  1020.         for ( i = 0; i < entities.Num(); i++ ) 
  1021.         {
  1022.             if (entities[i]->epairs.GetInt("export"))
  1023.             {
  1024.                 entities[i]->Write( fp, i );
  1025.             }
  1026.         }
  1027.     }
  1028.     else
  1029.     {
  1030.         for ( i = 0; i < entities.Num(); i++ ) {
  1031.             entities[i]->Write( fp, i );
  1032. // RAVEN BEGIN
  1033. // rhummer: See if there are func_groups, there should be if there was during loading..
  1034.             if ( !idStr::Icmp( entities[i]->epairs.GetString( "classname" ), "func_group" ) ) {
  1035.                 funcGroupFound = true;
  1036.             }
  1037. // RAVEN END
  1038.         }
  1039.     }
  1040.  
  1041. // RAVEN BEGIN
  1042. // rhummer: If this is true..something bad happened to cause all func_groups to go away between loading and saving.
  1043.     if ( mHasFuncGroups && !funcGroupFound && cvarSystem->GetCVarBool( "developer" ) ) {
  1044.         common->Warning( "^5Did not find any ^2func_groups^0 during save, but there were ^2func_groups^0 found during loading.  Was this intended?\n");
  1045.     }
  1046. // RAVEN END
  1047.  
  1048.     idLib::fileSystem->CloseFile( fp );
  1049.  
  1050.     return true;
  1051. }
  1052. // RAVEN END
  1053.  
  1054. /*
  1055. ===============
  1056. idMapFile::SetGeometryCRC
  1057. ===============
  1058. */
  1059. void idMapFile::SetGeometryCRC( void ) {
  1060.     int i;
  1061.  
  1062.     geometryCRC = 0;
  1063.     for ( i = 0; i < entities.Num(); i++ ) {
  1064.         geometryCRC ^= entities[i]->GetGeometryCRC();
  1065.     }
  1066. }
  1067.  
  1068. /*
  1069. ===============
  1070. idMapFile::AddEntity
  1071. ===============
  1072. */
  1073. int idMapFile::AddEntity( idMapEntity *mapEnt ) {
  1074.     int ret = entities.Append( mapEnt );
  1075.     return ret;
  1076. }
  1077.  
  1078. /*
  1079. ===============
  1080. idMapFile::FindEntity
  1081. ===============
  1082. */
  1083. idMapEntity *idMapFile::FindEntity( const char *name ) {
  1084.     for ( int i = 0; i < entities.Num(); i++ ) {
  1085.         idMapEntity *ent = entities[i];
  1086.         if ( idStr::Icmp( ent->epairs.GetString( "name" ), name ) == 0 ) {
  1087.             return ent;
  1088.         }
  1089.     }
  1090.     return NULL;
  1091. }
  1092.  
  1093. /*
  1094. ===============
  1095. idMapFile::RemoveEntity
  1096. ===============
  1097. */
  1098. void idMapFile::RemoveEntity( idMapEntity *mapEnt ) {
  1099.     entities.Remove( mapEnt );
  1100.     delete mapEnt;
  1101. }
  1102.  
  1103. /*
  1104. ===============
  1105. idMapFile::RemoveEntity
  1106. ===============
  1107. */
  1108. void idMapFile::RemoveEntities( const char *classname ) {
  1109.     for ( int i = 0; i < entities.Num(); i++ ) {
  1110.         idMapEntity *ent = entities[i];
  1111.         if ( idStr::Icmp( ent->epairs.GetString( "classname" ), classname ) == 0 ) {
  1112.             delete entities[i];
  1113.             entities.RemoveIndex( i );
  1114.             i--;
  1115.         }
  1116.     }
  1117. }
  1118.  
  1119. /*
  1120. ===============
  1121. idMapFile::RemoveAllEntities
  1122. ===============
  1123. */
  1124. void idMapFile::RemoveAllEntities() {
  1125.     entities.DeleteContents( true );
  1126.     hasPrimitiveData = false;
  1127. }
  1128.  
  1129. /*
  1130. ===============
  1131. idMapFile::RemovePrimitiveData
  1132. ===============
  1133. */
  1134. void idMapFile::RemovePrimitiveData() {
  1135.     for ( int i = 0; i < entities.Num(); i++ ) {
  1136.         idMapEntity *ent = entities[i];
  1137.         ent->RemovePrimitiveData();
  1138.     }
  1139.     hasPrimitiveData = false;
  1140. // RAVEN BEGIN
  1141. // rhummer: debugging stuff..
  1142.     if ( cvarSystem->GetCVarBool( "developer" ) ) {
  1143.         common->Printf( "^2Removed all primitive data from ^0%s\n", name.c_str() );
  1144.     }
  1145. // RAVEN END
  1146. }
  1147.  
  1148. /*
  1149. ===============
  1150. idMapFile::NeedsReload
  1151. ===============
  1152. */
  1153. bool idMapFile::NeedsReload() {
  1154.     if ( name.Length() ) {
  1155.         unsigned int time = (unsigned int)-1;
  1156.         if ( idLib::fileSystem->ReadFile( name, NULL, &time ) > 0 ) {
  1157.             return ( time > fileTime );
  1158.         }
  1159.     }
  1160.     return true;
  1161. }
  1162.  
  1163. bool idMapFile::WriteExport( const char *fileName, bool fromBasePath )
  1164. {
  1165.     int            i, j;
  1166.     idDict        newPairs;
  1167.  
  1168.     for ( i = 0; i < entities.Num(); i++ ) 
  1169.     {
  1170.         idMapEntity *ent = entities[i];
  1171.  
  1172.         if ( ent->epairs.GetInt("export") == 1 )
  1173.         {
  1174. /*            for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) 
  1175.             {
  1176.                 const idKeyValue    *kv = ent->epairs.GetKeyVal(j);
  1177.  
  1178.                 if (kv->key.CmpPrefix("export_") == 0)
  1179.                 {
  1180.                     ent->epairs.Delete(kv->key.c_str());
  1181.                     j--;
  1182.                     continue;
  1183.                 }
  1184.             }
  1185. */
  1186.             newPairs.Clear();
  1187.  
  1188.             for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) 
  1189.             {
  1190.                 const idKeyValue    *kv = ent->epairs.GetKeyVal(j);
  1191.  
  1192.                 if (kv->GetKey().CmpPrefix("export_") != 0)
  1193.                 {
  1194.                     char                newName[1024];
  1195.  
  1196.                     sprintf(newName, "export_%s", kv->GetKey().c_str());
  1197.  
  1198.                     if (!ent->epairs.GetString(newName, 0))
  1199.                     {    // don't overwrite an existing export
  1200.                         newPairs.Set (newName, kv->GetValue().c_str());
  1201.                     }
  1202.                 }
  1203.             }
  1204.  
  1205.             ent->epairs.Copy(newPairs);
  1206.         }
  1207.     }
  1208.  
  1209.     return Write(fileName, "export", fromBasePath, true);
  1210. }
  1211.  
  1212. bool idMapFile::ParseExport( const char *filename, bool osPath )
  1213. {
  1214.     // no string concatenation for epairs and allow path names for materials
  1215. // RAVEN BEGIN
  1216. // jsinger: Done this way to reduce the amount of code to change.
  1217. //          The auto pointer will delete the lexer when this method exits.
  1218.     idAutoPtr<Lexer>        lexer(LexerFactory::MakeLexer( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) );
  1219.     Lexer &src(*lexer);
  1220. // RAVEN END
  1221.     idToken        token;
  1222.     idStr        fullName;
  1223.     idMapEntity *mapEnt;
  1224.     int            i, j;
  1225.     int            numMerged, numNotMerged, numAdded, numRemoved;
  1226.  
  1227.     numMerged = numNotMerged = numAdded = numRemoved = 0;
  1228.     mHasExportEntities = false;
  1229.     
  1230.     fullName = filename;
  1231.     fullName.StripFileExtension();
  1232.     fullName.SetFileExtension( "export" );
  1233.     src.LoadFile( fullName, osPath );
  1234.     if ( !src.IsLoaded() ) {
  1235.         // didn't get anything at all
  1236.         return false;
  1237.     }
  1238.  
  1239.     version = OLD_MAP_VERSION;
  1240.     fileTime = src.GetFileTime();
  1241.     mExportEntities.DeleteContents( true );
  1242.  
  1243.     if ( src.CheckTokenString( "Version" ) ) {
  1244.         src.ReadTokenOnLine( &token );
  1245.         version = token.GetIntValue();
  1246.     }
  1247.  
  1248.     while( 1 ) {
  1249.         mapEnt = idMapEntity::Parse( src, false, version );
  1250.         if ( !mapEnt ) {
  1251.             break;
  1252.         }
  1253.         mExportEntities.Append( mapEnt );
  1254.     }
  1255.  
  1256.     if (mExportEntities.Num())
  1257.     {
  1258.         mHasExportEntities = true;
  1259.     }
  1260.  
  1261.     for(i=0;i<mExportEntities.Num();i++)
  1262.     {
  1263.         idMapEntity *ent = mExportEntities[i];
  1264.         idMapEntity *mapEnt;
  1265.         const char    *name;
  1266.  
  1267.         name = ent->epairs.GetString("name");
  1268.         mapEnt = FindEntity(name);
  1269.  
  1270.         if (!mapEnt)
  1271.         {    // check to see if we have an export_name
  1272.             name = ent->epairs.GetString("export_name", 0);
  1273.             if (!name || ent->epairs.GetInt("original") )
  1274.             {    // this is a new entity
  1275.                 ent->epairs.SetInt("export", 2);            // 2 = when re-exporting, don't duplicated the export_ fields
  1276.                 ent->epairs.SetInt("merged", 1);
  1277.                 ent->epairs.SetInt("original", 0);            // 0 = now the entity in map is always treated as master
  1278.                 entities.Append(ent);
  1279.                 mExportEntities.RemoveIndex(i);
  1280.                 i--;
  1281.                 numAdded++;
  1282.                 continue;
  1283.             }
  1284.             else
  1285.             {
  1286.                 mapEnt = FindEntity(name);
  1287.             }
  1288.         }
  1289.  
  1290.         if (!mapEnt)
  1291.         {    // this export entity has been removed from the original map
  1292.             numNotMerged++;
  1293.         }
  1294.         else
  1295.         {    // exists in both, do a merge!
  1296.             mapEnt->epairs.SetInt("export", 2);            // 2 = when re-exporting, don't duplicated the export_ fields
  1297.             mapEnt->epairs.SetInt("merged", 1);
  1298.             for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) 
  1299.             {
  1300.                 char                origName[1024];
  1301.                 const idKeyValue    *exportKV = ent->epairs.GetKeyVal(j);
  1302.                 const idKeyValue    *origKV, *mapKV;
  1303.  
  1304.                 if (exportKV->GetKey().CmpPrefix("export_") != 0)
  1305.                 {
  1306.                     sprintf(origName, "export_%s", exportKV->GetKey().c_str());
  1307.                     origKV = ent->epairs.FindKey(origName);
  1308.                     mapKV = mapEnt->epairs.FindKey(exportKV->GetKey().c_str());
  1309.  
  1310.                     if (origKV && mapKV)
  1311.                     {    // keys exist in all spots, compare
  1312.                         if (origKV->GetValue() == exportKV->GetValue())
  1313.                         {    // export hasn't change
  1314.                         }
  1315.                         else if (mapKV->GetValue() == origKV->GetValue())
  1316.                         {    // map is same as the orig, yet export has changed, so bring it over
  1317.                             mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str());
  1318.                         }
  1319.                     }
  1320.                     else if (origKV)
  1321.                     {    // map has removed this key, so we shouldn't merge it
  1322.                     }
  1323.                     else if (mapKV)
  1324.                     {    // map has added this key, so we'll ignore the exported value, which has also been added
  1325.                     }
  1326.                     else
  1327.                     {    // this was added to the export, so merge it in
  1328.                         mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str());
  1329.                     }
  1330.                 }
  1331.                 else
  1332.                 {
  1333.                     mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str());
  1334.                 }
  1335.             }
  1336.             numMerged++;
  1337.         }
  1338.     }
  1339.  
  1340.     // check for any entities that have been marked as exported in the original map but have been removed from the export file
  1341.     for(i=0;i<entities.Num();i++)
  1342.     {
  1343.         idMapEntity *ent = entities[i];
  1344.  
  1345.         if (ent->epairs.GetInt("export"))
  1346.         {
  1347.             if (!ent->epairs.GetInt("merged"))
  1348.             {
  1349.                 entities.RemoveIndex(i);
  1350.                 numRemoved++;
  1351.                 i--;
  1352.             }
  1353.             else
  1354.             {
  1355.                 ent->epairs.Delete("merged");
  1356.             }
  1357.         }
  1358.     }
  1359.  
  1360.     common->Printf("Export Merge: %d added, %d merged, %d not merged, %d removed\n", numAdded, numMerged, numNotMerged, numRemoved);
  1361.  
  1362.     mExportEntities.DeleteContents( true );
  1363.  
  1364.     return true;
  1365. }
  1366.