Using Sys_FPrintf with SYS_WRN and SYS_ERR
[xonotic/netradiant.git] / tools / quake3 / common / aselib.c
1 /*
2    Copyright (C) 1999-2006 Id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22
23 #include "aselib.h"
24 #include "inout.h"
25
26 #include <assert.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #define MAX_ASE_MATERIALS           32
31 #define MAX_ASE_OBJECTS             64
32 #define MAX_ASE_ANIMATIONS          32
33 #define MAX_ASE_ANIMATION_FRAMES    512
34
35 #define VERBOSE( x ) { if ( ase.verbose ) { Sys_Printf x ; } }
36
37 typedef struct
38 {
39         float x, y, z;
40         float nx, ny, nz;
41         float s, t;
42 } aseVertex_t;
43
44 typedef struct
45 {
46         float s, t;
47 } aseTVertex_t;
48
49 typedef int aseFace_t[3];
50
51 typedef struct
52 {
53         int numFaces;
54         int numVertexes;
55         int numTVertexes;
56
57         int timeValue;
58
59         aseVertex_t     *vertexes;
60         aseTVertex_t    *tvertexes;
61         aseFace_t       *faces, *tfaces;
62
63         int currentFace, currentVertex;
64 } aseMesh_t;
65
66 typedef struct
67 {
68         int numFrames;
69         aseMesh_t frames[MAX_ASE_ANIMATION_FRAMES];
70
71         int currentFrame;
72 } aseMeshAnimation_t;
73
74 typedef struct
75 {
76         char name[128];
77 } aseMaterial_t;
78
79 /*
80 ** contains the animate sequence of a single surface
81 ** using a single material
82 */
83 typedef struct
84 {
85         char name[128];
86
87         int materialRef;
88         int numAnimations;
89
90         aseMeshAnimation_t anim;
91
92 } aseGeomObject_t;
93
94 typedef struct
95 {
96         int numMaterials;
97         aseMaterial_t materials[MAX_ASE_MATERIALS];
98         aseGeomObject_t objects[MAX_ASE_OBJECTS];
99
100         char    *buffer;
101         char    *curpos;
102         int len;
103
104         int currentObject;
105         qboolean verbose;
106         qboolean grabAnims;
107
108 } ase_t;
109
110 static char s_token[1024];
111 static ase_t ase;
112 static char gl_filename[1024];
113
114 static void ASE_Process( void );
115 static void ASE_FreeGeomObject( int ndx );
116
117 #if defined ( __linux__ ) || defined ( __APPLE__ )
118
119 static char* strlwr( char* string ){
120         char *cp;
121         for ( cp = string; *cp; ++cp )
122         {
123                 if ( 'A' <= *cp && *cp <= 'Z' ) {
124                         *cp += 'a' - 'A';
125                 }
126         }
127
128         return string;
129 }
130
131 #endif
132
133 /*
134 ** ASE_Load
135 */
136 void ASE_Load( const char *filename, qboolean verbose, qboolean grabAnims ){
137         FILE *fp = fopen( filename, "rb" );
138
139         if ( !fp ) {
140                 Error( "File not found '%s'", filename );
141         }
142
143         memset( &ase, 0, sizeof( ase ) );
144
145         ase.verbose = verbose;
146         ase.grabAnims = grabAnims;
147         ase.len = Q_filelength( fp );
148
149         ase.curpos = ase.buffer = safe_malloc( ase.len );
150
151         Sys_Printf( "Processing '%s'\n", filename );
152
153         if ( fread( ase.buffer, ase.len, 1, fp ) != 1 ) {
154                 fclose( fp );
155                 Error( "fread() != -1 for '%s'", filename );
156         }
157
158         fclose( fp );
159
160         strcpy( gl_filename, filename );
161
162         ASE_Process();
163 }
164
165 /*
166 ** ASE_Free
167 */
168 void ASE_Free( void ){
169         int i;
170
171         for ( i = 0; i < ase.currentObject; i++ )
172         {
173                 ASE_FreeGeomObject( i );
174         }
175 }
176
177 /*
178 ** ASE_GetNumSurfaces
179 */
180 int ASE_GetNumSurfaces( void ){
181         return ase.currentObject;
182 }
183
184 /*
185 ** ASE_GetSurfaceName
186 */
187 const char *ASE_GetSurfaceName( int which ){
188         aseGeomObject_t *pObject = &ase.objects[which];
189
190         if ( !pObject->anim.numFrames ) {
191                 return 0;
192         }
193
194         return pObject->name;
195 }
196
197 /*
198 ** ASE_GetSurfaceAnimation
199 **
200 ** Returns an animation (sequence of polysets)
201 */
202 polyset_t *ASE_GetSurfaceAnimation( int which, int *pNumFrames, int skipFrameStart, int skipFrameEnd, int maxFrames ){
203         aseGeomObject_t *pObject = &ase.objects[which];
204         polyset_t *psets;
205         int numFramesInAnimation;
206         int numFramesToKeep;
207         int i, f;
208
209         if ( !pObject->anim.numFrames ) {
210                 return 0;
211         }
212
213         if ( pObject->anim.numFrames > maxFrames && maxFrames != -1 ) {
214                 numFramesInAnimation = maxFrames;
215         }
216         else
217         {
218                 numFramesInAnimation = pObject->anim.numFrames;
219                 if ( maxFrames != -1 ) {
220                         Sys_FPrintf( SYS_WRN, "WARNING: ASE_GetSurfaceAnimation maxFrames > numFramesInAnimation\n" );
221                 }
222         }
223
224         if ( skipFrameEnd != -1 ) {
225                 numFramesToKeep = numFramesInAnimation - ( skipFrameEnd - skipFrameStart + 1 );
226         }
227         else{
228                 numFramesToKeep = numFramesInAnimation;
229         }
230
231         *pNumFrames = numFramesToKeep;
232
233         psets = calloc( sizeof( polyset_t ) * numFramesToKeep, 1 );
234
235         for ( f = 0, i = 0; i < numFramesInAnimation; i++ )
236         {
237                 int t;
238                 aseMesh_t *pMesh = &pObject->anim.frames[i];
239
240                 if ( skipFrameStart != -1 ) {
241                         if ( i >= skipFrameStart && i <= skipFrameEnd ) {
242                                 continue;
243                         }
244                 }
245
246                 strcpy( psets[f].name, pObject->name );
247                 strcpy( psets[f].materialname, ase.materials[pObject->materialRef].name );
248
249                 psets[f].triangles = calloc( sizeof( triangle_t ) * pObject->anim.frames[i].numFaces, 1 );
250                 psets[f].numtriangles = pObject->anim.frames[i].numFaces;
251
252                 for ( t = 0; t < pObject->anim.frames[i].numFaces; t++ )
253                 {
254                         int k;
255
256                         for ( k = 0; k < 3; k++ )
257                         {
258                                 psets[f].triangles[t].verts[k][0] = pMesh->vertexes[pMesh->faces[t][k]].x;
259                                 psets[f].triangles[t].verts[k][1] = pMesh->vertexes[pMesh->faces[t][k]].y;
260                                 psets[f].triangles[t].verts[k][2] = pMesh->vertexes[pMesh->faces[t][k]].z;
261
262                                 if ( pMesh->tvertexes && pMesh->tfaces ) {
263                                         psets[f].triangles[t].texcoords[k][0] = pMesh->tvertexes[pMesh->tfaces[t][k]].s;
264                                         psets[f].triangles[t].texcoords[k][1] = pMesh->tvertexes[pMesh->tfaces[t][k]].t;
265                                 }
266
267                         }
268                 }
269
270                 f++;
271         }
272
273         return psets;
274 }
275
276 static void ASE_FreeGeomObject( int ndx ){
277         aseGeomObject_t *pObject;
278         int i;
279
280         pObject = &ase.objects[ndx];
281
282         for ( i = 0; i < pObject->anim.numFrames; i++ )
283         {
284                 if ( pObject->anim.frames[i].vertexes ) {
285                         free( pObject->anim.frames[i].vertexes );
286                 }
287                 if ( pObject->anim.frames[i].tvertexes ) {
288                         free( pObject->anim.frames[i].tvertexes );
289                 }
290                 if ( pObject->anim.frames[i].faces ) {
291                         free( pObject->anim.frames[i].faces );
292                 }
293                 if ( pObject->anim.frames[i].tfaces ) {
294                         free( pObject->anim.frames[i].tfaces );
295                 }
296         }
297
298         memset( pObject, 0, sizeof( *pObject ) );
299 }
300
301 static aseMesh_t *ASE_GetCurrentMesh( void ){
302         aseGeomObject_t *pObject;
303
304         if ( ase.currentObject >= MAX_ASE_OBJECTS ) {
305                 Error( "Too many GEOMOBJECTs" );
306                 return 0; // never called
307         }
308
309         pObject = &ase.objects[ase.currentObject];
310
311         if ( pObject->anim.currentFrame >= MAX_ASE_ANIMATION_FRAMES ) {
312                 Error( "Too many MESHes" );
313                 return 0;
314         }
315
316         return &pObject->anim.frames[pObject->anim.currentFrame];
317 }
318
319 static int CharIsTokenDelimiter( int ch ){
320         if ( ch <= 32 ) {
321                 return 1;
322         }
323         return 0;
324 }
325
326 static int ASE_GetToken( qboolean restOfLine ){
327         int i = 0;
328
329         if ( ase.buffer == 0 ) {
330                 return 0;
331         }
332
333         if ( ( ase.curpos - ase.buffer ) == ase.len ) {
334                 return 0;
335         }
336
337         // skip over crap
338         while ( ( ( ase.curpos - ase.buffer ) < ase.len ) &&
339                         ( *ase.curpos <= 32 ) )
340         {
341                 ase.curpos++;
342         }
343
344         while ( ( ase.curpos - ase.buffer ) < ase.len )
345         {
346                 s_token[i] = *ase.curpos;
347
348                 ase.curpos++;
349                 i++;
350
351                 if ( ( CharIsTokenDelimiter( s_token[i - 1] ) && !restOfLine ) ||
352                          ( ( s_token[i - 1] == '\n' ) || ( s_token[i - 1] == '\r' ) ) ) {
353                         s_token[i - 1] = 0;
354                         break;
355                 }
356         }
357
358         s_token[i] = 0;
359
360         return 1;
361 }
362
363 static void ASE_ParseBracedBlock( void ( *parser )( const char *token ) ){
364         int indent = 0;
365
366         while ( ASE_GetToken( qfalse ) )
367         {
368                 if ( !strcmp( s_token, "{" ) ) {
369                         indent++;
370                 }
371                 else if ( !strcmp( s_token, "}" ) ) {
372                         --indent;
373                         if ( indent == 0 ) {
374                                 break;
375                         }
376                         else if ( indent < 0 ) {
377                                 Error( "Unexpected '}'" );
378                         }
379                 }
380                 else
381                 {
382                         if ( parser ) {
383                                 parser( s_token );
384                         }
385                 }
386         }
387 }
388
389 static void ASE_SkipEnclosingBraces( void ){
390         int indent = 0;
391
392         while ( ASE_GetToken( qfalse ) )
393         {
394                 if ( !strcmp( s_token, "{" ) ) {
395                         indent++;
396                 }
397                 else if ( !strcmp( s_token, "}" ) ) {
398                         indent--;
399                         if ( indent == 0 ) {
400                                 break;
401                         }
402                         else if ( indent < 0 ) {
403                                 Error( "Unexpected '}'" );
404                         }
405                 }
406         }
407 }
408
409 static void ASE_SkipRestOfLine( void ){
410         ASE_GetToken( qtrue );
411 }
412
413 static void ASE_KeyMAP_DIFFUSE( const char *token ){
414         char fullpath[1024], bitmap[1024], modeldir[1024];
415         char filename[1024];
416         int i = 0, count;
417
418         strcpy( filename, gl_filename );
419
420         if ( !strcmp( token, "*BITMAP" ) ) {
421                 ASE_GetToken( qfalse );
422
423                 // the purpose of this whole chunk of code below is to extract the relative path
424                 // from a full path in the ASE
425
426                 strcpy( bitmap, s_token + 1 );
427                 if ( strchr( bitmap, '"' ) ) {
428                         *strchr( bitmap, '"' ) = 0;
429                 }
430
431                 /* convert backslash to slash */
432                 while ( bitmap[i] )
433                 {
434                         if ( bitmap[i] == '\\' ) {
435                                 bitmap[i] = '/';
436                         }
437                         i++;
438                 }
439
440                 /* remove filename from path */
441                 for ( i = strlen( filename ); i > 0; i-- )
442                 {
443                         if ( filename[i] == '/' ) {
444                                 filename[i] = '\0';
445                                 break;
446                         }
447                 }
448
449                 /* replaces a relative path with a full path */
450                 if ( bitmap[0] == '.' && bitmap[1] == '.' && bitmap[2] == '/' ) {
451                         while ( bitmap[0] == '.' && bitmap[1] == '.' && bitmap[2] == '/' )
452                         {
453                                 /* remove last item from path */
454                                 for ( i = strlen( filename ); i > 0; i-- )
455                                 {
456                                         if ( filename[i] == '/' ) {
457                                                 filename[i] = '\0';
458                                                 break;
459                                         }
460                                 }
461                                 strcpy( bitmap, &bitmap[3] );
462                         }
463                         strcat( filename, "/" );
464                         strcat( filename, bitmap );
465                         strcpy( bitmap, filename );
466                 }
467
468                 if ( strstr( bitmap, gamedir ) ) {
469                         strcpy( ase.materials[ase.numMaterials].name, strstr( bitmap, gamedir ) + strlen( gamedir ) );
470                         Sys_Printf( "material name: \'%s\'\n", strstr( bitmap, gamedir ) + strlen( gamedir ) );
471                 }
472                 else
473                 {
474                         sprintf( ase.materials[ase.numMaterials].name, "(not converted: '%s')", bitmap );
475                         Sys_Printf( "WARNING: illegal material name '%s'\n", bitmap );
476                 }
477         }
478         else
479         {
480         }
481 }
482
483 static void ASE_KeyMATERIAL( const char *token ){
484         if ( !strcmp( token, "*MAP_DIFFUSE" ) ) {
485                 ASE_ParseBracedBlock( ASE_KeyMAP_DIFFUSE );
486         }
487         else
488         {
489         }
490 }
491
492 static void ASE_KeyMATERIAL_LIST( const char *token ){
493         if ( !strcmp( token, "*MATERIAL_COUNT" ) ) {
494                 ASE_GetToken( qfalse );
495                 VERBOSE( ( "..num materials: %s\n", s_token ) );
496                 if ( atoi( s_token ) > MAX_ASE_MATERIALS ) {
497                         Error( "Too many materials!" );
498                 }
499                 ase.numMaterials = 0;
500         }
501         else if ( !strcmp( token, "*MATERIAL" ) ) {
502                 VERBOSE( ( "..material %d ", ase.numMaterials ) );
503                 ASE_ParseBracedBlock( ASE_KeyMATERIAL );
504                 ase.numMaterials++;
505         }
506 }
507
508 static void ASE_KeyMESH_VERTEX_LIST( const char *token ){
509         aseMesh_t *pMesh = ASE_GetCurrentMesh();
510
511         if ( !strcmp( token, "*MESH_VERTEX" ) ) {
512                 ASE_GetToken( qfalse );     // skip number
513
514                 ASE_GetToken( qfalse );
515                 pMesh->vertexes[pMesh->currentVertex].y = atof( s_token );
516
517                 ASE_GetToken( qfalse );
518                 pMesh->vertexes[pMesh->currentVertex].x = -atof( s_token );
519
520                 ASE_GetToken( qfalse );
521                 pMesh->vertexes[pMesh->currentVertex].z = atof( s_token );
522
523                 pMesh->currentVertex++;
524
525                 if ( pMesh->currentVertex > pMesh->numVertexes ) {
526                         Error( "pMesh->currentVertex >= pMesh->numVertexes" );
527                 }
528         }
529         else
530         {
531                 Error( "Unknown token '%s' while parsing MESH_VERTEX_LIST", token );
532         }
533 }
534
535 static void ASE_KeyMESH_FACE_LIST( const char *token ){
536         aseMesh_t *pMesh = ASE_GetCurrentMesh();
537
538         if ( !strcmp( token, "*MESH_FACE" ) ) {
539                 ASE_GetToken( qfalse ); // skip face number
540
541                 ASE_GetToken( qfalse ); // skip label
542                 ASE_GetToken( qfalse ); // first vertex
543                 pMesh->faces[pMesh->currentFace][0] = atoi( s_token );
544
545                 ASE_GetToken( qfalse ); // skip label
546                 ASE_GetToken( qfalse ); // second vertex
547                 pMesh->faces[pMesh->currentFace][2] = atoi( s_token );
548
549                 ASE_GetToken( qfalse ); // skip label
550                 ASE_GetToken( qfalse ); // third vertex
551                 pMesh->faces[pMesh->currentFace][1] = atoi( s_token );
552
553                 ASE_GetToken( qtrue );
554
555 /*
556         if ( ( p = strstr( s_token, "*MESH_MTLID" ) ) != 0 )
557         {
558             p += strlen( "*MESH_MTLID" ) + 1;
559             mtlID = atoi( p );
560         }
561         else
562         {
563             Error( "No *MESH_MTLID found for face!" );
564         }
565  */
566
567                 pMesh->currentFace++;
568         }
569         else
570         {
571                 Error( "Unknown token '%s' while parsing MESH_FACE_LIST", token );
572         }
573 }
574
575 static void ASE_KeyTFACE_LIST( const char *token ){
576         aseMesh_t *pMesh = ASE_GetCurrentMesh();
577
578         if ( !strcmp( token, "*MESH_TFACE" ) ) {
579                 int a, b, c;
580
581                 ASE_GetToken( qfalse );
582
583                 ASE_GetToken( qfalse );
584                 a = atoi( s_token );
585                 ASE_GetToken( qfalse );
586                 c = atoi( s_token );
587                 ASE_GetToken( qfalse );
588                 b = atoi( s_token );
589
590                 pMesh->tfaces[pMesh->currentFace][0] = a;
591                 pMesh->tfaces[pMesh->currentFace][1] = b;
592                 pMesh->tfaces[pMesh->currentFace][2] = c;
593
594                 pMesh->currentFace++;
595         }
596         else
597         {
598                 Error( "Unknown token '%s' in MESH_TFACE", token );
599         }
600 }
601
602 static void ASE_KeyMESH_TVERTLIST( const char *token ){
603         aseMesh_t *pMesh = ASE_GetCurrentMesh();
604
605         if ( !strcmp( token, "*MESH_TVERT" ) ) {
606                 char u[80], v[80], w[80];
607
608                 ASE_GetToken( qfalse );
609
610                 ASE_GetToken( qfalse );
611                 strcpy( u, s_token );
612
613                 ASE_GetToken( qfalse );
614                 strcpy( v, s_token );
615
616                 ASE_GetToken( qfalse );
617                 strcpy( w, s_token );
618
619                 pMesh->tvertexes[pMesh->currentVertex].s = atof( u );
620                 pMesh->tvertexes[pMesh->currentVertex].t = 1.0f - atof( v );
621
622                 pMesh->currentVertex++;
623
624                 if ( pMesh->currentVertex > pMesh->numTVertexes ) {
625                         Error( "pMesh->currentVertex > pMesh->numTVertexes" );
626                 }
627         }
628         else
629         {
630                 Error( "Unknown token '%s' while parsing MESH_TVERTLIST", token );
631         }
632 }
633
634 static void ASE_KeyMESH( const char *token ){
635         aseMesh_t *pMesh = ASE_GetCurrentMesh();
636
637         if ( !strcmp( token, "*TIMEVALUE" ) ) {
638                 ASE_GetToken( qfalse );
639
640                 pMesh->timeValue = atoi( s_token );
641                 VERBOSE( ( ".....timevalue: %d\n", pMesh->timeValue ) );
642         }
643         else if ( !strcmp( token, "*MESH_NUMVERTEX" ) ) {
644                 ASE_GetToken( qfalse );
645
646                 pMesh->numVertexes = atoi( s_token );
647                 VERBOSE( ( ".....TIMEVALUE: %d\n", pMesh->timeValue ) );
648                 VERBOSE( ( ".....num vertexes: %d\n", pMesh->numVertexes ) );
649         }
650         else if ( !strcmp( token, "*MESH_NUMFACES" ) ) {
651                 ASE_GetToken( qfalse );
652
653                 pMesh->numFaces = atoi( s_token );
654                 VERBOSE( ( ".....num faces: %d\n", pMesh->numFaces ) );
655         }
656         else if ( !strcmp( token, "*MESH_NUMTVFACES" ) ) {
657                 ASE_GetToken( qfalse );
658
659                 if ( atoi( s_token ) != pMesh->numFaces ) {
660                         Error( "MESH_NUMTVFACES != MESH_NUMFACES" );
661                 }
662         }
663         else if ( !strcmp( token, "*MESH_NUMTVERTEX" ) ) {
664                 ASE_GetToken( qfalse );
665
666                 pMesh->numTVertexes = atoi( s_token );
667                 VERBOSE( ( ".....num tvertexes: %d\n", pMesh->numTVertexes ) );
668         }
669         else if ( !strcmp( token, "*MESH_VERTEX_LIST" ) ) {
670                 pMesh->vertexes = calloc( sizeof( aseVertex_t ) * pMesh->numVertexes, 1 );
671                 pMesh->currentVertex = 0;
672                 VERBOSE( ( ".....parsing MESH_VERTEX_LIST\n" ) );
673                 ASE_ParseBracedBlock( ASE_KeyMESH_VERTEX_LIST );
674         }
675         else if ( !strcmp( token, "*MESH_TVERTLIST" ) ) {
676                 pMesh->currentVertex = 0;
677                 pMesh->tvertexes = calloc( sizeof( aseTVertex_t ) * pMesh->numTVertexes, 1 );
678                 VERBOSE( ( ".....parsing MESH_TVERTLIST\n" ) );
679                 ASE_ParseBracedBlock( ASE_KeyMESH_TVERTLIST );
680         }
681         else if ( !strcmp( token, "*MESH_FACE_LIST" ) ) {
682                 pMesh->faces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );
683                 pMesh->currentFace = 0;
684                 VERBOSE( ( ".....parsing MESH_FACE_LIST\n" ) );
685                 ASE_ParseBracedBlock( ASE_KeyMESH_FACE_LIST );
686         }
687         else if ( !strcmp( token, "*MESH_TFACELIST" ) ) {
688                 pMesh->tfaces = calloc( sizeof( aseFace_t ) * pMesh->numFaces, 1 );
689                 pMesh->currentFace = 0;
690                 VERBOSE( ( ".....parsing MESH_TFACE_LIST\n" ) );
691                 ASE_ParseBracedBlock( ASE_KeyTFACE_LIST );
692         }
693         else if ( !strcmp( token, "*MESH_NORMALS" ) ) {
694                 ASE_ParseBracedBlock( 0 );
695         }
696 }
697
698 static void ASE_KeyMESH_ANIMATION( const char *token ){
699         aseMesh_t *pMesh = ASE_GetCurrentMesh();
700
701         // loads a single animation frame
702         if ( !strcmp( token, "*MESH" ) ) {
703                 VERBOSE( ( "...found MESH\n" ) );
704                 assert( pMesh->faces == 0 );
705                 assert( pMesh->vertexes == 0 );
706                 assert( pMesh->tvertexes == 0 );
707                 memset( pMesh, 0, sizeof( *pMesh ) );
708
709                 ASE_ParseBracedBlock( ASE_KeyMESH );
710
711                 if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES ) {
712                         Error( "Too many animation frames" );
713                 }
714         }
715         else
716         {
717                 Error( "Unknown token '%s' while parsing MESH_ANIMATION", token );
718         }
719 }
720
721 static void ASE_KeyGEOMOBJECT( const char *token ){
722         if ( !strcmp( token, "*NODE_NAME" ) ) {
723                 char *name = ase.objects[ase.currentObject].name;
724
725                 ASE_GetToken( qtrue );
726                 VERBOSE( ( " %s\n", s_token ) );
727                 strcpy( ase.objects[ase.currentObject].name, s_token + 1 );
728                 if ( strchr( ase.objects[ase.currentObject].name, '"' ) ) {
729                         *strchr( ase.objects[ase.currentObject].name, '"' ) = 0;
730                 }
731
732                 if ( strstr( name, "tag" ) == name ) {
733                         while ( strchr( name, '_' ) != strrchr( name, '_' ) )
734                         {
735                                 *strrchr( name, '_' ) = 0;
736                         }
737                         while ( strrchr( name, ' ' ) )
738                         {
739                                 *strrchr( name, ' ' ) = 0;
740                         }
741                 }
742         }
743         else if ( !strcmp( token, "*NODE_PARENT" ) ) {
744                 ASE_SkipRestOfLine();
745         }
746         // ignore unused data blocks
747         else if ( !strcmp( token, "*NODE_TM" ) ||
748                           !strcmp( token, "*TM_ANIMATION" ) ) {
749                 ASE_ParseBracedBlock( 0 );
750         }
751         // ignore regular meshes that aren't part of animation
752         else if ( !strcmp( token, "*MESH" ) && !ase.grabAnims ) {
753 /*
754         if ( strstr( ase.objects[ase.currentObject].name, "tag_" ) == ase.objects[ase.currentObject].name )
755         {
756             s_forceStaticMesh = true;
757             ASE_ParseBracedBlock( ASE_KeyMESH );
758             s_forceStaticMesh = false;
759         }
760  */
761                 ASE_ParseBracedBlock( ASE_KeyMESH );
762                 if ( ++ase.objects[ase.currentObject].anim.currentFrame == MAX_ASE_ANIMATION_FRAMES ) {
763                         Error( "Too many animation frames" );
764                 }
765                 ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
766                 ase.objects[ase.currentObject].numAnimations++;
767 /*
768         // ignore meshes that aren't part of animations if this object isn't a
769         // a tag
770         else
771         {
772             ASE_ParseBracedBlock( 0 );
773         }
774  */
775         }
776         // according to spec these are obsolete
777         else if ( !strcmp( token, "*MATERIAL_REF" ) ) {
778                 ASE_GetToken( qfalse );
779
780                 ase.objects[ase.currentObject].materialRef = atoi( s_token );
781         }
782         // loads a sequence of animation frames
783         else if ( !strcmp( token, "*MESH_ANIMATION" ) ) {
784                 if ( ase.grabAnims ) {
785                         VERBOSE( ( "..found MESH_ANIMATION\n" ) );
786
787                         if ( ase.objects[ase.currentObject].numAnimations ) {
788                                 Error( "Multiple MESH_ANIMATIONS within a single GEOM_OBJECT" );
789                         }
790                         ASE_ParseBracedBlock( ASE_KeyMESH_ANIMATION );
791                         ase.objects[ase.currentObject].anim.numFrames = ase.objects[ase.currentObject].anim.currentFrame;
792                         ase.objects[ase.currentObject].numAnimations++;
793                 }
794                 else
795                 {
796                         ASE_SkipEnclosingBraces();
797                 }
798         }
799         // skip unused info
800         else if ( !strcmp( token, "*PROP_MOTIONBLUR" ) ||
801                           !strcmp( token, "*PROP_CASTSHADOW" ) ||
802                           !strcmp( token, "*PROP_RECVSHADOW" ) ) {
803                 ASE_SkipRestOfLine();
804         }
805 }
806
807 static void ConcatenateObjects( aseGeomObject_t *pObjA, aseGeomObject_t *pObjB ){
808 }
809
810 static void CollapseObjects( void ){
811         int i;
812         int numObjects = ase.currentObject;
813
814         for ( i = 0; i < numObjects; i++ )
815         {
816                 int j;
817
818                 // skip tags
819                 if ( strstr( ase.objects[i].name, "tag" ) == ase.objects[i].name ) {
820                         continue;
821                 }
822
823                 if ( !ase.objects[i].numAnimations ) {
824                         continue;
825                 }
826
827                 for ( j = i + 1; j < numObjects; j++ )
828                 {
829                         if ( strstr( ase.objects[j].name, "tag" ) == ase.objects[j].name ) {
830                                 continue;
831                         }
832                         if ( ase.objects[i].materialRef == ase.objects[j].materialRef ) {
833                                 if ( ase.objects[j].numAnimations ) {
834                                         ConcatenateObjects( &ase.objects[i], &ase.objects[j] );
835                                 }
836                         }
837                 }
838         }
839 }
840
841 /*
842 ** ASE_Process
843 */
844 static void ASE_Process( void ){
845         while ( ASE_GetToken( qfalse ) )
846         {
847                 if ( !strcmp( s_token, "*3DSMAX_ASCIIEXPORT" ) ||
848                          !strcmp( s_token, "*COMMENT" ) ) {
849                         ASE_SkipRestOfLine();
850                 }
851                 else if ( !strcmp( s_token, "*SCENE" ) ) {
852                         ASE_SkipEnclosingBraces();
853                 }
854                 else if ( !strcmp( s_token, "*MATERIAL_LIST" ) ) {
855                         VERBOSE( ( "MATERIAL_LIST\n" ) );
856
857                         ASE_ParseBracedBlock( ASE_KeyMATERIAL_LIST );
858                 }
859                 else if ( !strcmp( s_token, "*GEOMOBJECT" ) ) {
860                         VERBOSE( ( "GEOMOBJECT" ) );
861
862                         ASE_ParseBracedBlock( ASE_KeyGEOMOBJECT );
863
864                         if ( strstr( ase.objects[ase.currentObject].name, "Bip" ) ||
865                                  strstr( ase.objects[ase.currentObject].name, "ignore_" ) ) {
866                                 ASE_FreeGeomObject( ase.currentObject );
867                                 VERBOSE( ( "(discarding BIP/ignore object)\n" ) );
868                         }
869                         else if ( ( strstr( ase.objects[ase.currentObject].name, "h_" ) != ase.objects[ase.currentObject].name ) &&
870                                           ( strstr( ase.objects[ase.currentObject].name, "l_" ) != ase.objects[ase.currentObject].name ) &&
871                                           ( strstr( ase.objects[ase.currentObject].name, "u_" ) != ase.objects[ase.currentObject].name ) &&
872                                           ( strstr( ase.objects[ase.currentObject].name, "tag" ) != ase.objects[ase.currentObject].name ) &&
873                                           ase.grabAnims ) {
874                                 VERBOSE( ( "(ignoring improperly labeled object '%s')\n", ase.objects[ase.currentObject].name ) );
875                                 ASE_FreeGeomObject( ase.currentObject );
876                         }
877                         else
878                         {
879                                 if ( ++ase.currentObject == MAX_ASE_OBJECTS ) {
880                                         Error( "Too many GEOMOBJECTs" );
881                                 }
882                         }
883                 }
884                 else if ( s_token[0] ) {
885                         Sys_Printf( "Unknown token '%s'\n", s_token );
886                 }
887         }
888
889         if ( !ase.currentObject ) {
890                 Error( "No animation data!" );
891         }
892
893         CollapseObjects();
894 }