#include "precompiled.h" #pragma hdrstop #ifdef _XENON #define PACIFIER_UPDATE session->PacifierUpdate() #else #define PACIFIER_UPDATE #endif /* =============== FloatCRC =============== */ ID_INLINE unsigned int FloatCRC( float f ) { return *(unsigned int *)&f; } /* =============== StringCRC =============== */ ID_INLINE unsigned int StringCRC( const char *str ) { unsigned int i, crc; const unsigned char *ptr; crc = 0; ptr = reinterpret_cast(str); for ( i = 0; str[i]; i++ ) { crc ^= str[i] << (i & 3); } return crc; } /* ================= ComputeAxisBase WARNING : special case behaviour of atan2(y,x) <-> atan(y/x) might not be the same everywhere when x == 0 rotation by (0,RotY,RotZ) assigns X to normal ================= */ static void ComputeAxisBase( const idVec3 &normal, idVec3 &texS, idVec3 &texT ) { float RotY, RotZ; idVec3 n; // do some cleaning n[0] = ( idMath::Fabs( normal[0] ) < 1e-6f ) ? 0.0f : normal[0]; n[1] = ( idMath::Fabs( normal[1] ) < 1e-6f ) ? 0.0f : normal[1]; n[2] = ( idMath::Fabs( normal[2] ) < 1e-6f ) ? 0.0f : normal[2]; RotY = -idMath::ATan( n[2], idMath::Sqrt( n[1] * n[1] + n[0] * n[0]) ); RotZ = idMath::ATan( n[1], n[0] ); // rotate (0,1,0) and (0,0,1) to compute texS and texT // RAVEN BEGIN // jscott: routed thru idMath texS[0] = -idMath::Sin( RotZ ); texS[1] = idMath::Cos( RotZ ); texS[2] = 0; // the texT vector is along -Z ( T texture coorinates axis ) texT[0] = -idMath::Sin( RotY ) * idMath::Cos( RotZ ); texT[1] = -idMath::Sin( RotY ) * idMath::Sin( RotZ ); texT[2] = -idMath::Cos( RotY ); // RAVEN END } // RAVEN BEGIN // rhummer: Trying to debug why func_groups disappear when editing a map when they shouldn't. idMapFile::~idMapFile( void ) { entities.DeleteContents( true ); if ( cvarSystem->GetCVarBool( "developer" ) ) { common->Printf( "^2done with .map file ^0%s\n", name.c_str() ); } } // RAVEN END /* ================= idMapBrushSide::GetTextureVectors ================= */ void idMapBrushSide::GetTextureVectors( idVec4 v[2] ) const { int i; idVec3 texX, texY; ComputeAxisBase( plane.Normal(), texX, texY ); for ( i = 0; i < 2; i++ ) { v[i][0] = texX[0] * texMat[i][0] + texY[0] * texMat[i][1]; v[i][1] = texX[1] * texMat[i][0] + texY[1] * texMat[i][1]; v[i][2] = texX[2] * texMat[i][0] + texY[2] * texMat[i][1]; v[i][3] = texMat[i][2] + ( origin * v[i].ToVec3() ); } } /* ================= idMapPatch::Parse ================= */ // RAVEN BEGIN // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files idMapPatch *idMapPatch::Parse( Lexer &src, const idVec3 &origin, bool patchDef3, int version ) { // RAVEN END TIME_THIS_SCOPE( __FUNCLINE__); float info[7]; idDrawVert *vert; idToken token; int i, j; if ( !src.ExpectTokenString( "{" ) ) { return NULL; } // read the material (we had an implicit 'textures/' in the old format...) if ( !src.ReadToken( &token ) ) { src.Error( "idMapPatch::Parse: unexpected EOF" ); return NULL; } // Parse it if (patchDef3) { if ( !src.Parse1DMatrix( 7, info ) ) { src.Error( "idMapPatch::Parse: unable to Parse patchDef3 info" ); return NULL; } } else { if ( !src.Parse1DMatrix( 5, info ) ) { src.Error( "idMapPatch::Parse: unable to parse patchDef2 info" ); return NULL; } } idMapPatch *patch = new idMapPatch( info[0], info[1] ); patch->SetSize( info[0], info[1] ); if ( version < 2 ) { patch->SetMaterial( "textures/" + token ); } else { patch->SetMaterial( token ); } if ( patchDef3 ) { patch->SetHorzSubdivisions( info[2] ); patch->SetVertSubdivisions( info[3] ); patch->SetExplicitlySubdivided( true ); } if ( patch->GetWidth() < 0 || patch->GetHeight() < 0 ) { src.Error( "idMapPatch::Parse: bad size" ); delete patch; return NULL; } // these were written out in the wrong order, IMHO if ( !src.ExpectTokenString( "(" ) ) { src.Error( "idMapPatch::Parse: bad patch vertex data" ); delete patch; return NULL; } for ( j = 0; j < patch->GetWidth(); j++ ) { if ( !src.ExpectTokenString( "(" ) ) { src.Error( "idMapPatch::Parse: bad vertex row data" ); delete patch; return NULL; } for ( i = 0; i < patch->GetHeight(); i++ ) { float v[5]; if ( !src.Parse1DMatrix( 5, v ) ) { src.Error( "idMapPatch::Parse: bad vertex column data" ); delete patch; return NULL; } vert = &((*patch)[i * patch->GetWidth() + j]); vert->xyz[0] = v[0] - origin[0]; vert->xyz[1] = v[1] - origin[1]; vert->xyz[2] = v[2] - origin[2]; vert->st[0] = v[3]; vert->st[1] = v[4]; } if ( !src.ExpectTokenString( ")" ) ) { delete patch; src.Error( "idMapPatch::Parse: unable to parse patch control points" ); return NULL; } } if ( !src.ExpectTokenString( ")" ) || !src.ExpectTokenString( "}" ) || !src.ExpectTokenString( "}" ) ) { src.Error( "idMapPatch::Parse: unable to parse patch control points, no closure" ); delete patch; return NULL; } return patch; } /* ============ idMapPatch::Write ============ */ bool idMapPatch::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const { int i, j; const idDrawVert *v; if ( GetExplicitlySubdivided() ) { fp->WriteFloatString( "// primitive %d\n{\n patchDef3\n {\n", primitiveNum ); fp->WriteFloatString( " \"%s\"\n ( %d %d %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight(), GetHorzSubdivisions(), GetVertSubdivisions()); } else { fp->WriteFloatString( "// primitive %d\n{\n patchDef2\n {\n", primitiveNum ); fp->WriteFloatString( " \"%s\"\n ( %d %d 0 0 0 )\n", GetMaterial(), GetWidth(), GetHeight()); } fp->WriteFloatString( " (\n" ); for ( i = 0; i < GetWidth(); i++ ) { fp->WriteFloatString( " ( " ); for ( j = 0; j < GetHeight(); j++ ) { v = &verts[ j * GetWidth() + i ]; fp->WriteFloatString( " ( %f %f %f %f %f )", v->xyz[0] + origin[0], v->xyz[1] + origin[1], v->xyz[2] + origin[2], v->st[0], v->st[1] ); } fp->WriteFloatString( " )\n" ); } fp->WriteFloatString( " )\n }\n}\n" ); return true; } // RAVEN BEGIN // rjohnson: added resolve for handling func_groups and other aspects. Before, radiant would do this processing on a map destroying the original data /* =============== idMapPatch::AdjustOrigin =============== */ void idMapPatch::AdjustOrigin( idVec3 &delta ) { TranslateSelf( delta ); } // RAVEN END /* =============== idMapPatch::GetGeometryCRC =============== */ unsigned int idMapPatch::GetGeometryCRC( void ) const { int i, j; unsigned int crc; crc = GetHorzSubdivisions() ^ GetVertSubdivisions(); for ( i = 0; i < GetWidth(); i++ ) { for ( j = 0; j < GetHeight(); j++ ) { crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.x ); crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.y ); crc ^= FloatCRC( verts[j * GetWidth() + i].xyz.z ); } } crc ^= StringCRC( GetMaterial() ); return crc; } /* ================= idMapBrush::Parse ================= */ // RAVEN BEGIN // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files idMapBrush *idMapBrush::Parse( Lexer &src, const idVec3 &origin, bool newFormat, int version ) { // RAVEN END TIME_THIS_SCOPE( __FUNCLINE__); int i; idVec3 planepts[3]; idToken token; idList sides; idMapBrushSide *side; idDict epairs; if ( !src.ExpectTokenString( "{" ) ) { return NULL; } do { if ( !src.ReadToken( &token ) ) { src.Error( "idMapBrush::Parse: unexpected EOF" ); sides.DeleteContents( true ); return NULL; } if ( token == "}" ) { break; } // here we may have to jump over brush epairs ( only used in editor ) do { // if token is a brace if ( token == "(" ) { break; } // the token should be a key string for a key/value pair if ( token.type != TT_STRING ) { src.Error( "idMapBrush::Parse: unexpected %s, expected ( or epair key string", token.c_str() ); sides.DeleteContents( true ); return NULL; } idStr key = token; if ( !src.ReadTokenOnLine( &token ) || token.type != TT_STRING ) { src.Error( "idMapBrush::Parse: expected epair value string not found" ); sides.DeleteContents( true ); return NULL; } epairs.Set( key, token ); // try to read the next key if ( !src.ReadToken( &token ) ) { src.Error( "idMapBrush::Parse: unexpected EOF" ); sides.DeleteContents( true ); return NULL; } } while (1); src.UnreadToken( &token ); side = new idMapBrushSide(); sides.Append(side); if ( newFormat ) { if ( !src.Parse1DMatrix( 4, side->plane.ToFloatPtr() ) ) { src.Error( "idMapBrush::Parse: unable to read brush side plane definition" ); sides.DeleteContents( true ); return NULL; } } else { // read the three point plane definition if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) || !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) || !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) { src.Error( "idMapBrush::Parse: unable to read brush side plane definition" ); sides.DeleteContents( true ); return NULL; } planepts[0] -= origin; planepts[1] -= origin; planepts[2] -= origin; side->plane.FromPoints( planepts[0], planepts[1], planepts[2] ); } // read the texture matrix // this is odd, because the texmat is 2D relative to default planar texture axis if ( !src.Parse2DMatrix( 2, 3, side->texMat[0].ToFloatPtr() ) ) { src.Error( "idMapBrush::Parse: unable to read brush side texture matrix" ); sides.DeleteContents( true ); return NULL; } side->origin = origin; // read the material if ( !src.ReadTokenOnLine( &token ) ) { src.Error( "idMapBrush::Parse: unable to read brush side material" ); sides.DeleteContents( true ); return NULL; } // we had an implicit 'textures/' in the old format... if ( version < 2 ) { side->material = "textures/" + token; } else { side->material = token; } // RAVEN BEGIN // jscott: make sure the material is properly parsed declManager->FindMaterial( token ); // RAVEN END // RAVEN BEGIN // jscott: removed these in later versions if( version < 3 ) { // RAVEN END // Q2 allowed override of default flags and values, but we don't any more if ( src.ReadTokenOnLine( &token ) ) { if ( src.ReadTokenOnLine( &token ) ) { if ( src.ReadTokenOnLine( &token ) ) { } } } } } while( 1 ); if ( !src.ExpectTokenString( "}" ) ) { sides.DeleteContents( true ); return NULL; } idMapBrush *brush = new idMapBrush(); for ( i = 0; i < sides.Num(); i++ ) { brush->AddSide( sides[i] ); } brush->epairs = epairs; return brush; } /* ================= idMapBrush::ParseQ3 ================= */ // RAVEN BEGIN // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files idMapBrush *idMapBrush::ParseQ3( Lexer &src, const idVec3 &origin ) { // RAVEN END int i, shift[2], rotate; float scale[2]; idVec3 planepts[3]; idToken token; idList sides; idMapBrushSide *side; idDict epairs; do { if ( src.CheckTokenString( "}" ) ) { break; } side = new idMapBrushSide(); sides.Append( side ); // read the three point plane definition if (!src.Parse1DMatrix( 3, planepts[0].ToFloatPtr() ) || !src.Parse1DMatrix( 3, planepts[1].ToFloatPtr() ) || !src.Parse1DMatrix( 3, planepts[2].ToFloatPtr() ) ) { src.Error( "idMapBrush::ParseQ3: unable to read brush side plane definition" ); sides.DeleteContents( true ); return NULL; } planepts[0] -= origin; planepts[1] -= origin; planepts[2] -= origin; side->plane.FromPoints( planepts[0], planepts[1], planepts[2] ); // read the material if ( !src.ReadTokenOnLine( &token ) ) { src.Error( "idMapBrush::ParseQ3: unable to read brush side material" ); sides.DeleteContents( true ); return NULL; } // we have an implicit 'textures/' in the old format side->material = "textures/" + token; // read the texture shift, rotate and scale shift[0] = src.ParseInt(); shift[1] = src.ParseInt(); rotate = src.ParseInt(); scale[0] = src.ParseFloat(); scale[1] = src.ParseFloat(); side->texMat[0] = idVec3( 0.03125f, 0.0f, 0.0f ); side->texMat[1] = idVec3( 0.0f, 0.03125f, 0.0f ); side->origin = origin; // Q2 allowed override of default flags and values, but we don't any more if ( src.ReadTokenOnLine( &token ) ) { if ( src.ReadTokenOnLine( &token ) ) { if ( src.ReadTokenOnLine( &token ) ) { } } } } while( 1 ); idMapBrush *brush = new idMapBrush(); for ( i = 0; i < sides.Num(); i++ ) { brush->AddSide( sides[i] ); } brush->epairs = epairs; return brush; } /* ============ idMapBrush::Write ============ */ bool idMapBrush::Write( idFile *fp, int primitiveNum, const idVec3 &origin ) const { int i; idMapBrushSide *side; fp->WriteFloatString( "// primitive %d\n{\n brushDef3\n {\n", primitiveNum ); // write brush epairs for ( i = 0; i < epairs.GetNumKeyVals(); i++) { fp->WriteFloatString( " \"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str()); } // write brush sides for ( i = 0; i < GetNumSides(); i++ ) { side = GetSide( i ); fp->WriteFloatString( " ( %f %f %f %f ) ", side->plane[0], side->plane[1], side->plane[2], side->plane[3] ); // RAVEN BEGIN fp->WriteFloatString( "( ( %f %f %f ) ( %f %f %f ) ) \"%s\"\n", // RAVEN END side->texMat[0][0], side->texMat[0][1], side->texMat[0][2], side->texMat[1][0], side->texMat[1][1], side->texMat[1][2], side->material.c_str() ); } fp->WriteFloatString( " }\n}\n" ); return true; } /* =============== idMapBrush::GetGeometryCRC =============== */ unsigned int idMapBrush::GetGeometryCRC( void ) const { int i, j; idMapBrushSide *mapSide; unsigned int crc; crc = 0; for ( i = 0; i < GetNumSides(); i++ ) { mapSide = GetSide(i); for ( j = 0; j < 4; j++ ) { crc ^= FloatCRC( mapSide->GetPlane()[j] ); } crc ^= StringCRC( mapSide->GetMaterial() ); } return crc; } // RAVEN BEGIN // rjohnson: added resolve for handling func_groups and other aspects. Before, radiant would do this processing on a map destroying the original data // This is taken from tools/radiant/EditorBrushPrimit.cpp float SarrusDet(idVec3 a, idVec3 b, idVec3 c) { 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]; } /* =============== idMapBrush::AdjustOrigin =============== */ void idMapBrush::AdjustOrigin( idVec3 &delta ) { int i; idMapBrushSide *mapSide; for ( i = 0; i < GetNumSides(); i++ ) { mapSide = GetSide(i); mapSide->SetPlane( mapSide->GetPlane().Translate( delta ) ); // This is taken from Face_MoveTexture_BrushPrimit() in tools/radiant/EditorBrushPrimit.cpp idVec3 texS, texT; float tx, ty; idVec3 M[3]; // columns of the matrix .. easier that way float det; idVec3 D[2]; // compute plane axis base ( doesn't change with translation ) ComputeAxisBase( mapSide->GetPlane().Normal(), texS, texT); // compute translation vector in plane axis base tx = DotProduct(delta, texS); ty = DotProduct(delta, texT); // fill the data vectors M[0][0] = tx; M[0][1] = 1.0f + tx; M[0][2] = tx; M[1][0] = ty; M[1][1] = ty; M[1][2] = 1.0f + ty; M[2][0] = 1.0f; M[2][1] = 1.0f; M[2][2] = 1.0f; idVec3 tm[2]; mapSide->GetTextureMatrix( tm[0], tm[1] ); D[0][0] = tm[0][2]; D[0][1] = tm[0][0] + tm[0][2]; D[0][2] = tm[0][1] + tm[0][2]; D[1][0] = tm[1][2]; D[1][1] = tm[1][0] + tm[1][2]; D[1][2] = tm[1][1] + tm[1][2]; // solve det = SarrusDet(M[0], M[1], M[2]); if ( det != 0. ) { tm[0][0] = SarrusDet(D[0], M[1], M[2]) / det; tm[0][1] = SarrusDet(M[0], D[0], M[2]) / det; tm[0][2] = SarrusDet(M[0], M[1], D[0]) / det; tm[1][0] = SarrusDet(D[1], M[1], M[2]) / det; tm[1][1] = SarrusDet(M[0], D[1], M[2]) / det; tm[1][2] = SarrusDet(M[0], M[1], D[1]) / det; mapSide->SetTextureMatrix(tm); } } } // RAVEN END /* ================ idMapEntity::Parse ================ */ // RAVEN BEGIN // jsinger: changed to be Lexer instead of idLexer so that we have the ability to read binary files idMapEntity *idMapEntity::Parse( Lexer &src, bool worldSpawn, int version ) { // RAVEN END TIME_THIS_SCOPE( __FUNCLINE__); idToken token; idMapEntity *mapEnt; idMapPatch *mapPatch; idMapBrush *mapBrush; bool worldent; idVec3 origin; double v1, v2, v3; if ( !src.ReadToken(&token) ) { return NULL; } if ( token != "{" ) { src.Error( "idMapEntity::Parse: { not found, found %s", token.c_str() ); return NULL; } mapEnt = new idMapEntity(); if ( worldSpawn ) { mapEnt->primitives.Resize( 1024, 256 ); } origin.Zero(); worldent = false; do { if ( !src.ReadToken(&token) ) { src.Error( "idMapEntity::Parse: EOF without closing brace" ); return NULL; } if ( token == "}" ) { break; } if ( token == "{" ) { // parse a brush or patch if ( !src.ReadToken( &token ) ) { src.Error( "idMapEntity::Parse: unexpected EOF" ); return NULL; } if ( worldent ) { origin.Zero(); } // if is it a brush: brush, brushDef, brushDef2, brushDef3 if ( token.Icmpn( "brush", 5 ) == 0 ) { mapBrush = idMapBrush::Parse( src, origin, ( !token.Icmp( "brushDef2" ) || !token.Icmp( "brushDef3" ) ), version ); if ( !mapBrush ) { return NULL; } mapEnt->AddPrimitive( mapBrush ); } // if is it a patch: patchDef2, patchDef3 else if ( token.Icmpn( "patch", 5 ) == 0 ) { mapPatch = idMapPatch::Parse( src, origin, !token.Icmp( "patchDef3" ), version ); if ( !mapPatch ) { return NULL; } mapEnt->AddPrimitive( mapPatch ); } // assume it's a brush in Q3 or older style else { src.UnreadToken( &token ); mapBrush = idMapBrush::ParseQ3( src, origin ); if ( !mapBrush ) { return NULL; } mapEnt->AddPrimitive( mapBrush ); } } else { idStr key, value; // parse a key / value pair key = token; src.ReadTokenOnLine( &token ); value = token; // strip trailing spaces that sometimes get accidentally // added in the editor value.StripTrailingWhitespace(); key.StripTrailingWhitespace(); mapEnt->epairs.Set( key, value ); if ( !idStr::Icmp( key, "origin" ) ) { // scanf into doubles, then assign, so it is idVec size independent v1 = v2 = v3 = 0; sscanf( value, "%lf %lf %lf", &v1, &v2, &v3 ); origin.x = v1; origin.y = v2; origin.z = v3; } else if ( !idStr::Icmp( key, "classname" ) && !idStr::Icmp( value, "worldspawn" ) ) { worldent = true; } } } while( 1 ); return mapEnt; } /* ============ idMapEntity::Write ============ */ bool idMapEntity::Write( idFile *fp, int entityNum ) const { int i; idMapPrimitive *mapPrim; idVec3 origin; fp->WriteFloatString( "// entity %d\n{\n", entityNum ); // write entity epairs for ( i = 0; i < epairs.GetNumKeyVals(); i++) { fp->WriteFloatString( "\"%s\" \"%s\"\n", epairs.GetKeyVal(i)->GetKey().c_str(), epairs.GetKeyVal(i)->GetValue().c_str()); } epairs.GetVector( "origin", "0 0 0", origin ); // write pritimives for ( i = 0; i < GetNumPrimitives(); i++ ) { mapPrim = GetPrimitive( i ); switch( mapPrim->GetType() ) { case idMapPrimitive::TYPE_BRUSH: static_cast(mapPrim)->Write( fp, i, origin ); break; case idMapPrimitive::TYPE_PATCH: static_cast(mapPrim)->Write( fp, i, origin ); break; } } fp->WriteFloatString( "}\n" ); return true; } /* =============== idMapEntity::RemovePrimitiveData =============== */ void idMapEntity::RemovePrimitiveData() { primitives.DeleteContents(true); } /* =============== idMapEntity::GetGeometryCRC =============== */ unsigned int idMapEntity::GetGeometryCRC( void ) const { int i; unsigned int crc; idMapPrimitive *mapPrim; crc = 0; for ( i = 0; i < GetNumPrimitives(); i++ ) { mapPrim = GetPrimitive( i ); switch( mapPrim->GetType() ) { case idMapPrimitive::TYPE_BRUSH: crc ^= static_cast(mapPrim)->GetGeometryCRC(); if ( epairs.GetString( "model" ) ) { crc ^= StringCRC( epairs.GetString( "model" ) ); } break; case idMapPrimitive::TYPE_PATCH: crc ^= static_cast(mapPrim)->GetGeometryCRC(); if ( epairs.GetString( "model" ) ) { crc ^= StringCRC( epairs.GetString( "model" ) ); } break; } } return crc; } /* =============== idMapFile::Parse =============== */ bool idMapFile::Parse( const char *filename, bool ignoreRegion, bool osPath ) { // no string concatenation for epairs and allow path names for materials // RAVEN BEGIN // jsinger: Done this way to reduce the amount of code change. The auto pointer will // delete the lexer when this method exits idAutoPtr lexer(LexerFactory::MakeLexer( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES )); Lexer &src(*lexer); // RAVEN END idToken token; idStr fullName; idMapEntity *mapEnt; TIME_THIS_SCOPE( __FUNCLINE__); name = filename; name.StripFileExtension(); fullName = name; hasPrimitiveData = false; if ( !ignoreRegion ) { // try loading a .reg file first fullName.SetFileExtension( "reg" ); src.LoadFile( fullName, osPath ); } if ( !src.IsLoaded() ) { PACIFIER_UPDATE; // now try a .map file fullName.SetFileExtension( "map" ); src.LoadFile( fullName, osPath ); if ( !src.IsLoaded() ) { // didn't get anything at all return false; } } version = OLD_MAP_VERSION; fileTime = src.GetFileTime(); entities.DeleteContents( true ); if ( src.CheckTokenString( "Version" ) ) { src.ReadTokenOnLine( &token ); version = token.GetIntValue(); } while( 1 ) { PACIFIER_UPDATE; mapEnt = idMapEntity::Parse( src, ( entities.Num() == 0 ), version ); if ( !mapEnt ) { break; } // RAVEN BEGIN // rhummer: Check to see if there are func_groups in the map file. if ( !mHasFuncGroups && !idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) ) { mHasFuncGroups = true; } // RAVEN END entities.Append( mapEnt ); } PACIFIER_UPDATE; ParseExport(filename, osPath); SetGeometryCRC(); // RAVEN BEGIN // rhummer: Trying to debug why func_groups disappear when editing a map when they shouldn't. if ( cvarSystem->GetCVarBool( "developer" ) ) { common->Printf( "^2loaded .map file ^0%s\n", filename ); } mHasBeenResolved = false; // RAVEN END hasPrimitiveData = true; return true; } // RAVEN BEGIN // rjohnson: added resolve for handling func_groups and other aspects. Before, radiant would do this processing on a map destroying the original data void idMapFile::Resolve( void ) { int i, j, k; idMapEntity *mapEnt; if ( !hasPrimitiveData ) { return; } // if the map has a worldspawn if ( entities.Num() ) { // "removeEntities" "classname" can be set in the worldspawn to remove all entities with the given classname const idKeyValue *removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", NULL ); while ( removeEntities ) { RemoveEntities( removeEntities->GetValue() ); removeEntities = entities[0]->epairs.MatchPrefix( "removeEntities", removeEntities ); } // "overrideMaterial" "material" can be set in the worldspawn to reset all materials idStr material; if ( entities[0]->epairs.GetString( "overrideMaterial", "", material ) ) { for ( i = 0; i < entities.Num(); i++ ) { mapEnt = entities[i]; for ( j = 0; j < mapEnt->GetNumPrimitives(); j++ ) { idMapPrimitive *mapPrimitive = mapEnt->GetPrimitive( j ); switch( mapPrimitive->GetType() ) { case idMapPrimitive::TYPE_BRUSH: { idMapBrush *mapBrush = static_cast(mapPrimitive); for ( k = 0; k < mapBrush->GetNumSides(); k++ ) { mapBrush->GetSide( k )->SetMaterial( material ); } break; } case idMapPrimitive::TYPE_PATCH: { static_cast(mapPrimitive)->SetMaterial( material ); break; } } } } } // force all entities to have a name key/value pair if ( entities[0]->epairs.GetBool( "forceEntityNames" ) ) { for ( i = 1; i < entities.Num(); i++ ) { mapEnt = entities[i]; if ( !mapEnt->epairs.FindKey( "name" ) ) { mapEnt->epairs.Set( "name", va( "%s%d", mapEnt->epairs.GetString( "classname", "forcedName" ), i ) ); } } } // move the primitives of any func_group entities to the worldspawn for ( i = 1; i < entities.Num(); i++ ) { mapEnt = entities[i]; if ( idStr::Icmp( mapEnt->epairs.GetString( "classname" ), "func_group" ) == 0 ) { idVec3 delta; mapEnt->epairs.GetVector( "origin", "0 0 0", delta ); for( j = 0; j < mapEnt->primitives.Num(); j++ ) { idMapPrimitive *mapPrim = mapEnt->primitives[j]; mapPrim->AdjustOrigin( delta ); } entities[0]->primitives.Append( mapEnt->primitives ); mapEnt->primitives.Clear(); } } RemoveEntities( "func_group" ); } // rhummer: mHasBeenResolved = true; if ( cvarSystem->GetCVarBool( "developer" ) ) { common->Printf( "^2idMapFile::Resolve has been run on ^0%s^2 so no func_groups exist.\n", name.c_str() ); } } // rjohnson: added export /* ============ idMapFile::Write ============ */ bool idMapFile::Write( const char *fileName, const char *ext, bool fromBasePath, bool exportOnly ) { int i; idStr qpath; idFile *fp; // RAVEN BEGIN // rhummer: Used to verify func_groups didn't disappear somehow. bool funcGroupFound = false; // RAVEN END qpath = fileName; qpath.SetFileExtension( ext ); idLib::common->Printf( "writing %s...\n", qpath.c_str() ); if ( fromBasePath ) { fp = idLib::fileSystem->OpenFileWrite( qpath, "fs_devpath" ); } else { fp = idLib::fileSystem->OpenExplicitFileWrite( qpath ); } if ( !fp ) { idLib::common->Warning( "Couldn't open %s\n", qpath.c_str() ); return false; } fp->WriteFloatString( "Version %d\n", CURRENT_MAP_VERSION ); if (exportOnly) { for ( i = 0; i < entities.Num(); i++ ) { if (entities[i]->epairs.GetInt("export")) { entities[i]->Write( fp, i ); } } } else { for ( i = 0; i < entities.Num(); i++ ) { entities[i]->Write( fp, i ); // RAVEN BEGIN // rhummer: See if there are func_groups, there should be if there was during loading.. if ( !idStr::Icmp( entities[i]->epairs.GetString( "classname" ), "func_group" ) ) { funcGroupFound = true; } // RAVEN END } } // RAVEN BEGIN // rhummer: If this is true..something bad happened to cause all func_groups to go away between loading and saving. if ( mHasFuncGroups && !funcGroupFound && cvarSystem->GetCVarBool( "developer" ) ) { common->Warning( "^5Did not find any ^2func_groups^0 during save, but there were ^2func_groups^0 found during loading. Was this intended?\n"); } // RAVEN END idLib::fileSystem->CloseFile( fp ); return true; } // RAVEN END /* =============== idMapFile::SetGeometryCRC =============== */ void idMapFile::SetGeometryCRC( void ) { int i; geometryCRC = 0; for ( i = 0; i < entities.Num(); i++ ) { geometryCRC ^= entities[i]->GetGeometryCRC(); } } /* =============== idMapFile::AddEntity =============== */ int idMapFile::AddEntity( idMapEntity *mapEnt ) { int ret = entities.Append( mapEnt ); return ret; } /* =============== idMapFile::FindEntity =============== */ idMapEntity *idMapFile::FindEntity( const char *name ) { for ( int i = 0; i < entities.Num(); i++ ) { idMapEntity *ent = entities[i]; if ( idStr::Icmp( ent->epairs.GetString( "name" ), name ) == 0 ) { return ent; } } return NULL; } /* =============== idMapFile::RemoveEntity =============== */ void idMapFile::RemoveEntity( idMapEntity *mapEnt ) { entities.Remove( mapEnt ); delete mapEnt; } /* =============== idMapFile::RemoveEntity =============== */ void idMapFile::RemoveEntities( const char *classname ) { for ( int i = 0; i < entities.Num(); i++ ) { idMapEntity *ent = entities[i]; if ( idStr::Icmp( ent->epairs.GetString( "classname" ), classname ) == 0 ) { delete entities[i]; entities.RemoveIndex( i ); i--; } } } /* =============== idMapFile::RemoveAllEntities =============== */ void idMapFile::RemoveAllEntities() { entities.DeleteContents( true ); hasPrimitiveData = false; } /* =============== idMapFile::RemovePrimitiveData =============== */ void idMapFile::RemovePrimitiveData() { for ( int i = 0; i < entities.Num(); i++ ) { idMapEntity *ent = entities[i]; ent->RemovePrimitiveData(); } hasPrimitiveData = false; // RAVEN BEGIN // rhummer: debugging stuff.. if ( cvarSystem->GetCVarBool( "developer" ) ) { common->Printf( "^2Removed all primitive data from ^0%s\n", name.c_str() ); } // RAVEN END } /* =============== idMapFile::NeedsReload =============== */ bool idMapFile::NeedsReload() { if ( name.Length() ) { unsigned int time = (unsigned int)-1; if ( idLib::fileSystem->ReadFile( name, NULL, &time ) > 0 ) { return ( time > fileTime ); } } return true; } bool idMapFile::WriteExport( const char *fileName, bool fromBasePath ) { int i, j; idDict newPairs; for ( i = 0; i < entities.Num(); i++ ) { idMapEntity *ent = entities[i]; if ( ent->epairs.GetInt("export") == 1 ) { /* for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) { const idKeyValue *kv = ent->epairs.GetKeyVal(j); if (kv->key.CmpPrefix("export_") == 0) { ent->epairs.Delete(kv->key.c_str()); j--; continue; } } */ newPairs.Clear(); for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) { const idKeyValue *kv = ent->epairs.GetKeyVal(j); if (kv->GetKey().CmpPrefix("export_") != 0) { char newName[1024]; sprintf(newName, "export_%s", kv->GetKey().c_str()); if (!ent->epairs.GetString(newName, 0)) { // don't overwrite an existing export newPairs.Set (newName, kv->GetValue().c_str()); } } } ent->epairs.Copy(newPairs); } } return Write(fileName, "export", fromBasePath, true); } bool idMapFile::ParseExport( const char *filename, bool osPath ) { // no string concatenation for epairs and allow path names for materials // RAVEN BEGIN // jsinger: Done this way to reduce the amount of code to change. // The auto pointer will delete the lexer when this method exits. idAutoPtr lexer(LexerFactory::MakeLexer( LEXFL_NOSTRINGCONCAT | LEXFL_NOSTRINGESCAPECHARS | LEXFL_ALLOWPATHNAMES ) ); Lexer &src(*lexer); // RAVEN END idToken token; idStr fullName; idMapEntity *mapEnt; int i, j; int numMerged, numNotMerged, numAdded, numRemoved; numMerged = numNotMerged = numAdded = numRemoved = 0; mHasExportEntities = false; fullName = filename; fullName.StripFileExtension(); fullName.SetFileExtension( "export" ); src.LoadFile( fullName, osPath ); if ( !src.IsLoaded() ) { // didn't get anything at all return false; } version = OLD_MAP_VERSION; fileTime = src.GetFileTime(); mExportEntities.DeleteContents( true ); if ( src.CheckTokenString( "Version" ) ) { src.ReadTokenOnLine( &token ); version = token.GetIntValue(); } while( 1 ) { mapEnt = idMapEntity::Parse( src, false, version ); if ( !mapEnt ) { break; } mExportEntities.Append( mapEnt ); } if (mExportEntities.Num()) { mHasExportEntities = true; } for(i=0;iepairs.GetString("name"); mapEnt = FindEntity(name); if (!mapEnt) { // check to see if we have an export_name name = ent->epairs.GetString("export_name", 0); if (!name || ent->epairs.GetInt("original") ) { // this is a new entity ent->epairs.SetInt("export", 2); // 2 = when re-exporting, don't duplicated the export_ fields ent->epairs.SetInt("merged", 1); ent->epairs.SetInt("original", 0); // 0 = now the entity in map is always treated as master entities.Append(ent); mExportEntities.RemoveIndex(i); i--; numAdded++; continue; } else { mapEnt = FindEntity(name); } } if (!mapEnt) { // this export entity has been removed from the original map numNotMerged++; } else { // exists in both, do a merge! mapEnt->epairs.SetInt("export", 2); // 2 = when re-exporting, don't duplicated the export_ fields mapEnt->epairs.SetInt("merged", 1); for ( j = 0; j < ent->epairs.GetNumKeyVals(); j++) { char origName[1024]; const idKeyValue *exportKV = ent->epairs.GetKeyVal(j); const idKeyValue *origKV, *mapKV; if (exportKV->GetKey().CmpPrefix("export_") != 0) { sprintf(origName, "export_%s", exportKV->GetKey().c_str()); origKV = ent->epairs.FindKey(origName); mapKV = mapEnt->epairs.FindKey(exportKV->GetKey().c_str()); if (origKV && mapKV) { // keys exist in all spots, compare if (origKV->GetValue() == exportKV->GetValue()) { // export hasn't change } else if (mapKV->GetValue() == origKV->GetValue()) { // map is same as the orig, yet export has changed, so bring it over mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str()); } } else if (origKV) { // map has removed this key, so we shouldn't merge it } else if (mapKV) { // map has added this key, so we'll ignore the exported value, which has also been added } else { // this was added to the export, so merge it in mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str()); } } else { mapEnt->epairs.Set(exportKV->GetKey().c_str(), exportKV->GetValue().c_str()); } } numMerged++; } } // check for any entities that have been marked as exported in the original map but have been removed from the export file for(i=0;iepairs.GetInt("export")) { if (!ent->epairs.GetInt("merged")) { entities.RemoveIndex(i); numRemoved++; i--; } else { ent->epairs.Delete("merged"); } } } common->Printf("Export Merge: %d added, %d merged, %d not merged, %d removed\n", numAdded, numMerged, numNotMerged, numRemoved); mExportEntities.DeleteContents( true ); return true; }