/* =========================================================================== Doom 3 GPL Source Code Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code"). Doom 3 Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 Source Code. If not, see . In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ // This is real evil but allows the code to inspect arbitrary class variables. #define private public #define protected public #include "sys/platform.h" #include "idlib/containers/LinkList.h" #include "idlib/containers/StaticList.h" #include "idlib/math/Quat.h" #include "idlib/math/Interpolate.h" #include "idlib/bv/Bounds.h" #include "idlib/Lexer.h" #include "framework/Common.h" #include "framework/FileSystem.h" #include "Entity.h" #ifdef ID_DEBUG_MEMORY #include "GameTypeInfo.h" // Make sure this is up to date! #else #include "NoGameTypeInfo.h" #endif #include "TypeInfo.h" // disabled because it's adds about 64MB to state dumps and takes a really long time //#define DUMP_GAMELOCAL typedef void (*WriteVariableType_t)( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ); class idTypeInfoTools { public: static const classTypeInfo_t * FindClassInfo( const char *typeName ); static const enumTypeInfo_t * FindEnumInfo( const char *typeName ); static bool IsSubclassOf( const char *typeName, const char *superType ); static void PrintType( const void *typePtr, const char *typeName ); static void WriteTypeToFile( idFile *fp, const void *typePtr, const char *typeName ); static void InitTypeVariables( const void *typePtr, const char *typeName, int value ); static void WriteGameState( const char *fileName ); static void CompareGameState( const char *fileName ); private: static idFile * fp; static int initValue; static WriteVariableType_t Write; static idLexer * src; static bool typeError; static const char * OutputString( const char *string ); static bool ParseTemplateArguments( idLexer &src, idStr &arguments ); static void PrintVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ); static void WriteVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ); static void WriteGameStateVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ); static void InitVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ); static void VerifyVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ); static int WriteVariable_r( const void *varPtr, const char *varName, const char *varType, const char *scope, const char *prefix, const int pointerDepth ); static void WriteClass_r( const void *classPtr, const char *className, const char *classType, const char *scope, const char *prefix, const int pointerDepth ); }; idFile * idTypeInfoTools::fp = NULL; int idTypeInfoTools::initValue = 0; WriteVariableType_t idTypeInfoTools::Write = NULL; idLexer * idTypeInfoTools::src = NULL; bool idTypeInfoTools::typeError = false; /* ================ GetTypeVariableName ================ */ const char *GetTypeVariableName( const char *typeName, int offset ) { static char varName[1024]; int i; for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) { if ( idStr::Cmp( typeName, classTypeInfo[i].typeName ) == 0 ) { if ( classTypeInfo[i].variables[0].name != NULL && offset >= classTypeInfo[i].variables[0].offset ) { break; } typeName = classTypeInfo[i].superType; if ( *typeName == '\0' ) { return ""; } i = -1; } } const classTypeInfo_t &classInfo = classTypeInfo[i]; for ( i = 0; classInfo.variables[i].name != NULL; i++ ) { if ( offset <= classInfo.variables[i].offset ) { break; } } if ( i == 0 ) { idStr::snPrintf( varName, sizeof( varName ), "%s::", classInfo.typeName ); } else { idStr::snPrintf( varName, sizeof( varName ), "%s::%s", classInfo.typeName, classInfo.variables[i-1].name ); } return varName; } /* ================ idTypeInfoTools::FindClassInfo ================ */ const classTypeInfo_t *idTypeInfoTools::FindClassInfo( const char *typeName ) { int i; for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) { if ( idStr::Cmp( typeName, classTypeInfo[i].typeName ) == 0 ) { return &classTypeInfo[i]; } } return NULL; } /* ================ idTypeInfoTools::FindEnumInfo ================ */ const enumTypeInfo_t *idTypeInfoTools::FindEnumInfo( const char *typeName ) { int i; for ( i = 0; enumTypeInfo[i].typeName != NULL; i++ ) { if ( idStr::Cmp( typeName, enumTypeInfo[i].typeName ) == 0 ) { return &enumTypeInfo[i]; } } return NULL; } /* ================ idTypeInfoTools::IsSubclassOf ================ */ bool idTypeInfoTools::IsSubclassOf( const char *typeName, const char *superType ) { int i; while( *typeName != '\0' ) { if ( idStr::Cmp( typeName, superType ) == 0 ) { return true; } for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) { if ( idStr::Cmp( typeName, classTypeInfo[i].typeName ) == 0 ) { typeName = classTypeInfo[i].superType; break; } } if ( classTypeInfo[i].typeName == NULL ) { common->Warning( "super class %s not found", typeName ); break; } } return false; } /* ================ idTypeInfoTools::OutputString ================ */ const char *idTypeInfoTools::OutputString( const char *string ) { static int index = 0; static char buffers[4][16384]; char *out; int i, c; out = buffers[index]; index = ( index + 1 ) & 3; if ( string == NULL ) { return NULL; } for ( i = 0; i < sizeof( buffers[0] ) - 2; i++ ) { c = *string++; switch( c ) { case '\0': out[i] = '\0'; return out; case '\\': out[i++] = '\\'; out[i] = '\\'; break; case '\n': out[i++] = '\\'; out[i] = 'n'; break; case '\r': out[i++] = '\\'; out[i] = 'r'; break; case '\t': out[i++] = '\\'; out[i] = 't'; break; case '\v': out[i++] = '\\'; out[i] = 'v'; break; default: out[i] = c; break; } } out[i] = '\0'; return out; } /* ================ idTypeInfoTools::ParseTemplateArguments ================ */ bool idTypeInfoTools::ParseTemplateArguments( idLexer &src, idStr &arguments ) { int indent; idToken token; arguments = ""; if ( !src.ExpectTokenString( "<" ) ) { return false; } indent = 1; while( indent ) { if ( !src.ReadToken( &token ) ) { break; } if ( token == "<" ) { indent++; } else if ( token == ">" ) { indent--; } else { if ( arguments.Length() ) { arguments += " "; } arguments += token; } } return true; } /* ================ idTypeInfoTools::PrintType ================ */ void idTypeInfoTools::PrintType( const void *typePtr, const char *typeName ) { idTypeInfoTools::fp = NULL; idTypeInfoTools::initValue = 0; idTypeInfoTools::Write = PrintVariable; WriteClass_r( typePtr, "", typeName, "", "", 0 ); } /* ================ idTypeInfoTools::WriteTypeToFile ================ */ void idTypeInfoTools::WriteTypeToFile( idFile *fp, const void *typePtr, const char *typeName ) { idTypeInfoTools::fp = fp; idTypeInfoTools::initValue = 0; idTypeInfoTools::Write = WriteVariable; WriteClass_r( typePtr, "", typeName, "", "", 0 ); } /* ================ idTypeInfoTools::InitTypeVariables ================ */ void idTypeInfoTools::InitTypeVariables( const void *typePtr, const char *typeName, int value ) { idTypeInfoTools::fp = NULL; idTypeInfoTools::initValue = value; idTypeInfoTools::Write = InitVariable; WriteClass_r( typePtr, "", typeName, "", "", 0 ); } /* ================ IsAllowedToChangedFromSaveGames ================ */ bool IsAllowedToChangedFromSaveGames( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value ) { if ( idStr::Icmp( scope, "idAnimator" ) == 0 ) { if ( idStr::Icmp( varName, "forceUpdate" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "lastTransformTime" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "AFPoseTime" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "frameBounds" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idClipModel" ) == 0 ) { if ( idStr::Icmp( varName, "touchCount" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idEntity" ) == 0 ) { if ( idStr::Icmp( varName, "numPVSAreas" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "renderView" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idBrittleFracture" ) == 0 ) { if ( idStr::Icmp( varName, "changed" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idPhysics_AF" ) == 0 ) { return true; } else if ( idStr::Icmp( scope, "renderEntity_t" ) == 0 ) { // These get fixed up when UpdateVisuals is called if ( idStr::Icmp( varName, "origin" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "axis" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "bounds" ) == 0 ) { return true; } } if ( idStr::Icmpn( prefix, "idAFEntity_Base::af.idAF::physicsObj.idPhysics_AF", 49) == 0 ) { return true; } return false; } /* ================ IsRenderHandleVariable ================ */ bool IsRenderHandleVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value ) { if ( idStr::Icmp( scope, "idClipModel" ) == 0 ) { if ( idStr::Icmp( varName, "renderModelHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idFXLocalAction" ) == 0 ) { if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "modelDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idEntity" ) == 0 ) { if ( idStr::Icmp( varName, "modelDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idLight" ) == 0 ) { if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idAFEntity_Gibbable" ) == 0 ) { if ( idStr::Icmp( varName, "skeletonModelDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idAFEntity_SteamPipe" ) == 0 ) { if ( idStr::Icmp( varName, "steamModelHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idItem" ) == 0 ) { if ( idStr::Icmp( varName, "itemShellHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idExplodingBarrel" ) == 0 ) { if ( idStr::Icmp( varName, "particleModelDefHandle" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idProjectile" ) == 0 ) { if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idBFGProjectile" ) == 0 ) { if ( idStr::Icmp( varName, "secondModelDefHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idSmokeParticles" ) == 0 ) { if ( idStr::Icmp( varName, "renderEntityHandle" ) == 0 ) { return true; } } else if ( idStr::Icmp( scope, "idWeapon" ) == 0 ) { if ( idStr::Icmp( varName, "muzzleFlashHandle" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "worldMuzzleFlashHandle" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "guiLightHandle" ) == 0 ) { return true; } if ( idStr::Icmp( varName, "nozzleGlowHandle" ) == 0 ) { return true; } } return false; } /* ================ idTypeInfoTools::PrintVariable ================ */ void idTypeInfoTools::PrintVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ) { common->Printf( "%s%s::%s%s = \"%s\"\n", prefix, scope, varName, postfix, value ); } /* ================ idTypeInfoTools::WriteVariable ================ */ void idTypeInfoTools::WriteVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ) { for ( int i = idStr::FindChar( value, '#', 0 ); i >= 0; i = idStr::FindChar( value, '#', i+1 ) ) { if ( idStr::Icmpn( value+i+1, "INF", 3 ) == 0 || idStr::Icmpn( value+i+1, "IND", 3 ) == 0 || idStr::Icmpn( value+i+1, "NAN", 3 ) == 0 || idStr::Icmpn( value+i+1, "QNAN", 4 ) == 0 || idStr::Icmpn( value+i+1, "SNAN", 4 ) == 0 ) { common->Warning( "%s%s::%s%s = \"%s\"", prefix, scope, varName, postfix, value ); break; } } fp->WriteFloatString( "%s%s::%s%s = \"%s\"\n", prefix, scope, varName, postfix, value ); } /* ================ idTypeInfoTools::WriteGameStateVariable ================ */ void idTypeInfoTools::WriteGameStateVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ) { for ( int i = idStr::FindChar( value, '#', 0 ); i >= 0; i = idStr::FindChar( value, '#', i+1 ) ) { if ( idStr::Icmpn( value+i+1, "INF", 3 ) == 0 || idStr::Icmpn( value+i+1, "IND", 3 ) == 0 || idStr::Icmpn( value+i+1, "NAN", 3 ) == 0 || idStr::Icmpn( value+i+1, "QNAN", 4 ) == 0 || idStr::Icmpn( value+i+1, "SNAN", 4 ) == 0 ) { common->Warning( "%s%s::%s%s = \"%s\"", prefix, scope, varName, postfix, value ); break; } } if ( IsRenderHandleVariable( varName, varType, scope, prefix, postfix, value ) ) { return; } if ( IsAllowedToChangedFromSaveGames( varName, varType, scope, prefix, postfix, value ) ) { return; } fp->WriteFloatString( "%s%s::%s%s = \"%s\"\n", prefix, scope, varName, postfix, value ); } /* ================ idTypeInfoTools::InitVariable ================ */ void idTypeInfoTools::InitVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ) { if ( varPtr != NULL && varSize > 0 ) { // NOTE: skip renderer handles if ( IsRenderHandleVariable( varName, varType, scope, prefix, postfix, value ) ) { return; } memset( const_cast(varPtr), initValue, varSize ); } } /* ================ idTypeInfoTools::VerifyVariable ================ */ void idTypeInfoTools::VerifyVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value, const void *varPtr, int varSize ) { idToken token; if ( typeError ) { return; } src->SkipUntilString( "=" ); src->ExpectTokenType( TT_STRING, 0, &token ); if ( token.Cmp( value ) != 0 ) { // NOTE: skip several things if ( IsRenderHandleVariable( varName, varType, scope, prefix, postfix, value ) ) { return; } if ( IsAllowedToChangedFromSaveGames( varName, varType, scope, prefix, postfix, value ) ) { return; } src->Warning( "state diff for %s%s::%s%s\n%s\n%s", prefix, scope, varName, postfix, token.c_str(), value ); typeError = true; } } /* ================ idTypeInfoTools::WriteVariable_r ================ */ int idTypeInfoTools::WriteVariable_r( const void *varPtr, const char *varName, const char *varType, const char *scope, const char *prefix, const int pointerDepth ) { int i, isPointer, typeSize; idLexer typeSrc; idToken token; idStr typeString, templateArgs; isPointer = 0; typeSize = -1; // create a type string without 'const', 'mutable', 'class', 'struct', 'union' typeSrc.LoadMemory( varType, idStr::Length( varType ), varName ); while( typeSrc.ReadToken( &token ) ) { if ( token != "const" && token != "mutable" && token != "class" && token != "struct" && token != "union" ) { typeString += token + " "; } } typeString.StripTrailing( ' ' ); typeSrc.FreeSource(); // if this is an array if ( typeString[typeString.Length() - 1] == ']' ) { for ( i = typeString.Length(); i > 0 && typeString[i - 1] != '['; i-- ) { } int num = atoi( &typeString[i] ); idStr listVarType = typeString; listVarType.CapLength( i - 1 ); typeSize = 0; for ( i = 0; i < num; i++ ) { idStr listVarName = va( "%s[%d]", varName, i ); int size = WriteVariable_r( varPtr, listVarName, listVarType, scope, prefix, pointerDepth ); typeSize += size; if ( size == -1 ) { break; } varPtr = (void *)( ( (byte *) varPtr ) + size ); } return typeSize; } // if this is a pointer isPointer = 0; for ( i = typeString.Length(); i > 0 && typeString[i - 1] == '*'; i -= 2 ) { if ( varPtr == (void *)0xcdcdcdcd || ( varPtr != NULL && *((unsigned int *)varPtr) == 0xcdcdcdcd ) ) { common->Warning( "%s%s::%s%s references uninitialized memory", prefix, scope, varName, "" ); return typeSize; } if ( varPtr != NULL ) { varPtr = *((void **)varPtr); } isPointer++; } if ( varPtr == NULL ) { Write( varName, varType, scope, prefix, "", "", varPtr, 0 ); return sizeof( void * ); } typeSrc.LoadMemory( typeString, typeString.Length(), varName ); if ( !typeSrc.ReadToken( &token ) ) { Write( varName, varType, scope, prefix, "", va( "", varType ), varPtr, 0 ); return -1; } // get full type while( typeSrc.CheckTokenString( "::" ) ) { idToken newToken; typeSrc.ExpectTokenType( TT_NAME, 0, &newToken ); token += "::" + newToken; } if ( token == "signed" ) { if ( !typeSrc.ReadToken( &token ) ) { Write( varName, varType, scope, prefix, "", va( "", varType ), varPtr, 0 ); return -1; } if ( token == "char" ) { typeSize = sizeof( signed char ); Write( varName, varType, scope, prefix, "", va( "%d", *((signed char *)varPtr) ), varPtr, typeSize ); } else if ( token == "short" ) { typeSize = sizeof( signed short ); Write( varName, varType, scope, prefix, "", va( "%d", *((signed short *)varPtr) ), varPtr, typeSize ); } else if ( token == "int" ) { typeSize = sizeof( signed int ); Write( varName, varType, scope, prefix, "", va( "%d", *((signed int *)varPtr) ), varPtr, typeSize ); } else if ( token == "long" ) { typeSize = sizeof( signed long ); Write( varName, varType, scope, prefix, "", va( "%ld", *((signed long *)varPtr) ), varPtr, typeSize ); } else { Write( varName, varType, scope, prefix, "", va( "", varType ), varPtr, 0 ); return -1; } } else if ( token == "unsigned" ) { if ( !typeSrc.ReadToken( &token ) ) { Write( varName, varType, scope, prefix, "", va( "", varType ), varPtr, 0 ); return -1; } if ( token == "char" ) { typeSize = sizeof( unsigned char ); Write( varName, varType, scope, prefix, "", va( "%d", *((unsigned char *)varPtr) ), varPtr, typeSize ); } else if ( token == "short" ) { typeSize = sizeof( unsigned short ); Write( varName, varType, scope, prefix, "", va( "%d", *((unsigned short *)varPtr) ), varPtr, typeSize ); } else if ( token == "int" ) { typeSize = sizeof( unsigned int ); Write( varName, varType, scope, prefix, "", va( "%d", *((unsigned int *)varPtr) ), varPtr, typeSize ); } else if ( token == "long" ) { typeSize = sizeof( unsigned long ); Write( varName, varType, scope, prefix, "", va( "%lu", *((unsigned long *)varPtr) ), varPtr, typeSize ); } else { Write( varName, varType, scope, prefix, "", va( "", varType ), varPtr, 0 ); return -1; } } else if ( token == "byte" ) { typeSize = sizeof( byte ); Write( varName, varType, scope, prefix, "", va( "%d", *((byte *)varPtr) ), varPtr, typeSize ); } else if ( token == "word" ) { typeSize = sizeof( word ); Write( varName, varType, scope, prefix, "", va( "%d", *((word *)varPtr) ), varPtr, typeSize ); } else if ( token == "dword" ) { typeSize = sizeof( dword ); Write( varName, varType, scope, prefix, "", va( "%d", *((dword *)varPtr) ), varPtr, typeSize ); } else if ( token == "bool" ) { typeSize = sizeof( bool ); Write( varName, varType, scope, prefix, "", va( "%d", *((bool *)varPtr) ), varPtr, typeSize ); } else if ( token == "char" ) { typeSize = sizeof( char ); Write( varName, varType, scope, prefix, "", va( "%d", *((char *)varPtr) ), varPtr, typeSize ); } else if ( token == "short" ) { typeSize = sizeof( short ); Write( varName, varType, scope, prefix, "", va( "%d", *((short *)varPtr) ), varPtr, typeSize ); } else if ( token == "int" ) { typeSize = sizeof( int ); Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), varPtr, typeSize ); } else if ( token == "long" ) { typeSize = sizeof( long ); Write( varName, varType, scope, prefix, "", va( "%ld", *((long *)varPtr) ), varPtr, typeSize ); } else if ( token == "float" ) { typeSize = sizeof( float ); Write( varName, varType, scope, prefix, "", idStr( *((float *)varPtr) ).c_str(), varPtr, typeSize ); } else if ( token == "double" ) { typeSize = sizeof( double ); Write( varName, varType, scope, prefix, "", idStr( (float)*((double *)varPtr) ).c_str(), varPtr, typeSize ); } else if ( token == "idVec2" ) { typeSize = sizeof( idVec2 ); Write( varName, varType, scope, prefix, "", ((idVec2 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idVec3" ) { typeSize = sizeof( idVec3 ); Write( varName, varType, scope, prefix, "", ((idVec3 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idVec4" ) { typeSize = sizeof( idVec4 ); Write( varName, varType, scope, prefix, "", ((idVec4 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idVec5" ) { typeSize = sizeof( idVec5 ); Write( varName, varType, scope, prefix, "", ((idVec5 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idVec6" ) { typeSize = sizeof( idVec6 ); Write( varName, varType, scope, prefix, "", ((idVec6 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idVecX" ) { const idVecX *vec = ((idVecX *)varPtr); if ( vec->ToFloatPtr() != NULL ) { Write( varName, varType, scope, prefix, "", vec->ToString( 8 ), vec->ToFloatPtr(), vec->GetSize() * sizeof( float ) ); } else { Write( varName, varType, scope, prefix, "", "", varPtr, 0 ); } typeSize = sizeof( idVecX ); } else if ( token == "idMat2" ) { typeSize = sizeof( idMat2 ); Write( varName, varType, scope, prefix, "", ((idMat2 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idMat3" ) { typeSize = sizeof( idMat3 ); Write( varName, varType, scope, prefix, "", ((idMat3 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idMat4" ) { typeSize = sizeof( idMat4 ); Write( varName, varType, scope, prefix, "", ((idMat4 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idMat5" ) { typeSize = sizeof( idMat5 ); Write( varName, varType, scope, prefix, "", ((idMat5 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idMat6" ) { typeSize = sizeof( idMat6 ); Write( varName, varType, scope, prefix, "", ((idMat6 *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idMatX" ) { typeSize = sizeof( idMatX ); const idMatX *mat = ((idMatX *)varPtr); if ( mat->ToFloatPtr() != NULL ) { Write( varName, varType, scope, prefix, "", mat->ToString( 8 ), mat->ToFloatPtr(), mat->GetNumColumns() * mat->GetNumRows() * sizeof( float ) ); } else { Write( varName, varType, scope, prefix, "", "", NULL, 0 ); } } else if ( token == "idAngles" ) { typeSize = sizeof( idAngles ); Write( varName, varType, scope, prefix, "", ((idAngles *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idQuat" ) { typeSize = sizeof( idQuat ); Write( varName, varType, scope, prefix, "", ((idQuat *)varPtr)->ToString( 8 ), varPtr, typeSize ); } else if ( token == "idBounds" ) { typeSize = sizeof( idBounds ); const idBounds *bounds = ((idBounds *)varPtr); if ( bounds->IsCleared() ) { Write( varName, varType, scope, prefix, "", "", varPtr, typeSize ); } else { Write( varName, varType, scope, prefix, "", va( "(%s)-(%s)", (*bounds)[0].ToString( 8 ), (*bounds)[1].ToString( 8 ) ), varPtr, typeSize ); } } else if ( token == "idList" ) { idList *list = ((idList *)varPtr); Write( varName, varType, scope, prefix, ".num", va( "%d", list->Num() ), NULL, 0 ); // NOTE: we don't care about the amount of memory allocated //Write( varName, varType, scope, prefix, ".size", va( "%d", list->Size() ), NULL, 0 ); Write( varName, varType, scope, prefix, ".granularity", va( "%d", list->GetGranularity() ), NULL, 0 ); if ( list->Num() && ParseTemplateArguments( typeSrc, templateArgs ) ) { void *listVarPtr = list->Ptr(); for ( i = 0; i < list->Num(); i++ ) { idStr listVarName = va( "%s[%d]", varName, i ); int size = WriteVariable_r( listVarPtr, listVarName, templateArgs, scope, prefix, pointerDepth ); if ( size == -1 ) { break; } listVarPtr = (void *)( ( (byte *) listVarPtr ) + size ); } } typeSize = sizeof( idList ); } else if ( token == "idStaticList" ) { idStaticList *list = ((idStaticList *)varPtr); Write( varName, varType, scope, prefix, ".num", va( "%d", list->Num() ), NULL, 0 ); int totalSize = 0; if ( list->Num() && ParseTemplateArguments( typeSrc, templateArgs ) ) { void *listVarPtr = list->Ptr(); for ( i = 0; i < list->Num(); i++ ) { idStr listVarName = va( "%s[%d]", varName, i ); int size = WriteVariable_r( listVarPtr, listVarName, templateArgs, scope, prefix, pointerDepth ); if ( size == -1 ) { break; } totalSize += size; listVarPtr = (void *)( ( (byte *) listVarPtr ) + size ); } } typeSize = sizeof( int ) + totalSize; } else if ( token == "idLinkList" ) { // FIXME: implement typeSize = sizeof( idLinkList ); Write( varName, varType, scope, prefix, "", va( "", varType ), NULL, 0 ); } else if ( token == "idStr" ) { typeSize = sizeof( idStr ); const idStr *str = ((idStr *)varPtr); Write( varName, varType, scope, prefix, "", OutputString( str->c_str() ), str->c_str(), str->Length() ); } else if ( token == "idStrList" ) { typeSize = sizeof( idStrList ); const idStrList *list = ((idStrList *)varPtr); if ( list->Num() ) { for ( i = 0; i < list->Num(); i++ ) { Write( varName, varType, scope, prefix, va("[%d]", i ), OutputString( (*list)[i].c_str() ), (*list)[i].c_str(), (*list)[i].Length() ); } } else { Write( varName, varType, scope, prefix, "", "", NULL, 0 ); } } else if ( token == "idDict" ) { typeSize = sizeof( idDict ); const idDict *dict = ((idDict *)varPtr); if ( dict->GetNumKeyVals() ) { for ( i = 0; i < dict->GetNumKeyVals(); i++ ) { const idKeyValue *kv = dict->GetKeyVal( i ); Write( varName, varType, scope, prefix, va("[%d]", i ), va( "\'%s\' \'%s\'", OutputString( kv->GetKey().c_str() ), OutputString( kv->GetValue().c_str() ) ), NULL, 0 ); } } else { Write( varName, varType, scope, prefix, "", "", NULL, 0 ); } } else if ( token == "idExtrapolate" ) { const idExtrapolate *interpolate = ((idExtrapolate *)varPtr); Write( varName, varType, scope, prefix, ".extrapolationType", idStr( interpolate->GetExtrapolationType() ).c_str(), &interpolate->extrapolationType, sizeof( interpolate->extrapolationType ) ); Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) ); Write( varName, varType, scope, prefix, ".duration", idStr( interpolate->GetDuration() ).c_str(), &interpolate->duration, sizeof( interpolate->duration ) ); if ( ParseTemplateArguments( typeSrc, templateArgs ) ) { if ( templateArgs == "int" ) { const idExtrapolate *interpolate = ((idExtrapolate *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".baseSpeed", idStr( interpolate->GetBaseSpeed() ).c_str(), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) ); Write( varName, varType, scope, prefix, ".speed", idStr( interpolate->GetSpeed() ).c_str(), &interpolate->speed, sizeof( interpolate->speed ) ); typeSize = sizeof( idExtrapolate ); } else if ( templateArgs == "float" ) { const idExtrapolate *interpolate = ((idExtrapolate *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".baseSpeed", idStr( interpolate->GetBaseSpeed() ).c_str(), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) ); Write( varName, varType, scope, prefix, ".speed", idStr( interpolate->GetSpeed() ).c_str(), &interpolate->speed, sizeof( interpolate->speed ) ); typeSize = sizeof( idExtrapolate ); } else if ( templateArgs == "idVec3" ) { const idExtrapolate *interpolate = ((idExtrapolate *)varPtr); Write( varName, varType, scope, prefix, ".startValue", interpolate->GetStartValue().ToString( 8 ), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".baseSpeed", interpolate->GetBaseSpeed().ToString( 8 ), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) ); Write( varName, varType, scope, prefix, ".speed", interpolate->GetSpeed().ToString( 8 ), &interpolate->speed, sizeof( interpolate->speed ) ); typeSize = sizeof( idExtrapolate ); } else if ( templateArgs == "idAngles" ) { const idExtrapolate *interpolate = ((idExtrapolate *)varPtr); Write( varName, varType, scope, prefix, ".startValue", interpolate->GetStartValue().ToString( 8 ), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".baseSpeed", interpolate->GetBaseSpeed().ToString( 8 ), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) ); Write( varName, varType, scope, prefix, ".speed", interpolate->GetSpeed().ToString( 8 ), &interpolate->speed, sizeof( interpolate->speed ) ); typeSize = sizeof( idExtrapolate ); } else { Write( varName, varType, scope, prefix, "", va( "", templateArgs.c_str() ), NULL, 0 ); } } } else if ( token == "idInterpolate" ) { const idInterpolate *interpolate = ((idInterpolate *)varPtr); Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) ); Write( varName, varType, scope, prefix, ".duration", idStr( interpolate->GetDuration() ).c_str(), &interpolate->duration, sizeof( interpolate->duration ) ); if ( ParseTemplateArguments( typeSrc, templateArgs ) ) { if ( templateArgs == "int" ) { const idInterpolate *interpolate = ((idInterpolate *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) ); typeSize = sizeof( idInterpolate ); } else if ( templateArgs == "float" ) { const idInterpolate *interpolate = ((idInterpolate *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) ); typeSize = sizeof( idInterpolate ); } else { Write( varName, varType, scope, prefix, "", va( "", templateArgs.c_str() ), NULL, 0 ); } } } else if ( token == "idInterpolateAccelDecelLinear" ) { const idInterpolateAccelDecelLinear *interpolate = ((idInterpolateAccelDecelLinear *)varPtr); Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) ); Write( varName, varType, scope, prefix, ".accelTime", idStr( interpolate->GetAcceleration() ).c_str(), &interpolate->accelTime, sizeof( interpolate->accelTime ) ); Write( varName, varType, scope, prefix, ".linearTime", idStr( interpolate->linearTime ).c_str(), &interpolate->linearTime, sizeof( interpolate->linearTime ) ); Write( varName, varType, scope, prefix, ".decelTime", idStr( interpolate->GetDeceleration() ).c_str(), &interpolate->decelTime, sizeof( interpolate->decelTime ) ); if ( ParseTemplateArguments( typeSrc, templateArgs ) ) { if ( templateArgs == "int" ) { const idInterpolateAccelDecelLinear *interpolate = ((idInterpolateAccelDecelLinear *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) ); typeSize = sizeof( idInterpolateAccelDecelLinear ); } else if ( templateArgs == "float" ) { const idInterpolateAccelDecelLinear *interpolate = ((idInterpolateAccelDecelLinear *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) ); typeSize = sizeof( idInterpolateAccelDecelLinear ); } else { Write( varName, varType, scope, prefix, "", va( "", templateArgs.c_str() ), NULL, 0 ); } } } else if ( token == "idInterpolateAccelDecelSine" ) { const idInterpolateAccelDecelSine *interpolate = ((idInterpolateAccelDecelSine *)varPtr); Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) ); Write( varName, varType, scope, prefix, ".accelTime", idStr( interpolate->GetAcceleration() ).c_str(), &interpolate->accelTime, sizeof( interpolate->accelTime ) ); Write( varName, varType, scope, prefix, ".linearTime", idStr( interpolate->linearTime ).c_str(), &interpolate->linearTime, sizeof( interpolate->linearTime ) ); Write( varName, varType, scope, prefix, ".decelTime", idStr( interpolate->GetDeceleration() ).c_str(), &interpolate->decelTime, sizeof( interpolate->decelTime ) ); if ( ParseTemplateArguments( typeSrc, templateArgs ) ) { if ( templateArgs == "int" ) { const idInterpolateAccelDecelSine *interpolate = ((idInterpolateAccelDecelSine *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) ); typeSize = sizeof( idInterpolateAccelDecelSine ); } else if ( templateArgs == "float" ) { const idInterpolateAccelDecelSine *interpolate = ((idInterpolateAccelDecelSine *)varPtr); Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) ); Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) ); typeSize = sizeof( idInterpolateAccelDecelSine ); } else { Write( varName, varType, scope, prefix, "", va( "", templateArgs.c_str() ), NULL, 0 ); } } } else if ( token == "idUserInterface" ) { typeSize = sizeof( idUserInterface ); const idUserInterface *gui = ((idUserInterface *)varPtr); Write( varName, varType, scope, prefix, "", gui->Name(), varPtr, sizeof( varPtr ) ); } else if ( token == "idRenderModel" ) { typeSize = sizeof( idRenderModel ); const idRenderModel *model = ((idRenderModel *)varPtr); Write( varName, varType, scope, prefix, "", model->Name(), varPtr, sizeof( varPtr ) ); } else if ( token == "qhandle_t" ) { typeSize = sizeof( int ); Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), varPtr, typeSize ); } else if ( token == "cmHandle_t" ) { typeSize = sizeof( int ); Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), varPtr, typeSize ); } else if ( token == "idEntityPtr" ) { typeSize = sizeof( idEntityPtr ); const idEntityPtr *entPtr = ((idEntityPtr *)varPtr); if ( entPtr->GetEntity() ) { idEntity *entity = entPtr->GetEntity(); Write( varName, varType, scope, prefix, ".", va( "entity %d: \'%s\'", entity->entityNumber, entity->name.c_str() ), varPtr, typeSize ); } else { Write( varName, varType, scope, prefix, "", "", varPtr, typeSize ); } } else if ( token == "idEntity::entityFlags_s" ) { const idEntity::entityFlags_s *flags = ((idEntity::entityFlags_s *)varPtr); Write( varName, varType, scope, prefix, ".notarget", flags->notarget ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".noknockback", flags->noknockback ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".takedamage", flags->takedamage ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".hidden", flags->hidden ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".bindOrientated", flags->bindOrientated ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".solidForTeam", flags->solidForTeam ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".forcePhysicsUpdate", flags->forcePhysicsUpdate ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".selected", flags->selected ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".neverDormant", flags->neverDormant ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".isDormant", flags->isDormant ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".hasAwakened", flags->hasAwakened ? "true" : "false", NULL, 0 ); Write( varName, varType, scope, prefix, ".networkSync", flags->networkSync ? "true" : "false", NULL, 0 ); typeSize = sizeof( idEntity::entityFlags_s ); } else if ( token == "idScriptBool" ) { typeSize = sizeof( idScriptBool ); const idScriptBool *scriptBool = ((idScriptBool *)varPtr); if ( scriptBool->IsLinked() ) { Write( varName, varType, scope, prefix, "", ( *scriptBool != 0 ) ? "true" : "false", varPtr, typeSize ); } else { Write( varName, varType, scope, prefix, "", "", varPtr, typeSize ); } } else { const classTypeInfo_t *classTypeInfo = FindClassInfo( scope + ( "::" + token ) ); if ( classTypeInfo == NULL ) { classTypeInfo = FindClassInfo( token ); } if ( classTypeInfo != NULL ) { typeSize = classTypeInfo->size; if ( !isPointer ) { char newPrefix[1024]; idStr::snPrintf( newPrefix, sizeof( newPrefix ), "%s%s::%s.", prefix, scope, varName ); WriteClass_r( varPtr, "", token, token, newPrefix, pointerDepth ); } else if ( token == "idAnim" ) { const idAnim *anim = ((idAnim*)varPtr); Write( varName, varType, scope, prefix, "", anim->Name(), NULL, 0 ); } else if ( token == "idPhysics" ) { const idPhysics *physics = ((idPhysics*)varPtr); Write( varName, varType, scope, prefix, "", physics->GetType()->classname, NULL, 0 ); } else if ( IsSubclassOf( token, "idEntity" ) ) { const idEntity *entity = ((idEntity*)varPtr); Write( varName, varType, scope, prefix, "", va( "entity %d: \'%s\'", entity->entityNumber, entity->name.c_str() ), NULL, 0 ); } else if ( IsSubclassOf( token, "idDecl" ) ) { const idDecl *decl = ((idDecl *)varPtr); Write( varName, varType, scope, prefix, "", decl->GetName(), NULL, 0 ); } else if ( pointerDepth == 0 && ( token == "idAFBody" || token == "idAFTree" || token == "idClipModel" || IsSubclassOf( token, "idAFConstraint" ) ) ) { char newPrefix[1024]; idStr::snPrintf( newPrefix, sizeof( newPrefix ), "%s%s::%s->", prefix, scope, varName ); WriteClass_r( varPtr, "", token, token, newPrefix, pointerDepth + 1 ); } else { Write( varName, varType, scope, prefix, "", va( "", varType ), NULL, 0 ); return -1; } } else { const enumTypeInfo_t *enumTypeInfo = FindEnumInfo( scope + ( "::" + token ) ); if ( enumTypeInfo == NULL ) { enumTypeInfo = FindEnumInfo( token ); } if ( enumTypeInfo != NULL ) { typeSize = sizeof( int ); // NOTE: assuming sizeof( enum ) is sizeof( int ) for ( i = 0; enumTypeInfo->values[i].name != NULL; i++ ) { if ( *((int *)varPtr) == enumTypeInfo->values[i].value ) { break; } } if ( enumTypeInfo->values[i].name != NULL ) { Write( varName, varType, scope, prefix, "", enumTypeInfo->values[i].name, NULL, 0 ); } else { Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), NULL, 0 ); } } else { Write( varName, varType, scope, prefix, "", va( "", varType ), NULL, 0 ); return -1; } } } i = 0; do { if ( *((unsigned int *)varPtr) == 0xcdcdcdcd ) { common->Warning( "%s%s::%s%s uses uninitialized memory", prefix, scope, varName, "" ); break; } } while( ++i < typeSize ); if ( isPointer ) { return sizeof( void * ); } return typeSize; } /* ================ idTypeInfoTools::WriteClass_r ================ */ void idTypeInfoTools::WriteClass_r( const void *classPtr, const char *className, const char *classType, const char *scope, const char *prefix, const int pointerDepth ) { int i; const classTypeInfo_t *classInfo = FindClassInfo( classType ); if ( !classInfo ) { return; } if ( *classInfo->superType != '\0' ) { WriteClass_r( classPtr, className, classInfo->superType, scope, prefix, pointerDepth ); } for ( i = 0; classInfo->variables[i].name != NULL; i++ ) { const classVariableInfo_t &classVar = classInfo->variables[i]; void *varPtr = (void *) (((byte *)classPtr) + classVar.offset); WriteVariable_r( varPtr, classVar.name, classVar.type, classType, prefix, pointerDepth ); } } /* ================ idTypeInfoTools::WriteGameState ================ */ void idTypeInfoTools::WriteGameState( const char *fileName ) { int i, num; idFile *file; file = fileSystem->OpenFileWrite( fileName ); if ( !file ) { common->Warning( "couldn't open %s", fileName ); return; } fp = file; Write = WriteGameStateVariable; //WriteVariable; #ifdef DUMP_GAMELOCAL file->WriteFloatString( "\ngameLocal {\n" ); WriteClass_r( (void *)&gameLocal, "", "idGameLocal", "idGameLocal", "", 0 ); file->WriteFloatString( "}\n" ); #endif for ( num = i = 0; i < gameLocal.num_entities; i++ ) { idEntity *ent = gameLocal.entities[i]; if ( ent == NULL ) { continue; } file->WriteFloatString( "\nentity %d %s {\n", i, ent->GetType()->classname ); WriteClass_r( (void *)ent, "", ent->GetType()->classname, ent->GetType()->classname, "", 0 ); file->WriteFloatString( "}\n" ); num++; } fileSystem->CloseFile( file ); common->Printf( "%d entities written\n", num ); } /* ================ idTypeInfoTools::CompareGameState ================ */ void idTypeInfoTools::CompareGameState( const char *fileName ) { int entityNum; idToken token; src = new idLexer(); src->SetFlags( LEXFL_NOSTRINGESCAPECHARS ); if ( !src->LoadFile( fileName ) ) { common->Warning( "couldn't load %s", fileName ); delete src; src = NULL; return; } fp = NULL; Write = VerifyVariable; #ifdef DUMP_GAMELOCAL if ( !src->ExpectTokenString( "gameLocal" ) || !src->ExpectTokenString( "{" ) ) { delete src; src = NULL; return; } WriteClass_r( (void *)&gameLocal, "", "idGameLocal", "idGameLocal", "", 0 ); if ( !src->ExpectTokenString( "}" ) ) { delete src; src = NULL; return; } #endif while( src->ReadToken( &token ) ) { if ( token != "entity" ) { break; } if ( !src->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) { break; } entityNum = token.GetIntValue(); if ( entityNum < 0 || entityNum >= gameLocal.num_entities ) { src->Warning( "entity number %d out of range", entityNum ); break; } typeError = false; idEntity *ent = gameLocal.entities[entityNum]; if ( !ent ) { src->Warning( "entity %d is not spawned", entityNum ); src->SkipBracedSection( true ); continue; } if ( !src->ExpectTokenType( TT_NAME, 0, &token ) ) { break; } if ( token.Cmp( ent->GetType()->classname ) != 0 ) { src->Warning( "entity %d has wrong type", entityNum ); src->SkipBracedSection( true ); continue; } if ( !src->ExpectTokenString( "{" ) ) { src->Warning( "entity %d missing leading {", entityNum ); break; } WriteClass_r( (void *)ent, "", ent->GetType()->classname, ent->GetType()->classname, "", 0 ); if ( !src->SkipBracedSection( false ) ) { src->Warning( "entity %d missing trailing }", entityNum ); break; } } delete src; src = NULL; } /* ================ WriteGameState_f ================ */ void WriteGameState_f( const idCmdArgs &args ) { idStr fileName; if ( args.Argc() > 1 ) { fileName = args.Argv(1); } else { fileName = "GameState.txt"; } fileName.SetFileExtension( "gameState.txt" ); idTypeInfoTools::WriteGameState( fileName ); } /* ================ CompareGameState_f ================ */ void CompareGameState_f( const idCmdArgs &args ) { idStr fileName; if ( args.Argc() > 1 ) { fileName = args.Argv(1); } else { fileName = "GameState.txt"; } fileName.SetFileExtension( "gameState.txt" ); idTypeInfoTools::CompareGameState( fileName ); } /* ================ TestSaveGame_f ================ */ void TestSaveGame_f( const idCmdArgs &args ) { idStr name; if ( args.Argc() <= 1 ) { gameLocal.Printf( "testSaveGame \n" ); return; } name = args.Argv( 1 ); try { cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "map %s", name.c_str() ) ); name.Replace( "\\", "_" ); name.Replace( "/", "_" ); cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "saveGame test_%s", name.c_str() ) ); cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "loadGame test_%s", name.c_str() ) ); } catch( idException & ) { // an ERR_DROP was thrown } cmdSystem->BufferCommandText( CMD_EXEC_NOW, "quit" ); } /* ================ WriteTypeToFile ================ */ void WriteTypeToFile( idFile *fp, const void *typePtr, const char *typeName ) { idTypeInfoTools::WriteTypeToFile( fp, typePtr, typeName ); } /* ================ PrintType ================ */ void PrintType( const void *typePtr, const char *typeName ) { idTypeInfoTools::PrintType( typePtr, typeName ); } /* ================ InitTypeVariables ================ */ void InitTypeVariables( const void *typePtr, const char *typeName, int value ) { idTypeInfoTools::InitTypeVariables( typePtr, typeName, value ); } /* ================ ListTypeInfo_f ================ */ int SortTypeInfoByName( const int *a, const int *b ) { return idStr::Icmp( classTypeInfo[*a].typeName, classTypeInfo[*b].typeName ); } int SortTypeInfoBySize( const int *a, const int *b ) { if ( classTypeInfo[*a].size < classTypeInfo[*b].size ) { return -1; } if ( classTypeInfo[*a].size > classTypeInfo[*b].size ) { return 1; } return 0; } void ListTypeInfo_f( const idCmdArgs &args ) { int i, j; idList index; common->Printf( "%-32s : %-32s size (B)\n", "type name", "super type name" ); for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) { index.Append( i ); } if ( args.Argc() > 1 && idStr::Icmp( args.Argv( 1 ), "size" ) == 0 ) { index.Sort( SortTypeInfoBySize ); } else { index.Sort( SortTypeInfoByName ); } for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) { j = index[i]; common->Printf( "%-32s : %-32s %d\n", classTypeInfo[j].typeName, classTypeInfo[j].superType, classTypeInfo[j].size ); } }