]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/gamesys/TypeInfo.cpp
hello world
[icculus/iodoom3.git] / neo / d3xp / gamesys / TypeInfo.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 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.
23
24 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.
25
26 ===========================================================================
27 */
28
29 // This is real evil but allows the code to inspect arbitrary class variables.
30 #define private         public
31 #define protected       public
32
33 #include "../../idlib/precompiled.h"
34 #pragma hdrstop
35
36 #include "../Game_local.h"
37
38 #ifdef ID_DEBUG_MEMORY
39 #include "GameTypeInfo.h"                               // Make sure this is up to date!
40 #else
41 #include "NoGameTypeInfo.h"
42 #endif
43
44 // disabled because it's adds about 64MB to state dumps and takes a really long time
45 //#define DUMP_GAMELOCAL
46
47
48 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 );
49
50
51 class idTypeInfoTools {
52 public:
53         static const classTypeInfo_t *  FindClassInfo( const char *typeName );
54         static const enumTypeInfo_t *   FindEnumInfo( const char *typeName );
55         static bool                                             IsSubclassOf( const char *typeName, const char *superType );
56         static void                                             PrintType( const void *typePtr, const char *typeName );
57         static void                                             WriteTypeToFile( idFile *fp, const void *typePtr, const char *typeName );
58         static void                                             InitTypeVariables( const void *typePtr, const char *typeName, int value );
59         static void                                             WriteGameState( const char *fileName );
60         static void                                             CompareGameState( const char *fileName );
61
62 private:
63         static idFile *                                 fp;
64         static int                                              initValue;
65         static WriteVariableType_t              Write;
66         static idLexer *                                src;
67         static bool                                             typeError;
68
69         static const char *                             OutputString( const char *string );
70         static bool                                             ParseTemplateArguments( idLexer &src, idStr &arguments );
71         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 );
72         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 );
73         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 );
74         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 );
75         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 );
76         static int                                              WriteVariable_r( const void *varPtr, const char *varName, const char *varType, const char *scope, const char *prefix, const int pointerDepth );
77         static void                                             WriteClass_r( const void *classPtr, const char *className, const char *classType, const char *scope, const char *prefix, const int pointerDepth );
78 };
79
80 idFile *                                                        idTypeInfoTools::fp                     = NULL;
81 int                                                                     idTypeInfoTools::initValue      = 0;
82 WriteVariableType_t                                     idTypeInfoTools::Write          = NULL;
83 idLexer *                                                       idTypeInfoTools::src            = NULL;
84 bool                                                            idTypeInfoTools::typeError      = false;
85
86
87 /*
88 ================
89 GetTypeVariableName
90 ================
91 */
92 const char *GetTypeVariableName( const char *typeName, int offset ) {
93         static char varName[1024];
94         int i;
95
96         for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) {
97                 if ( idStr::Cmp( typeName, classTypeInfo[i].typeName ) == 0 ) {
98                         if ( classTypeInfo[i].variables[0].name != NULL && offset >= classTypeInfo[i].variables[0].offset ) {
99                                 break;
100                         }
101                         typeName = classTypeInfo[i].superType;
102                         if ( *typeName == '\0' ) {
103                                 return "<unknown>";
104                         }
105                         i = -1;
106                 }
107         }
108
109         const classTypeInfo_t &classInfo = classTypeInfo[i];
110
111         for ( i = 0; classInfo.variables[i].name != NULL; i++ ) {
112                 if ( offset <= classInfo.variables[i].offset ) {
113                         break;
114                 }
115         }
116         if ( i == 0 ) {
117                 idStr::snPrintf( varName, sizeof( varName ), "%s::<unknown>", classInfo.typeName );
118         } else {
119                 idStr::snPrintf( varName, sizeof( varName ), "%s::%s", classInfo.typeName, classInfo.variables[i-1].name );
120         }
121         return varName;
122 }
123
124 /*
125 ================
126 idTypeInfoTools::FindClassInfo
127 ================
128 */
129 const classTypeInfo_t *idTypeInfoTools::FindClassInfo( const char *typeName ) {
130         int i;
131
132         for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) {
133                 if ( idStr::Cmp( typeName, classTypeInfo[i].typeName ) == 0 ) {
134                         return &classTypeInfo[i];
135                 }
136         }
137         return NULL;
138 }
139
140 /*
141 ================
142 idTypeInfoTools::FindEnumInfo
143 ================
144 */
145 const enumTypeInfo_t *idTypeInfoTools::FindEnumInfo( const char *typeName ) {
146         int i;
147
148         for ( i = 0; enumTypeInfo[i].typeName != NULL; i++ ) {
149                 if ( idStr::Cmp( typeName, enumTypeInfo[i].typeName ) == 0 ) {
150                         return &enumTypeInfo[i];
151                 }
152         }
153         return NULL;
154 }
155
156 /*
157 ================
158 idTypeInfoTools::IsSubclassOf
159 ================
160 */
161 bool idTypeInfoTools::IsSubclassOf( const char *typeName, const char *superType ) {
162         int i;
163
164         while( *typeName != '\0' ) {
165                 if ( idStr::Cmp( typeName, superType ) == 0 ) {
166                         return true;
167                 }
168                 for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) {
169                         if ( idStr::Cmp( typeName, classTypeInfo[i].typeName ) == 0 ) {
170                                 typeName = classTypeInfo[i].superType;
171                                 break;
172                         }
173                 }
174                 if ( classTypeInfo[i].typeName == NULL ) {
175                         common->Warning( "super class %s not found", typeName );
176                         break;
177                 }
178         }
179         return false;
180 }
181
182 /*
183 ================
184 idTypeInfoTools::OutputString
185 ================
186 */
187 const char *idTypeInfoTools::OutputString( const char *string ) {
188         static int index = 0;
189         static char buffers[4][16384];
190         char *out;
191         int i, c;
192
193         out = buffers[index];
194         index = ( index + 1 ) & 3;
195
196         if ( string == NULL ) {
197                 return NULL;
198         }
199
200         for ( i = 0; i < sizeof( buffers[0] ) - 2; i++ ) {
201                 c = *string++;
202                 switch( c ) {
203                         case '\0': out[i] = '\0'; return out;
204                         case '\\': out[i++] = '\\'; out[i] = '\\'; break;
205                         case '\n': out[i++] = '\\'; out[i] = 'n'; break;
206                         case '\r': out[i++] = '\\'; out[i] = 'r'; break;
207                         case '\t': out[i++] = '\\'; out[i] = 't'; break;
208                         case '\v': out[i++] = '\\'; out[i] = 'v'; break;
209                         default: out[i] = c; break;
210                 }
211         }
212         out[i] = '\0';
213         return out;
214 }
215
216 /*
217 ================
218 idTypeInfoTools::ParseTemplateArguments
219 ================
220 */
221 bool idTypeInfoTools::ParseTemplateArguments( idLexer &src, idStr &arguments ) {
222         int indent;
223         idToken token;
224
225         arguments = "";
226
227         if ( !src.ExpectTokenString( "<" ) ) {
228                 return false;
229         }
230
231         indent = 1;
232         while( indent ) {
233                 if ( !src.ReadToken( &token ) ) {
234                         break;
235                 }
236                 if ( token == "<" ) {
237                         indent++;
238                 } else if ( token == ">" ) {
239                         indent--;
240                 } else {
241                         if ( arguments.Length() ) {
242                                 arguments += " ";
243                         }
244                         arguments += token;
245                 }
246         }
247         return true;
248 }
249
250 /*
251 ================
252 idTypeInfoTools::PrintType
253 ================
254 */
255 void idTypeInfoTools::PrintType( const void *typePtr, const char *typeName ) {
256         idTypeInfoTools::fp = NULL;
257         idTypeInfoTools::initValue = 0;
258         idTypeInfoTools::Write = PrintVariable;
259         WriteClass_r( typePtr, "", typeName, "", "", 0 );
260 }
261
262 /*
263 ================
264 idTypeInfoTools::WriteTypeToFile
265 ================
266 */
267 void idTypeInfoTools::WriteTypeToFile( idFile *fp, const void *typePtr, const char *typeName ) {
268         idTypeInfoTools::fp = fp;
269         idTypeInfoTools::initValue = 0;
270         idTypeInfoTools::Write = WriteVariable;
271         WriteClass_r( typePtr, "", typeName, "", "", 0 );
272 }
273
274 /*
275 ================
276 idTypeInfoTools::InitTypeVariables
277 ================
278 */
279 void idTypeInfoTools::InitTypeVariables( const void *typePtr, const char *typeName, int value ) {
280         idTypeInfoTools::fp = NULL;
281         idTypeInfoTools::initValue = value;
282         idTypeInfoTools::Write = InitVariable;
283         WriteClass_r( typePtr, "", typeName, "", "", 0 );
284 }
285
286 /*
287 ================
288 IsAllowedToChangedFromSaveGames
289 ================
290 */
291 bool IsAllowedToChangedFromSaveGames( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value ) {
292         if ( idStr::Icmp( scope, "idAnimator" ) == 0 ) {
293                 if ( idStr::Icmp( varName, "forceUpdate" ) == 0 ) {
294                         return true;
295                 }
296                 if ( idStr::Icmp( varName, "lastTransformTime" ) == 0 ) {
297                         return true;
298                 }
299                 if ( idStr::Icmp( varName, "AFPoseTime" ) == 0 ) {
300                         return true;
301                 }
302                 if ( idStr::Icmp( varName, "frameBounds" ) == 0 ) {
303                         return true;
304                 }
305         } else if ( idStr::Icmp( scope, "idClipModel" ) == 0 ) {
306                 if ( idStr::Icmp( varName, "touchCount" ) == 0 ) {
307                         return true;
308                 }
309         } else if ( idStr::Icmp( scope, "idEntity" ) == 0 ) {
310                 if ( idStr::Icmp( varName, "numPVSAreas" ) == 0 ) {
311                         return true;
312                 }
313                 if ( idStr::Icmp( varName, "renderView" ) == 0 ) {
314                         return true;
315                 }
316         } else if ( idStr::Icmp( scope, "idBrittleFracture" ) == 0 ) {
317                 if ( idStr::Icmp( varName, "changed" ) == 0 ) {
318                         return true;
319                 }
320         } else if ( idStr::Icmp( scope, "idPhysics_AF" ) == 0 ) {
321                 return true;
322         } else if ( idStr::Icmp( scope, "renderEntity_t" ) == 0 ) {
323                 // These get fixed up when UpdateVisuals is called
324                 if ( idStr::Icmp( varName, "origin" ) == 0 ) {
325                         return true;
326                 }
327                 if ( idStr::Icmp( varName, "axis" ) == 0 ) {
328                         return true;
329                 }
330                 if ( idStr::Icmp( varName, "bounds" ) == 0 ) {
331                         return true;
332                 }
333         }
334
335         if ( idStr::Icmpn( prefix, "idAFEntity_Base::af.idAF::physicsObj.idPhysics_AF", 49) == 0 ) {
336                 return true;
337         }
338
339         return false;
340 }
341
342 /*
343 ================
344 IsRenderHandleVariable
345 ================
346 */
347 bool IsRenderHandleVariable( const char *varName, const char *varType, const char *scope, const char *prefix, const char *postfix, const char *value ) {
348         if ( idStr::Icmp( scope, "idClipModel" ) == 0 ) {
349                 if ( idStr::Icmp( varName, "renderModelHandle" ) == 0 ) {
350                         return true;
351                 }
352         } else if ( idStr::Icmp( scope, "idFXLocalAction" ) == 0 ) {
353                 if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) {
354                         return true;
355                 }
356                 if ( idStr::Icmp( varName, "modelDefHandle" ) == 0 ) {
357                         return true;
358                 }
359         } else if ( idStr::Icmp( scope, "idEntity" ) == 0 ) {
360                 if ( idStr::Icmp( varName, "modelDefHandle" ) == 0 ) {
361                         return true;
362                 }
363         } else if ( idStr::Icmp( scope, "idLight" ) == 0 ) {
364                 if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) {
365                         return true;
366                 }
367         } else if ( idStr::Icmp( scope, "idAFEntity_Gibbable" ) == 0 ) {
368                 if ( idStr::Icmp( varName, "skeletonModelDefHandle" ) == 0 ) {
369                         return true;
370                 }
371         } else if ( idStr::Icmp( scope, "idAFEntity_SteamPipe" ) == 0 ) {
372                 if ( idStr::Icmp( varName, "steamModelHandle" ) == 0 ) {
373                         return true;
374                 }
375         } else if ( idStr::Icmp( scope, "idItem" ) == 0 ) {
376                 if ( idStr::Icmp( varName, "itemShellHandle" ) == 0 ) {
377                         return true;
378                 }
379         } else if ( idStr::Icmp( scope, "idExplodingBarrel" ) == 0 ) {
380                 if ( idStr::Icmp( varName, "particleModelDefHandle" ) == 0 ) {
381                         return true;
382                 }
383                 if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) {
384                         return true;
385                 }
386         } else if ( idStr::Icmp( scope, "idProjectile" ) == 0 ) {
387                 if ( idStr::Icmp( varName, "lightDefHandle" ) == 0 ) {
388                         return true;
389                 }
390         } else if ( idStr::Icmp( scope, "idBFGProjectile" ) == 0 ) {
391                 if ( idStr::Icmp( varName, "secondModelDefHandle" ) == 0 ) {
392                         return true;
393                 }
394         } else if ( idStr::Icmp( scope, "idSmokeParticles" ) == 0 ) {
395                 if ( idStr::Icmp( varName, "renderEntityHandle" ) == 0 ) {
396                         return true;
397                 }
398         } else if ( idStr::Icmp( scope, "idWeapon" ) == 0 ) {
399                 if ( idStr::Icmp( varName, "muzzleFlashHandle" ) == 0 ) {
400                         return true;
401                 }
402                 if ( idStr::Icmp( varName, "worldMuzzleFlashHandle" ) == 0 ) {
403                         return true;
404                 }
405                 if ( idStr::Icmp( varName, "guiLightHandle" ) == 0 ) {
406                         return true;
407                 }
408                 if ( idStr::Icmp( varName, "nozzleGlowHandle" ) == 0 ) {
409                         return true;
410                 }
411         }
412         return false;
413 }
414
415 /*
416 ================
417 idTypeInfoTools::PrintVariable
418 ================
419 */
420 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 ) {
421         common->Printf( "%s%s::%s%s = \"%s\"\n", prefix, scope, varName, postfix, value );
422 }
423
424 /*
425 ================
426 idTypeInfoTools::WriteVariable
427 ================
428 */
429 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 ) {
430
431         for ( int i = idStr::FindChar( value, '#', 0 ); i >= 0; i = idStr::FindChar( value, '#', i+1 ) ) {
432                 if (    idStr::Icmpn( value+i+1, "INF", 3 ) == 0 ||
433                                 idStr::Icmpn( value+i+1, "IND", 3 ) == 0 ||
434                                 idStr::Icmpn( value+i+1, "NAN", 3 ) == 0 ||
435                                 idStr::Icmpn( value+i+1, "QNAN", 4 ) == 0 ||
436                                 idStr::Icmpn( value+i+1, "SNAN", 4 ) == 0 ) {
437                         common->Warning( "%s%s::%s%s = \"%s\"", prefix, scope, varName, postfix, value );
438                         break;
439                 }
440         }
441         fp->WriteFloatString( "%s%s::%s%s = \"%s\"\n", prefix, scope, varName, postfix, value );
442 }
443
444 /*
445 ================
446 idTypeInfoTools::WriteGameStateVariable
447 ================
448 */
449 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 ) {
450
451         for ( int i = idStr::FindChar( value, '#', 0 ); i >= 0; i = idStr::FindChar( value, '#', i+1 ) ) {
452                 if (    idStr::Icmpn( value+i+1, "INF", 3 ) == 0 ||
453                                 idStr::Icmpn( value+i+1, "IND", 3 ) == 0 ||
454                                 idStr::Icmpn( value+i+1, "NAN", 3 ) == 0 ||
455                                 idStr::Icmpn( value+i+1, "QNAN", 4 ) == 0 ||
456                                 idStr::Icmpn( value+i+1, "SNAN", 4 ) == 0 ) {
457                         common->Warning( "%s%s::%s%s = \"%s\"", prefix, scope, varName, postfix, value );
458                         break;
459                 }
460         }
461
462         if ( IsRenderHandleVariable( varName, varType, scope, prefix, postfix, value ) ) {
463                 return;
464         }
465
466         if ( IsAllowedToChangedFromSaveGames( varName, varType, scope, prefix, postfix, value ) ) {
467                 return;
468         }
469
470         fp->WriteFloatString( "%s%s::%s%s = \"%s\"\n", prefix, scope, varName, postfix, value );
471 }
472
473 /*
474 ================
475 idTypeInfoTools::InitVariable
476 ================
477 */
478 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 ) {
479         if ( varPtr != NULL && varSize > 0 ) {
480                 // NOTE: skip renderer handles
481                 if ( IsRenderHandleVariable( varName, varType, scope, prefix, postfix, value ) ) {
482                         return;
483                 }
484                 memset( const_cast<void*>(varPtr), initValue, varSize );
485         }
486 }
487
488 /*
489 ================
490 idTypeInfoTools::VerifyVariable
491 ================
492 */
493 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 ) {
494         idToken token;
495
496         if ( typeError ) {
497                 return;
498         }
499
500         src->SkipUntilString( "=" );
501         src->ExpectTokenType( TT_STRING, 0, &token );
502         if ( token.Cmp( value ) != 0 ) {
503
504                 // NOTE: skip several things
505
506                 if ( IsRenderHandleVariable( varName, varType, scope, prefix, postfix, value ) ) {
507                         return;
508                 }
509
510                 if ( IsAllowedToChangedFromSaveGames( varName, varType, scope, prefix, postfix, value ) ) {
511                         return;
512                 }
513
514                 src->Warning( "state diff for %s%s::%s%s\n%s\n%s", prefix, scope, varName, postfix, token.c_str(), value );
515                 typeError = true;
516         }
517 }
518
519 /*
520 ================
521 idTypeInfoTools::WriteVariable_r
522 ================
523 */
524 int idTypeInfoTools::WriteVariable_r( const void *varPtr, const char *varName, const char *varType, const char *scope, const char *prefix, const int pointerDepth ) {
525         int i, isPointer, typeSize;
526         idLexer typeSrc;
527         idToken token;
528         idStr typeString, templateArgs;
529
530         isPointer = 0;
531         typeSize = -1;
532
533         // create a type string without 'const', 'mutable', 'class', 'struct', 'union'
534         typeSrc.LoadMemory( varType, idStr::Length( varType ), varName );
535         while( typeSrc.ReadToken( &token ) ) {
536                 if ( token != "const" && token != "mutable" && token != "class" && token != "struct" && token != "union" ) {
537                         typeString += token + " ";
538                 }
539         }
540         typeString.StripTrailing( ' ' );
541         typeSrc.FreeSource();
542
543         // if this is an array
544         if ( typeString[typeString.Length() - 1] == ']' ) {
545                 for ( i = typeString.Length(); i > 0 && typeString[i - 1] != '['; i-- ) {
546                 }
547                 int num = atoi( &typeString[i] );
548                 idStr listVarType = typeString;
549                 listVarType.CapLength( i - 1 );
550                 typeSize = 0;
551                 for ( i = 0; i < num; i++ ) {
552                         idStr listVarName = va( "%s[%d]", varName, i );
553                         int size = WriteVariable_r( varPtr, listVarName, listVarType, scope, prefix, pointerDepth );
554                         typeSize += size;
555                         if ( size == -1 ) {
556                                 break;
557                         }
558                         varPtr = (void *)( ( (byte *) varPtr ) + size );
559                 }
560                 return typeSize;
561         }
562
563         // if this is a pointer
564         isPointer = 0;
565         for ( i = typeString.Length(); i > 0 && typeString[i - 1] == '*'; i -= 2 ) {
566                 if ( varPtr == (void *)0xcdcdcdcd || ( varPtr != NULL && *((unsigned long *)varPtr) == 0xcdcdcdcd ) ) {
567                         common->Warning( "%s%s::%s%s references uninitialized memory", prefix, scope, varName, "" );
568                         return typeSize;
569                 }
570                 if ( varPtr != NULL  ) {
571                         varPtr = *((void **)varPtr);
572                 }
573                 isPointer++;
574         }
575
576         if ( varPtr == NULL ) {
577                 Write( varName, varType, scope, prefix, "", "<NULL>", varPtr, 0 );
578                 return sizeof( void * );
579         }
580
581         typeSrc.LoadMemory( typeString, typeString.Length(), varName );
582
583         if ( !typeSrc.ReadToken( &token ) ) {
584                 Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), varPtr, 0 );
585                 return -1;
586         }
587
588         // get full type
589         while( typeSrc.CheckTokenString( "::" ) ) {
590                 idToken newToken;
591                 typeSrc.ExpectTokenType( TT_NAME, 0, &newToken );
592                 token += "::" + newToken;
593         }
594
595         if ( token == "signed" ) {
596
597                 if ( !typeSrc.ReadToken( &token ) ) {
598                         Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), varPtr, 0 );
599                         return -1;
600                 }
601                 if ( token == "char" ) {
602
603                         typeSize = sizeof( signed char );
604                         Write( varName, varType, scope, prefix, "", va( "%d", *((signed char *)varPtr) ), varPtr, typeSize );
605
606                 } else if ( token == "short" ) {
607
608                         typeSize = sizeof( signed short );
609                         Write( varName, varType, scope, prefix, "", va( "%d", *((signed short *)varPtr) ), varPtr, typeSize );
610
611                 } else if ( token == "int" ) {
612
613                         typeSize = sizeof( signed int );
614                         Write( varName, varType, scope, prefix, "", va( "%d", *((signed int *)varPtr) ), varPtr, typeSize );
615
616                 } else if ( token == "long" ) {
617
618                         typeSize = sizeof( signed long );
619                         Write( varName, varType, scope, prefix, "", va( "%ld", *((signed long *)varPtr) ), varPtr, typeSize );
620
621                 } else {
622
623                         Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), varPtr, 0 );
624                         return -1;
625                 }
626
627         } else if ( token == "unsigned" ) {
628
629                 if ( !typeSrc.ReadToken( &token ) ) {
630                         Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), varPtr, 0 );
631                         return -1;
632                 }
633                 if ( token == "char" ) {
634
635                         typeSize = sizeof( unsigned char );
636                         Write( varName, varType, scope, prefix, "", va( "%d", *((unsigned char *)varPtr) ), varPtr, typeSize );
637
638                 } else if ( token == "short" ) {
639
640                         typeSize = sizeof( unsigned short );
641                         Write( varName, varType, scope, prefix, "", va( "%d", *((unsigned short *)varPtr) ), varPtr, typeSize );
642
643                 } else if ( token == "int" ) {
644
645                         typeSize = sizeof( unsigned int );
646                         Write( varName, varType, scope, prefix, "", va( "%d", *((unsigned int *)varPtr) ), varPtr, typeSize );
647
648                 } else if ( token == "long" ) {
649
650                         typeSize = sizeof( unsigned long );
651                         Write( varName, varType, scope, prefix, "", va( "%lu", *((unsigned long *)varPtr) ), varPtr, typeSize );
652
653                 } else {
654
655                         Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), varPtr, 0 );
656                         return -1;
657                 }
658
659         } else if ( token == "byte" ) {
660
661                 typeSize = sizeof( byte );
662                 Write( varName, varType, scope, prefix, "", va( "%d", *((byte *)varPtr) ), varPtr, typeSize );
663
664         } else if ( token == "word" ) {
665
666                 typeSize = sizeof( word );
667                 Write( varName, varType, scope, prefix, "", va( "%d", *((word *)varPtr) ), varPtr, typeSize );
668
669         } else if ( token == "dword" ) {
670
671                 typeSize = sizeof( dword );
672                 Write( varName, varType, scope, prefix, "", va( "%d", *((dword *)varPtr) ), varPtr, typeSize );
673
674         } else if ( token == "bool" ) {
675
676                 typeSize = sizeof( bool );
677                 Write( varName, varType, scope, prefix, "", va( "%d", *((bool *)varPtr) ), varPtr, typeSize );
678
679         } else if ( token == "char" ) {
680
681                 typeSize = sizeof( char );
682                 Write( varName, varType, scope, prefix, "", va( "%d", *((char *)varPtr) ), varPtr, typeSize );
683
684         } else if ( token == "short" ) {
685
686                 typeSize = sizeof( short );
687                 Write( varName, varType, scope, prefix, "", va( "%d", *((short *)varPtr) ), varPtr, typeSize );
688
689         } else if ( token == "int" ) {
690
691                 typeSize = sizeof( int );
692                 Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), varPtr, typeSize );
693
694         } else if ( token == "long" ) {
695
696                 typeSize = sizeof( long );
697                 Write( varName, varType, scope, prefix, "", va( "%ld", *((long *)varPtr) ), varPtr, typeSize );
698
699         } else if ( token == "float" ) {
700
701                 typeSize = sizeof( float );
702                 Write( varName, varType, scope, prefix, "", idStr( *((float *)varPtr) ).c_str(), varPtr, typeSize );
703
704         } else if ( token == "double" ) {
705
706                 typeSize = sizeof( double );
707                 Write( varName, varType, scope, prefix, "", idStr( (float)*((double *)varPtr) ).c_str(), varPtr, typeSize );
708
709         } else if ( token == "idVec2" ) {
710
711                 typeSize = sizeof( idVec2 );
712                 Write( varName, varType, scope, prefix, "", ((idVec2 *)varPtr)->ToString( 8 ), varPtr, typeSize );
713
714         } else if ( token == "idVec3" ) {
715
716                 typeSize = sizeof( idVec3 );
717                 Write( varName, varType, scope, prefix, "", ((idVec3 *)varPtr)->ToString( 8 ), varPtr, typeSize );
718
719         } else if ( token == "idVec4" ) {
720
721                 typeSize = sizeof( idVec4 );
722                 Write( varName, varType, scope, prefix, "", ((idVec4 *)varPtr)->ToString( 8 ), varPtr, typeSize );
723
724         } else if ( token == "idVec5" ) {
725
726                 typeSize = sizeof( idVec5 );
727                 Write( varName, varType, scope, prefix, "", ((idVec5 *)varPtr)->ToString( 8 ), varPtr, typeSize );
728
729         } else if ( token == "idVec6" ) {
730
731                 typeSize = sizeof( idVec6 );
732                 Write( varName, varType, scope, prefix, "", ((idVec6 *)varPtr)->ToString( 8 ), varPtr, typeSize );
733
734         } else if ( token == "idVecX" ) {
735
736                 const idVecX *vec = ((idVecX *)varPtr);
737                 if ( vec->ToFloatPtr() != NULL ) {
738                         Write( varName, varType, scope, prefix, "", vec->ToString( 8 ), vec->ToFloatPtr(), vec->GetSize() * sizeof( float ) );
739                 } else {
740                         Write( varName, varType, scope, prefix, "", "<NULL>", varPtr, 0 );
741                 }
742                 typeSize = sizeof( idVecX );
743
744         } else if ( token == "idMat2" ) {
745
746                 typeSize = sizeof( idMat2 );
747                 Write( varName, varType, scope, prefix, "", ((idMat2 *)varPtr)->ToString( 8 ), varPtr, typeSize );
748
749         } else if ( token == "idMat3" ) {
750
751                 typeSize = sizeof( idMat3 );
752                 Write( varName, varType, scope, prefix, "", ((idMat3 *)varPtr)->ToString( 8 ), varPtr, typeSize );
753
754         } else if ( token == "idMat4" ) {
755
756                 typeSize = sizeof( idMat4 );
757                 Write( varName, varType, scope, prefix, "", ((idMat4 *)varPtr)->ToString( 8 ), varPtr, typeSize );
758
759         } else if ( token == "idMat5" ) {
760
761                 typeSize = sizeof( idMat5 );
762                 Write( varName, varType, scope, prefix, "", ((idMat5 *)varPtr)->ToString( 8 ), varPtr, typeSize );
763
764         } else if ( token == "idMat6" ) {
765
766                 typeSize = sizeof( idMat6 );
767                 Write( varName, varType, scope, prefix, "", ((idMat6 *)varPtr)->ToString( 8 ), varPtr, typeSize );
768
769         } else if ( token == "idMatX" ) {
770
771                 typeSize = sizeof( idMatX );
772                 const idMatX *mat = ((idMatX *)varPtr);
773                 if ( mat->ToFloatPtr() != NULL ) {
774                         Write( varName, varType, scope, prefix, "", mat->ToString( 8 ), mat->ToFloatPtr(), mat->GetNumColumns() * mat->GetNumRows() * sizeof( float ) );
775                 } else {
776                         Write( varName, varType, scope, prefix, "", "<NULL>", NULL, 0 );
777                 }
778
779         } else if ( token == "idAngles" ) {
780
781                 typeSize = sizeof( idAngles );
782                 Write( varName, varType, scope, prefix, "", ((idAngles *)varPtr)->ToString( 8 ), varPtr, typeSize );
783
784         } else if ( token == "idQuat" ) {
785
786                 typeSize = sizeof( idQuat );
787                 Write( varName, varType, scope, prefix, "", ((idQuat *)varPtr)->ToString( 8 ), varPtr, typeSize );
788
789         } else if ( token == "idBounds" ) {
790
791                 typeSize = sizeof( idBounds );
792                 const idBounds *bounds = ((idBounds *)varPtr);
793                 if ( bounds->IsCleared() ) {
794                         Write( varName, varType, scope, prefix, "", "<cleared>", varPtr, typeSize );
795                 } else {
796                         Write( varName, varType, scope, prefix, "", va( "(%s)-(%s)", (*bounds)[0].ToString( 8 ), (*bounds)[1].ToString( 8 ) ), varPtr, typeSize );
797                 }
798
799         } else if ( token == "idList" ) {
800
801                 idList<int> *list = ((idList<int> *)varPtr);
802                 Write( varName, varType, scope, prefix, ".num", va( "%d", list->Num() ), NULL, 0 );
803                 // NOTE: we don't care about the amount of memory allocated
804                 //Write( varName, varType, scope, prefix, ".size", va( "%d", list->Size() ), NULL, 0 );
805                 Write( varName, varType, scope, prefix, ".granularity", va( "%d", list->GetGranularity() ), NULL, 0 );
806
807                 if ( list->Num() && ParseTemplateArguments( typeSrc, templateArgs ) ) {
808                         void *listVarPtr = list->Ptr();
809                         for ( i = 0; i < list->Num(); i++ ) {
810                                 idStr listVarName = va( "%s[%d]", varName, i );
811                                 int size = WriteVariable_r( listVarPtr, listVarName, templateArgs, scope, prefix, pointerDepth );
812                                 if ( size == -1 ) {
813                                         break;
814                                 }
815                                 listVarPtr = (void *)( ( (byte *) listVarPtr ) + size );
816                         }
817                 }
818
819                 typeSize = sizeof( idList<int> );
820
821         } else if ( token == "idStaticList" ) {
822
823                 idStaticList<int, 1> *list = ((idStaticList<int, 1> *)varPtr);
824                 Write( varName, varType, scope, prefix, ".num", va( "%d", list->Num() ), NULL, 0 );
825
826                 int totalSize = 0;
827                 if ( list->Num() && ParseTemplateArguments( typeSrc, templateArgs ) ) {
828                         void *listVarPtr = list->Ptr();
829                         for ( i = 0; i < list->Num(); i++ ) {
830                                 idStr listVarName = va( "%s[%d]", varName, i );
831                                 int size = WriteVariable_r( listVarPtr, listVarName, templateArgs, scope, prefix, pointerDepth );
832                                 if ( size == -1 ) {
833                                         break;
834                                 }
835                                 totalSize += size;
836                                 listVarPtr = (void *)( ( (byte *) listVarPtr ) + size );
837                         }
838                 }
839
840                 typeSize = sizeof( int ) + totalSize;
841
842         } else if ( token == "idLinkList" ) {
843
844                 // FIXME: implement
845                 typeSize = sizeof( idLinkList<idEntity> );
846                 Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), NULL, 0 );
847
848         } else if ( token == "idStr" ) {
849
850                 typeSize = sizeof( idStr );
851
852                 const idStr *str = ((idStr *)varPtr);
853                 Write( varName, varType, scope, prefix, "", OutputString( str->c_str() ), str->c_str(), str->Length() );
854
855         } else if ( token == "idStrList" ) {
856
857                 typeSize = sizeof( idStrList );
858
859                 const idStrList *list = ((idStrList *)varPtr);
860                 if ( list->Num() ) {
861                         for ( i = 0; i < list->Num(); i++ ) {
862                                 Write( varName, varType, scope, prefix, va("[%d]", i ), OutputString( (*list)[i].c_str() ), (*list)[i].c_str(), (*list)[i].Length() );
863                         }
864                 } else {
865                         Write( varName, varType, scope, prefix, "", "<empty>", NULL, 0 );
866                 }
867
868         } else if ( token == "idDict" ) {
869
870                 typeSize = sizeof( idDict );
871
872                 const idDict *dict = ((idDict *)varPtr);
873                 if ( dict->GetNumKeyVals() ) {
874                         for ( i = 0; i < dict->GetNumKeyVals(); i++ ) {
875                                 const idKeyValue *kv = dict->GetKeyVal( i );
876                                 Write( varName, varType, scope, prefix, va("[%d]", i ), va( "\'%s\'  \'%s\'", OutputString( kv->GetKey().c_str() ), OutputString( kv->GetValue().c_str() ) ), NULL, 0 );
877                         }
878                 } else {
879                         Write( varName, varType, scope, prefix, "", "<empty>", NULL, 0 );
880                 }
881
882         } else if ( token == "idExtrapolate" ) {
883
884                 const idExtrapolate<float> *interpolate = ((idExtrapolate<float> *)varPtr);
885                 Write( varName, varType, scope, prefix, ".extrapolationType", idStr( interpolate->GetExtrapolationType() ).c_str(), &interpolate->extrapolationType, sizeof( interpolate->extrapolationType ) );
886                 Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) );
887                 Write( varName, varType, scope, prefix, ".duration", idStr( interpolate->GetDuration() ).c_str(), &interpolate->duration, sizeof( interpolate->duration ) );
888
889                 if ( ParseTemplateArguments( typeSrc, templateArgs ) ) {
890                         if ( templateArgs == "int" ) {
891                                 const idExtrapolate<int> *interpolate = ((idExtrapolate<int> *)varPtr);
892                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
893                                 Write( varName, varType, scope, prefix, ".baseSpeed", idStr( interpolate->GetBaseSpeed() ).c_str(), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) );
894                                 Write( varName, varType, scope, prefix, ".speed", idStr( interpolate->GetSpeed() ).c_str(), &interpolate->speed, sizeof( interpolate->speed ) );
895                                 typeSize = sizeof( idExtrapolate<int> );
896                         } else if ( templateArgs == "float" ) {
897                                 const idExtrapolate<float> *interpolate = ((idExtrapolate<float> *)varPtr);
898                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
899                                 Write( varName, varType, scope, prefix, ".baseSpeed", idStr( interpolate->GetBaseSpeed() ).c_str(), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) );
900                                 Write( varName, varType, scope, prefix, ".speed", idStr( interpolate->GetSpeed() ).c_str(), &interpolate->speed, sizeof( interpolate->speed ) );
901                                 typeSize = sizeof( idExtrapolate<float> );
902                         } else if ( templateArgs == "idVec3" ) {
903                                 const idExtrapolate<idVec3> *interpolate = ((idExtrapolate<idVec3> *)varPtr);
904                                 Write( varName, varType, scope, prefix, ".startValue", interpolate->GetStartValue().ToString( 8 ), &interpolate->startValue, sizeof( interpolate->startValue ) );
905                                 Write( varName, varType, scope, prefix, ".baseSpeed", interpolate->GetBaseSpeed().ToString( 8 ), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) );
906                                 Write( varName, varType, scope, prefix, ".speed", interpolate->GetSpeed().ToString( 8 ), &interpolate->speed, sizeof( interpolate->speed ) );
907                                 typeSize = sizeof( idExtrapolate<idVec3> );
908                         } else if ( templateArgs == "idAngles" ) {
909                                 const idExtrapolate<idAngles> *interpolate = ((idExtrapolate<idAngles> *)varPtr);
910                                 Write( varName, varType, scope, prefix, ".startValue", interpolate->GetStartValue().ToString( 8 ), &interpolate->startValue, sizeof( interpolate->startValue ) );
911                                 Write( varName, varType, scope, prefix, ".baseSpeed", interpolate->GetBaseSpeed().ToString( 8 ), &interpolate->baseSpeed, sizeof( interpolate->baseSpeed ) );
912                                 Write( varName, varType, scope, prefix, ".speed", interpolate->GetSpeed().ToString( 8 ), &interpolate->speed, sizeof( interpolate->speed ) );
913                                 typeSize = sizeof( idExtrapolate<idAngles> );
914                         } else {
915                                 Write( varName, varType, scope, prefix, "", va( "<unknown template argument type '%s' for idExtrapolate>", templateArgs.c_str() ), NULL, 0 );
916                         }
917                 }
918
919         } else if ( token == "idInterpolate" ) {
920
921                 const idInterpolate<float> *interpolate = ((idInterpolate<float> *)varPtr);
922                 Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) );
923                 Write( varName, varType, scope, prefix, ".duration", idStr( interpolate->GetDuration() ).c_str(), &interpolate->duration, sizeof( interpolate->duration ) );
924
925                 if ( ParseTemplateArguments( typeSrc, templateArgs ) ) {
926                         if ( templateArgs == "int" ) {
927                                 const idInterpolate<int> *interpolate = ((idInterpolate<int> *)varPtr);
928                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
929                                 Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) );
930                                 typeSize = sizeof( idInterpolate<int> );
931                         } else if ( templateArgs == "float" ) {
932                                 const idInterpolate<float> *interpolate = ((idInterpolate<float> *)varPtr);
933                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
934                                 Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) );
935                                 typeSize = sizeof( idInterpolate<float> );
936                         } else {
937                                 Write( varName, varType, scope, prefix, "", va( "<unknown template argument type '%s' for idInterpolate>", templateArgs.c_str() ), NULL, 0 );
938                         }
939                 }
940
941         } else if ( token == "idInterpolateAccelDecelLinear" ) {
942
943                 const idInterpolateAccelDecelLinear<float> *interpolate = ((idInterpolateAccelDecelLinear<float> *)varPtr);
944                 Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) );
945                 Write( varName, varType, scope, prefix, ".accelTime", idStr( interpolate->GetAcceleration() ).c_str(), &interpolate->accelTime, sizeof( interpolate->accelTime ) );
946                 Write( varName, varType, scope, prefix, ".linearTime", idStr( interpolate->linearTime ).c_str(), &interpolate->linearTime, sizeof( interpolate->linearTime ) );
947                 Write( varName, varType, scope, prefix, ".decelTime", idStr( interpolate->GetDeceleration() ).c_str(), &interpolate->decelTime, sizeof( interpolate->decelTime ) );
948
949                 if ( ParseTemplateArguments( typeSrc, templateArgs ) ) {
950                         if ( templateArgs == "int" ) {
951                                 const idInterpolateAccelDecelLinear<int> *interpolate = ((idInterpolateAccelDecelLinear<int> *)varPtr);
952                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
953                                 Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) );
954                                 typeSize = sizeof( idInterpolateAccelDecelLinear<int> );
955                         } else if ( templateArgs == "float" ) {
956                                 const idInterpolateAccelDecelLinear<float> *interpolate = ((idInterpolateAccelDecelLinear<float> *)varPtr);
957                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
958                                 Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) );
959                                 typeSize = sizeof( idInterpolateAccelDecelLinear<float> );
960                         } else {
961                                 Write( varName, varType, scope, prefix, "", va( "<unknown template argument type '%s' for idInterpolateAccelDecelLinear>", templateArgs.c_str() ), NULL, 0 );
962                         }
963                 }
964
965         } else if ( token == "idInterpolateAccelDecelSine" ) {
966
967                 const idInterpolateAccelDecelSine<float> *interpolate = ((idInterpolateAccelDecelSine<float> *)varPtr);
968                 Write( varName, varType, scope, prefix, ".startTime", idStr( interpolate->GetStartTime() ).c_str(), &interpolate->startTime, sizeof( interpolate->startTime ) );
969                 Write( varName, varType, scope, prefix, ".accelTime", idStr( interpolate->GetAcceleration() ).c_str(), &interpolate->accelTime, sizeof( interpolate->accelTime ) );
970                 Write( varName, varType, scope, prefix, ".linearTime", idStr( interpolate->linearTime ).c_str(), &interpolate->linearTime, sizeof( interpolate->linearTime ) );
971                 Write( varName, varType, scope, prefix, ".decelTime", idStr( interpolate->GetDeceleration() ).c_str(), &interpolate->decelTime, sizeof( interpolate->decelTime ) );
972
973                 if ( ParseTemplateArguments( typeSrc, templateArgs ) ) {
974                         if ( templateArgs == "int" ) {
975                                 const idInterpolateAccelDecelSine<int> *interpolate = ((idInterpolateAccelDecelSine<int> *)varPtr);
976                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
977                                 Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) );
978                                 typeSize = sizeof( idInterpolateAccelDecelSine<int> );
979                         } else if ( templateArgs == "float" ) {
980                                 const idInterpolateAccelDecelSine<float> *interpolate = ((idInterpolateAccelDecelSine<float> *)varPtr);
981                                 Write( varName, varType, scope, prefix, ".startValue", idStr( interpolate->GetStartValue() ).c_str(), &interpolate->startValue, sizeof( interpolate->startValue ) );
982                                 Write( varName, varType, scope, prefix, ".endValue", idStr( interpolate->GetEndValue() ).c_str(), &interpolate->endValue, sizeof( interpolate->endValue ) );
983                                 typeSize = sizeof( idInterpolateAccelDecelSine<float> );
984                         } else {
985                                 Write( varName, varType, scope, prefix, "", va( "<unknown template argument type '%s' for idInterpolateAccelDecelSine>", templateArgs.c_str() ), NULL, 0 );
986                         }
987                 }
988
989         } else if ( token == "idUserInterface" ) {
990
991                 typeSize = sizeof( idUserInterface );
992                 const idUserInterface *gui = ((idUserInterface *)varPtr);
993                 Write( varName, varType, scope, prefix, "", gui->Name(), varPtr, sizeof( varPtr ) );
994
995         } else if ( token == "idRenderModel" ) {
996
997                 typeSize = sizeof( idRenderModel );
998                 const idRenderModel *model = ((idRenderModel *)varPtr);
999                 Write( varName, varType, scope, prefix, "", model->Name(), varPtr, sizeof( varPtr ) );
1000
1001         } else if ( token == "qhandle_t" ) {
1002
1003                 typeSize = sizeof( int );
1004                 Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), varPtr, typeSize );
1005
1006         } else if ( token == "cmHandle_t" ) {
1007
1008                 typeSize = sizeof( int );
1009                 Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), varPtr, typeSize );
1010
1011         } else if ( token == "idEntityPtr" ) {
1012
1013                 typeSize = sizeof( idEntityPtr<idEntity> );
1014
1015                 const idEntityPtr<idEntity> *entPtr = ((idEntityPtr<idEntity> *)varPtr);
1016                 if ( entPtr->GetEntity() ) {
1017                         idEntity *entity = entPtr->GetEntity();
1018                         Write( varName, varType, scope, prefix, ".", va( "entity %d: \'%s\'", entity->entityNumber, entity->name.c_str() ), varPtr, typeSize );
1019                 } else {
1020                         Write( varName, varType, scope, prefix, "", "<NULL>", varPtr, typeSize );
1021                 }
1022
1023         } else if ( token == "idEntity::entityFlags_s" ) {
1024
1025                 const idEntity::entityFlags_s *flags = ((idEntity::entityFlags_s *)varPtr);
1026                 Write( varName, varType, scope, prefix, ".notarget", flags->notarget ? "true" : "false", NULL, 0 );
1027                 Write( varName, varType, scope, prefix, ".noknockback", flags->noknockback ? "true" : "false", NULL, 0 );
1028                 Write( varName, varType, scope, prefix, ".takedamage", flags->takedamage ? "true" : "false", NULL, 0 );
1029                 Write( varName, varType, scope, prefix, ".hidden", flags->hidden ? "true" : "false", NULL, 0 );
1030                 Write( varName, varType, scope, prefix, ".bindOrientated", flags->bindOrientated ? "true" : "false", NULL, 0 );
1031                 Write( varName, varType, scope, prefix, ".solidForTeam", flags->solidForTeam ? "true" : "false", NULL, 0 );
1032                 Write( varName, varType, scope, prefix, ".forcePhysicsUpdate", flags->forcePhysicsUpdate ? "true" : "false", NULL, 0 );
1033                 Write( varName, varType, scope, prefix, ".selected", flags->selected ? "true" : "false", NULL, 0 );
1034                 Write( varName, varType, scope, prefix, ".neverDormant", flags->neverDormant ? "true" : "false", NULL, 0 );
1035                 Write( varName, varType, scope, prefix, ".isDormant", flags->isDormant ? "true" : "false", NULL, 0 );
1036                 Write( varName, varType, scope, prefix, ".hasAwakened", flags->hasAwakened ? "true" : "false", NULL, 0 );
1037                 Write( varName, varType, scope, prefix, ".networkSync", flags->networkSync ? "true" : "false", NULL, 0 );
1038                 typeSize = sizeof( idEntity::entityFlags_s );
1039
1040         } else if ( token == "idScriptBool" ) {
1041
1042                 typeSize = sizeof( idScriptBool );
1043
1044                 const idScriptBool *scriptBool = ((idScriptBool *)varPtr);
1045                 if ( scriptBool->IsLinked() ) {
1046                         Write( varName, varType, scope, prefix, "", ( *scriptBool != 0 ) ? "true" : "false", varPtr, typeSize );
1047                 } else {
1048                         Write( varName, varType, scope, prefix, "", "<not linked>", varPtr, typeSize );
1049                 }
1050
1051         } else {
1052
1053                 const classTypeInfo_t *classTypeInfo = FindClassInfo( scope + ( "::" + token ) );
1054                 if ( classTypeInfo == NULL ) {
1055                         classTypeInfo = FindClassInfo( token );
1056                 }
1057                 if ( classTypeInfo != NULL ) {
1058
1059                         typeSize = classTypeInfo->size;
1060
1061                         if ( !isPointer ) {
1062
1063                                 char newPrefix[1024];
1064                                 idStr::snPrintf( newPrefix, sizeof( newPrefix ), "%s%s::%s.", prefix, scope, varName );
1065                                 WriteClass_r( varPtr, "", token, token, newPrefix, pointerDepth );
1066
1067                         } else if ( token == "idAnim" ) {
1068
1069                                 const idAnim *anim = ((idAnim*)varPtr);
1070                                 Write( varName, varType, scope, prefix, "", anim->Name(), NULL, 0 );
1071
1072                         } else if ( token == "idPhysics" ) {
1073
1074                                 const idPhysics *physics = ((idPhysics*)varPtr);
1075                                 Write( varName, varType, scope, prefix, "", physics->GetType()->classname, NULL, 0 );
1076
1077                         } else if ( IsSubclassOf( token, "idEntity" ) ) {
1078
1079                                 const idEntity *entity = ((idEntity*)varPtr);
1080                                 Write( varName, varType, scope, prefix, "", va( "entity %d: \'%s\'", entity->entityNumber, entity->name.c_str() ), NULL, 0 );
1081
1082                         } else if ( IsSubclassOf( token, "idDecl" ) ) {
1083
1084                                 const idDecl *decl = ((idDecl *)varPtr);
1085                                 Write( varName, varType, scope, prefix, "", decl->GetName(), NULL, 0 );
1086
1087                         } else if ( pointerDepth == 0 && (
1088                                                 token == "idAFBody" ||
1089                                                 token == "idAFTree" ||
1090                                                 token == "idClipModel" ||
1091                                                 IsSubclassOf( token, "idAFConstraint" )
1092                                                 ) ) {
1093
1094                                 char newPrefix[1024];
1095                                 idStr::snPrintf( newPrefix, sizeof( newPrefix ), "%s%s::%s->", prefix, scope, varName );
1096                                 WriteClass_r( varPtr, "", token, token, newPrefix, pointerDepth + 1 );
1097
1098                         } else {
1099
1100                                 Write( varName, varType, scope, prefix, "", va( "<pointer type '%s' not listed>", varType ), NULL, 0 );
1101                                 return -1;
1102                         }
1103                 } else {
1104                         const enumTypeInfo_t *enumTypeInfo = FindEnumInfo( scope + ( "::" + token ) );
1105                         if ( enumTypeInfo == NULL ) {
1106                                 enumTypeInfo = FindEnumInfo( token );
1107                         }
1108                         if ( enumTypeInfo != NULL ) {
1109
1110                                 typeSize = sizeof( int );       // NOTE: assuming sizeof( enum ) is sizeof( int )
1111
1112                                 for ( i = 0; enumTypeInfo->values[i].name != NULL; i++ ) {
1113                                         if ( *((int *)varPtr) == enumTypeInfo->values[i].value ) {
1114                                                 break;
1115                                         }
1116                                 }
1117                                 if ( enumTypeInfo->values[i].name != NULL ) {
1118                                         Write( varName, varType, scope, prefix, "", enumTypeInfo->values[i].name, NULL, 0 );
1119                                 } else {
1120                                         Write( varName, varType, scope, prefix, "", va( "%d", *((int *)varPtr) ), NULL, 0 );
1121                                 }
1122
1123                         } else {
1124                                 Write( varName, varType, scope, prefix, "", va( "<unknown type '%s'>", varType ), NULL, 0 );
1125                                 return -1;
1126                         }
1127                 }
1128         }
1129
1130         i = 0;
1131         do {
1132                 if ( *((unsigned long *)varPtr) == 0xcdcdcdcd ) {
1133                         common->Warning( "%s%s::%s%s uses uninitialized memory", prefix, scope, varName, "" );
1134                         break;
1135                 }
1136         } while( ++i < typeSize );
1137
1138         if ( isPointer ) {
1139                 return sizeof( void * );
1140         }
1141         return typeSize;
1142 }
1143
1144 /*
1145 ================
1146 idTypeInfoTools::WriteClass_r
1147 ================
1148 */
1149 void idTypeInfoTools::WriteClass_r( const void *classPtr, const char *className, const char *classType, const char *scope, const char *prefix, const int pointerDepth ) {
1150         int i;
1151
1152         const classTypeInfo_t *classInfo = FindClassInfo( classType );
1153         if ( !classInfo ) {
1154                 return;
1155         }
1156         if ( *classInfo->superType != '\0' ) {
1157                 WriteClass_r( classPtr, className, classInfo->superType, scope, prefix, pointerDepth );
1158         }
1159
1160         for ( i = 0; classInfo->variables[i].name != NULL; i++ ) {
1161                 const classVariableInfo_t &classVar = classInfo->variables[i];
1162
1163                 void *varPtr = (void *) (((byte *)classPtr) + classVar.offset);
1164
1165                 WriteVariable_r( varPtr, classVar.name, classVar.type, classType, prefix, pointerDepth );
1166         }
1167 }
1168
1169 /*
1170 ================
1171 idTypeInfoTools::WriteGameState
1172 ================
1173 */
1174 void idTypeInfoTools::WriteGameState( const char *fileName ) {
1175         int i, num;
1176         idFile *file;
1177
1178         file = fileSystem->OpenFileWrite( fileName );
1179         if ( !file ) {
1180                 common->Warning( "couldn't open %s", fileName );
1181                 return;
1182         }
1183
1184         fp = file;
1185         Write = WriteGameStateVariable; //WriteVariable;
1186
1187 #ifdef DUMP_GAMELOCAL
1188
1189         file->WriteFloatString( "\ngameLocal {\n" );
1190         WriteClass_r( (void *)&gameLocal, "", "idGameLocal", "idGameLocal", "", 0 );
1191         file->WriteFloatString( "}\n" );
1192
1193 #endif
1194
1195         for ( num = i = 0; i < gameLocal.num_entities; i++ ) {
1196                 idEntity *ent = gameLocal.entities[i];
1197                 if ( ent == NULL ) {
1198                         continue;
1199                 }
1200                 file->WriteFloatString( "\nentity %d %s {\n", i, ent->GetType()->classname );
1201                 WriteClass_r( (void *)ent, "", ent->GetType()->classname, ent->GetType()->classname, "", 0 );
1202                 file->WriteFloatString( "}\n" );
1203                 num++;
1204         }
1205
1206         fileSystem->CloseFile( file );
1207
1208         common->Printf( "%d entities written\n", num );
1209 }
1210
1211 /*
1212 ================
1213 idTypeInfoTools::CompareGameState
1214 ================
1215 */
1216 void idTypeInfoTools::CompareGameState( const char *fileName ) {
1217         int entityNum;
1218         idToken token;
1219
1220         src = new idLexer();
1221         src->SetFlags( LEXFL_NOSTRINGESCAPECHARS );
1222
1223         if ( !src->LoadFile( fileName ) ) {
1224                 common->Warning( "couldn't load %s", fileName );
1225                 delete src;
1226                 src = NULL;
1227                 return;
1228         }
1229
1230         fp = NULL;
1231         Write = VerifyVariable;
1232
1233 #ifdef DUMP_GAMELOCAL
1234
1235         if ( !src->ExpectTokenString( "gameLocal" ) || !src->ExpectTokenString( "{" ) ) {
1236                 delete src;
1237                 src = NULL;
1238                 return;
1239         }
1240
1241         WriteClass_r( (void *)&gameLocal, "", "idGameLocal", "idGameLocal", "", 0 );
1242
1243         if ( !src->ExpectTokenString( "}" ) ) {
1244                 delete src;
1245                 src = NULL;
1246                 return;
1247         }
1248
1249 #endif
1250
1251         while( src->ReadToken( &token ) ) {
1252                 if ( token != "entity" ) {
1253                         break;
1254                 }
1255                 if ( !src->ExpectTokenType( TT_NUMBER, TT_INTEGER, &token ) ) {
1256                         break;
1257                 }
1258
1259                 entityNum = token.GetIntValue();
1260
1261                 if ( entityNum < 0 || entityNum >= gameLocal.num_entities ) {
1262                         src->Warning( "entity number %d out of range", entityNum );
1263                         break;
1264                 }
1265
1266                 typeError = false;
1267
1268                 idEntity *ent = gameLocal.entities[entityNum];
1269                 if ( !ent ) {
1270                         src->Warning( "entity %d is not spawned", entityNum );
1271                         src->SkipBracedSection( true );
1272                         continue;
1273                 }
1274
1275                 if ( !src->ExpectTokenType( TT_NAME, 0, &token ) ) {
1276                         break;
1277                 }
1278
1279                 if ( token.Cmp( ent->GetType()->classname ) != 0 ) {
1280                         src->Warning( "entity %d has wrong type", entityNum );
1281                         src->SkipBracedSection( true );
1282                         continue;
1283                 }
1284
1285                 if ( !src->ExpectTokenString( "{" ) ) {
1286                         src->Warning( "entity %d missing leading {", entityNum );
1287                         break;
1288                 }
1289
1290                 WriteClass_r( (void *)ent, "", ent->GetType()->classname, ent->GetType()->classname, "", 0 );
1291
1292                 if ( !src->SkipBracedSection( false ) ) {
1293                         src->Warning( "entity %d missing trailing }", entityNum );
1294                         break;
1295                 }
1296         }
1297
1298         delete src;
1299         src = NULL;
1300 }
1301
1302 /*
1303 ================
1304 WriteGameState_f
1305 ================
1306 */
1307 void WriteGameState_f( const idCmdArgs &args ) {
1308         idStr fileName;
1309
1310         if ( args.Argc() > 1 ) {
1311                 fileName = args.Argv(1);
1312         } else {
1313                 fileName = "GameState.txt";
1314         }
1315         fileName.SetFileExtension( "gameState.txt" );
1316
1317         idTypeInfoTools::WriteGameState( fileName );
1318 }
1319
1320 /*
1321 ================
1322 CompareGameState_f
1323 ================
1324 */
1325 void CompareGameState_f( const idCmdArgs &args ) {
1326         idStr fileName;
1327
1328         if ( args.Argc() > 1 ) {
1329                 fileName = args.Argv(1);
1330         } else {
1331                 fileName = "GameState.txt";
1332         }
1333         fileName.SetFileExtension( "gameState.txt" );
1334
1335         idTypeInfoTools::CompareGameState( fileName );
1336 }
1337
1338 /*
1339 ================
1340 TestSaveGame_f
1341 ================
1342 */
1343 void TestSaveGame_f( const idCmdArgs &args ) {
1344         idStr name;
1345
1346         if ( args.Argc() <= 1 ) {
1347                 gameLocal.Printf( "testSaveGame <mapName>\n" );
1348                 return;
1349         }
1350
1351         name = args.Argv( 1 );
1352
1353         try {
1354                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "map %s", name.c_str() ) );
1355                 name.Replace( "\\", "_" );
1356                 name.Replace( "/", "_" );
1357                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "saveGame test_%s", name.c_str() ) );
1358                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "loadGame test_%s", name.c_str() ) );
1359         }
1360         catch( idException & ) {
1361                 // an ERR_DROP was thrown
1362         }
1363         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "quit" );
1364 }
1365
1366 /*
1367 ================
1368 WriteTypeToFile
1369 ================
1370 */
1371 void WriteTypeToFile( idFile *fp, const void *typePtr, const char *typeName ) {
1372         idTypeInfoTools::WriteTypeToFile( fp, typePtr, typeName );
1373 }
1374
1375 /*
1376 ================
1377 PrintType
1378 ================
1379 */
1380 void PrintType( const void *typePtr, const char *typeName ) {
1381         idTypeInfoTools::PrintType( typePtr, typeName );
1382 }
1383
1384 /*
1385 ================
1386 InitTypeVariables
1387 ================
1388 */
1389 void InitTypeVariables( const void *typePtr, const char *typeName, int value ) {
1390         idTypeInfoTools::InitTypeVariables( typePtr, typeName, value );
1391 }
1392
1393 /*
1394 ================
1395 ListTypeInfo_f
1396 ================
1397 */
1398 int SortTypeInfoByName( const int *a, const int *b ) {
1399         return idStr::Icmp( classTypeInfo[*a].typeName, classTypeInfo[*b].typeName );
1400 }
1401
1402 int SortTypeInfoBySize( const int *a, const int *b ) {
1403         if ( classTypeInfo[*a].size < classTypeInfo[*b].size ) {
1404                 return -1;
1405         }
1406         if ( classTypeInfo[*a].size > classTypeInfo[*b].size ) {
1407                 return 1;
1408         }
1409         return 0;
1410 }
1411
1412 void ListTypeInfo_f( const idCmdArgs &args ) {
1413         int i, j;
1414         idList<int> index;
1415
1416         common->Printf( "%-32s : %-32s size (B)\n", "type name", "super type name" );
1417         for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) {
1418                 index.Append( i );
1419         }
1420
1421         if ( args.Argc() > 1 && idStr::Icmp( args.Argv( 1 ), "size" ) == 0 ) {
1422                 index.Sort( SortTypeInfoBySize );
1423         } else {
1424                 index.Sort( SortTypeInfoByName );
1425         }
1426
1427         for ( i = 0; classTypeInfo[i].typeName != NULL; i++ ) {
1428                 j = index[i];
1429                 common->Printf( "%-32s : %-32s %d\n", classTypeInfo[j].typeName, classTypeInfo[j].superType, classTypeInfo[j].size );
1430         }
1431 }