]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_3ds.c
Wean off #define
[xonotic/netradiant.git] / libs / picomodel / pm_3ds.c
1 /* -----------------------------------------------------------------------------
2
3    PicoModel Library
4
5    Copyright (c) 2002, Randy Reddig & seaw0lf
6    All rights reserved.
7
8    Redistribution and use in source and binary forms, with or without modification,
9    are permitted provided that the following conditions are met:
10
11    Redistributions of source code must retain the above copyright notice, this list
12    of conditions and the following disclaimer.
13
14    Redistributions in binary form must reproduce the above copyright notice, this
15    list of conditions and the following disclaimer in the documentation and/or
16    other materials provided with the distribution.
17
18    Neither the names of the copyright holders nor the names of its contributors may
19    be used to endorse or promote products derived from this software without
20    specific prior written permission.
21
22    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26    ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29    ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
33    ----------------------------------------------------------------------------- */
34
35 /* dependencies */
36 #include "picointernal.h"
37
38 /* ydnar */
39 static picoColor_t white = { 255,255,255,255 };
40
41 /* remarks:
42  * - 3ds file version is stored in pico special field 0 on load (ydnar: removed)
43  * todo:
44  * - sometimes there is one unnamed surface 0 having 0 verts as
45  *   well as 0 faces. this error occurs since pm 0.6 (ydnar?)
46  */
47 /* uncomment when debugging this module */
48 /* #define DEBUG_PM_3DS
49  #define DEBUG_PM_3DS_EX */
50
51 /* structure holding persistent 3ds loader specific data used */
52 /* to store formerly static vars to keep the module reentrant */
53 /* safe. put everything that needs to be static in here. */
54 typedef struct S3dsLoaderPers
55 {
56         picoModel_t    *model;          /* ptr to output model */
57         picoSurface_t  *surface;        /* ptr to current surface */
58         picoShader_t   *shader;         /* ptr to current shader */
59         picoByte_t     *bufptr;         /* ptr to raw data */
60         char           *basename;       /* ptr to model base name (eg. jeep) */
61         int cofs;
62         int maxofs;
63 }
64 T3dsLoaderPers;
65
66 /* 3ds chunk types that we use */
67 enum {
68         /* primary chunk */
69         CHUNK_MAIN              = 0x4D4D,
70
71         /* main chunks */
72         CHUNK_VERSION           = 0x0002,
73         CHUNK_EDITOR_CONFIG     = 0x3D3E,
74         CHUNK_EDITOR_DATA       = 0x3D3D,
75         CHUNK_KEYFRAME_DATA     = 0xB000,
76
77         /* editor data sub chunks */
78         CHUNK_MATERIAL          = 0xAFFF,
79         CHUNK_OBJECT            = 0x4000,
80
81         /* material sub chunks */
82         CHUNK_MATNAME           = 0xA000,
83         CHUNK_MATDIFFUSE        = 0xA020,
84         CHUNK_MATMAP            = 0xA200,
85         CHUNK_MATMAPFILE        = 0xA300,
86
87         /* lets us know we're reading a new object */
88         CHUNK_OBJECT_MESH       = 0x4100,
89
90         /* object mesh sub chunks */
91         CHUNK_OBJECT_VERTICES   = 0x4110,
92         CHUNK_OBJECT_FACES      = 0x4120,
93         CHUNK_OBJECT_MATERIAL   = 0x4130,
94         CHUNK_OBJECT_UV         = 0x4140,
95 };
96 #ifdef DEBUG_PM_3DS
97 static struct
98 {
99         int id;
100         char   *name;
101 }
102 debugChunkNames[] =
103 {
104         { CHUNK_MAIN, "CHUNK_MAIN"              },
105         { CHUNK_VERSION, "CHUNK_VERSION"           },
106         { CHUNK_EDITOR_CONFIG, "CHUNK_EDITOR_CONFIG"     },
107         { CHUNK_EDITOR_DATA, "CHUNK_EDITOR_DATA"       },
108         { CHUNK_KEYFRAME_DATA, "CHUNK_KEYFRAME_DATA"     },
109         { CHUNK_MATERIAL, "CHUNK_MATERIAL"          },
110         { CHUNK_OBJECT, "CHUNK_OBJECT"            },
111         { CHUNK_MATNAME, "CHUNK_MATNAME"           },
112         { CHUNK_MATDIFFUSE, "CHUNK_MATDIFFUSE"        },
113         { CHUNK_MATMAP, "CHUNK_MATMAP"            },
114         { CHUNK_MATMAPFILE, "CHUNK_MATMAPFILE"        },
115         { CHUNK_OBJECT_MESH, "CHUNK_OBJECT_MESH"       },
116         { CHUNK_OBJECT_VERTICES, "CHUNK_OBJECT_VERTICES"   },
117         { CHUNK_OBJECT_FACES, "CHUNK_OBJECT_FACES"      },
118         { CHUNK_OBJECT_MATERIAL, "CHUNK_OBJECT_MATERIAL"   },
119         { CHUNK_OBJECT_UV, "CHUNK_OBJECT_UV"         },
120         { 0,  NULL                     }
121 };
122 static char *DebugGetChunkName( int id ){
123         int i,max;  /* imax? ;) */
124         max = sizeof( debugChunkNames ) / sizeof( debugChunkNames[0] );
125
126         for ( i = 0; i < max; i++ )
127         {
128                 if ( debugChunkNames[i].id == id ) {
129                         /* gaynux update -sea */
130                         return _pico_strlwr( debugChunkNames[i].name );
131                 }
132         }
133         return "chunk_unknown";
134 }
135 #endif /*DEBUG_PM_3DS*/
136
137 /* this funky loader needs byte alignment */
138 #pragma pack(push, 1)
139
140 typedef struct S3dsIndices
141 {
142         unsigned short a,b,c;
143         unsigned short visible;
144 }
145 T3dsIndices;
146
147 typedef struct S3dsChunk
148 {
149         unsigned short id;
150         unsigned int len;
151 }
152 T3dsChunk;
153
154 /* restore previous data alignment */
155 #pragma pack(pop)
156
157 /* _3ds_canload:
158  *  validates an autodesk 3ds model file.
159  */
160 static int _3ds_canload( PM_PARAMS_CANLOAD ){
161         const T3dsChunk *chunk;
162
163         /* sanity check */
164         if ( bufSize < (int) sizeof( T3dsChunk ) ) {
165                 return PICO_PMV_ERROR_SIZE;
166         }
167
168         /* get pointer to 3ds header chunk */
169         chunk = (const T3dsChunk *)buffer;
170
171         /* check data length */
172         if ( bufSize < (int) _pico_little_long( chunk->len ) ) {
173                 return PICO_PMV_ERROR_SIZE;
174         }
175
176         /* check 3ds magic */
177         if ( _pico_little_short( chunk->id ) != CHUNK_MAIN ) {
178                 return PICO_PMV_ERROR_IDENT;
179         }
180
181         /* file seems to be a valid 3ds */
182         return PICO_PMV_OK;
183 }
184
185 static T3dsChunk *GetChunk( T3dsLoaderPers *pers ){
186         T3dsChunk *chunk;
187
188         /* sanity check */
189         if ( pers->cofs > pers->maxofs ) {
190                 return 0;
191         }
192
193 #ifdef DEBUG_PM_3DS
194 /*      printf("GetChunk: pers->cofs %x\n",pers->cofs); */
195 #endif
196         /* fill in pointer to chunk */
197         chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];
198         if ( !chunk ) {
199                 return NULL;
200         }
201
202         chunk->id  = _pico_little_short( chunk->id );
203         chunk->len = _pico_little_long( chunk->len );
204
205         /* advance in buffer */
206         pers->cofs += sizeof( T3dsChunk );
207
208         /* this means yay */
209         return chunk;
210 }
211
212 static int GetASCIIZ( T3dsLoaderPers *pers, char *dest, int max ){
213         int pos = 0;
214         int ch;
215
216         for (;; )
217         {
218                 ch = pers->bufptr[ pers->cofs++ ];
219                 if ( ch == '\0' ) {
220                         break;
221                 }
222                 if ( pers->cofs >= pers->maxofs ) {
223                         dest[ pos ] = '\0';
224                         return 0;
225                 }
226                 dest[ pos++ ] = ch;
227                 if ( pos >= max ) {
228                         break;
229                 }
230         }
231         dest[ pos ] = '\0';
232         return 1;
233 }
234
235 static picoByte_t GetByte( T3dsLoaderPers *pers ){
236         picoByte_t *value;
237
238         /* sanity check */
239         if ( pers->cofs > pers->maxofs ) {
240                 return 0;
241         }
242
243         /* get and return value */
244         value = (picoByte_t *)( pers->bufptr + pers->cofs );
245         pers->cofs += 1;
246         return *value;
247 }
248
249 static int GetWord( T3dsLoaderPers *pers ){
250         unsigned short *value;
251
252         /* sanity check */
253         if ( pers->cofs > pers->maxofs ) {
254                 return 0;
255         }
256
257         /* get and return value */
258         value = (unsigned short *)( pers->bufptr + pers->cofs );
259         pers->cofs += 2;
260         return _pico_little_short( *value );
261 }
262
263 static float GetFloat( T3dsLoaderPers *pers ){
264         float *value;
265
266         /* sanity check */
267         if ( pers->cofs > pers->maxofs ) {
268                 return 0;
269         }
270
271         /* get and return value */
272         value = (float *)( pers->bufptr + pers->cofs );
273         pers->cofs += 4;
274         return _pico_little_float( *value );
275 }
276
277 static int GetMeshVertices( T3dsLoaderPers *pers ){
278         int numVerts;
279         int i;
280
281         /* get number of verts for this surface */
282         numVerts = GetWord( pers );
283
284 #ifdef DEBUG_PM_3DS
285         printf( "GetMeshVertices: numverts %d\n",numVerts );
286 #endif
287         /* read in vertices for current surface */
288         for ( i = 0; i < numVerts; i++ )
289         {
290                 picoVec3_t v;
291                 v[0] = GetFloat( pers );
292                 v[1] = GetFloat( pers );    /* ydnar: unflipped */
293                 v[2] = GetFloat( pers );    /* ydnar: unflipped and negated */
294
295                 /* add current vertex */
296                 PicoSetSurfaceXYZ( pers->surface,i,v );
297                 PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */
298
299 #ifdef DEBUG_PM_3DS_EX
300                 printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
301 #endif
302         }
303         /* success (no errors occured) */
304         return 1;
305 }
306
307 static int GetMeshFaces( T3dsLoaderPers *pers ){
308         int numFaces;
309         int i;
310
311         /* get number of faces for this surface */
312         numFaces = GetWord( pers );
313
314 #ifdef DEBUG_PM_3DS
315         printf( "GetMeshFaces: numfaces %d\n",numFaces );
316 #endif
317         /* read in vertex indices for current surface */
318         for ( i = 0; i < numFaces; i++ )
319         {
320                 /* remember, we only need 3 of 4 values read in for each */
321                 /* face. the 4th value is a vis flag for 3dsmax which is */
322                 /* being ignored by us here */
323                 T3dsIndices face;
324                 face.a       = GetWord( pers );
325                 face.c       = GetWord( pers );   /* ydnar: flipped order */
326                 face.b       = GetWord( pers );   /* ydnar: flipped order */
327                 face.visible = GetWord( pers );
328
329                 /* copy indexes */
330                 PicoSetSurfaceIndex( pers->surface, ( i * 3 + 0 ), (picoIndex_t)face.a );
331                 PicoSetSurfaceIndex( pers->surface, ( i * 3 + 1 ), (picoIndex_t)face.b );
332                 PicoSetSurfaceIndex( pers->surface, ( i * 3 + 2 ), (picoIndex_t)face.c );
333
334 #ifdef DEBUG_PM_3DS_EX
335                 printf( "Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible );
336 #endif
337         }
338         /* success (no errors occured) */
339         return 1;
340 }
341
342 static int GetMeshTexCoords( T3dsLoaderPers *pers ){
343         int numTexCoords;
344         int i;
345
346         /* get number of uv coords for this surface */
347         numTexCoords = GetWord( pers );
348
349 #ifdef DEBUG_PM_3DS
350         printf( "GetMeshTexCoords: numcoords %d\n",numTexCoords );
351 #endif
352         /* read in uv coords for current surface */
353         for ( i = 0; i < numTexCoords; i++ )
354         {
355                 picoVec2_t uv;
356                 uv[0] =  GetFloat( pers );
357                 uv[1] = -GetFloat( pers );  /* ydnar: we use origin at bottom */
358
359                 /* to make sure we don't mess up memory */
360                 if ( pers->surface == NULL ) {
361                         continue;
362                 }
363
364                 /* add current uv */
365                 PicoSetSurfaceST( pers->surface,0,i,uv );
366
367 #ifdef DEBUG_PM_3DS_EX
368                 printf( "u: %f v: %f\n",uv[0],uv[1] );
369 #endif
370         }
371         /* success (no errors occured) */
372         return 1;
373 }
374
375 static int GetMeshShader( T3dsLoaderPers *pers ){
376         char shaderName[255] = { 0 };
377         picoShader_t  *shader;
378         int numSharedVerts;
379         int setShaderName = 0;
380         int i;
381
382         /* the shader is either the color or the texture map of the */
383         /* object. it can also hold other information like the brightness, */
384         /* shine, etc. stuff we don't really care about. we just want the */
385         /* color, or the texture map file name really */
386
387         /* get in the shader name */
388         if ( !GetASCIIZ( pers,shaderName,sizeof( shaderName ) ) ) {
389                 return 0;
390         }
391
392         /* ydnar: trim to first whitespace */
393         _pico_first_token( shaderName );
394
395         /* now that we have the shader name we need to go through all of */
396         /* the shaders and check the name against each shader. when we */
397         /* find a shader in our shader list that matches this name we */
398         /* just read in, then we assign the shader's id of the object to */
399         /* that shader */
400
401         /* get shader id for shader name */
402         shader = PicoFindShader( pers->model, shaderName, 1 );
403
404         /* we've found a matching shader */
405         if ( ( shader != NULL ) && pers->surface ) {
406                 char mapName[1024 + 1];
407                 char *mapNamePtr;
408                 memset( mapName,0,sizeof( mapName ) );
409
410                 /* get ptr to shader's map name */
411                 mapNamePtr = PicoGetShaderMapName( shader );
412
413                 /* we have a valid map name ptr */
414                 if ( mapNamePtr != NULL ) {
415                         char temp[128];
416                         const char *name;
417
418                         /* copy map name to local buffer */
419                         strcpy( mapName,mapNamePtr );
420
421                         /* extract file name */
422                         name = _pico_nopath( mapName );
423                         strncpy( temp, name, sizeof( temp ) );
424
425                         /* remove file extension */
426                         /* name = _pico_setfext( name,"" ); */
427
428                         /* assign default name if no name available */
429                         if ( strlen( temp ) < 1 ) {
430                                 strcpy( temp,pers->basename );
431                         }
432
433                         /* build shader name */
434                         _pico_strlwr( temp ); /* gaynux update -sea */
435                         sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );
436
437                         /* set shader name */
438                         /* PicoSetShaderName( shader,mapName ); */      /* ydnar: this will screw up the named shader */
439
440                         /* set surface's shader index */
441                         PicoSetSurfaceShader( pers->surface, shader );
442
443                         setShaderName = 1;
444                 }
445         }
446         /* we didn't set a shader name; throw out warning */
447         if ( !setShaderName ) {
448                 _pico_printf( PICO_WARNING,"3DS mesh is missing shader name" );
449         }
450         /* we don't process the list of shared vertices here; there is a */
451         /* short int that gives the number of faces of the mesh concerned */
452         /* by this shader, then there is the list itself of these faces. */
453         /* 0000 means the first face of the (4120) face list */
454
455         /* get number of shared verts */
456         numSharedVerts = GetWord( pers );
457
458 #ifdef DEBUG_PM_3DS
459         printf( "GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts );
460 #endif
461         /* skip list of shared verts */
462         for ( i = 0; i < numSharedVerts; i++ )
463         {
464                 GetWord( pers );
465         }
466         /* success (no errors occured) */
467         return 1;
468 }
469
470 static int GetDiffuseColor( T3dsLoaderPers *pers ){
471         /* todo: support all 3ds specific color formats; */
472         /* that means: rgb,tru,trug,rgbg */
473
474         /* get rgb color (range 0..255; 3 bytes) */
475         picoColor_t color;
476
477         color[0] = GetByte( pers );
478         color[1] = GetByte( pers );
479         color[2] = GetByte( pers );
480         color[3] = 255;
481
482         /* store this as the current shader's diffuse color */
483         if ( pers->shader ) {
484                 PicoSetShaderDiffuseColor( pers->shader,color );
485         }
486 #ifdef DEBUG_PM_3DS
487         printf( "GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2] );
488 #endif
489         /* success (no errors occured) */
490         return 1;
491 }
492
493 static int DoNextEditorDataChunk( T3dsLoaderPers *pers, long endofs ){
494         T3dsChunk *chunk;
495
496 #ifdef DEBUG_PM_3DS_EX
497         printf( "DoNextEditorDataChunk: endofs %d\n",endofs );
498 #endif
499         while ( pers->cofs < endofs )
500         {
501                 long nextofs = pers->cofs;
502                 if ( ( chunk = GetChunk( pers ) ) == NULL ) {
503                         return 0;
504                 }
505                 if ( !chunk->len ) {
506                         return 0;
507                 }
508                 nextofs += chunk->len;
509
510 #ifdef DEBUG_PM_3DS_EX
511                 printf( "Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName( chunk->id ),chunk->len,pers->cofs );
512 #endif
513                 /*** meshes ***/
514                 if ( chunk->id == CHUNK_OBJECT ) {
515                         picoSurface_t *surface;
516                         char surfaceName[ 0xff ] = { 0 };
517
518                         /* read in surface name */
519                         if ( !GetASCIIZ( pers,surfaceName,sizeof( surfaceName ) ) ) {
520                                 return 0; /* this is bad */
521
522                         }
523 //PicoGetSurfaceName
524                         /* ignore NULL name surfaces */
525 //                      if( surfaceName
526
527                         /* allocate a pico surface */
528                         surface = PicoNewSurface( pers->model );
529                         if ( surface == NULL ) {
530                                 pers->surface = NULL;
531                                 return 0; /* this is bad too */
532                         }
533                         /* assign ptr to current surface */
534                         pers->surface = surface;
535
536                         /* 3ds models surfaces are all triangle meshes */
537                         PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );
538
539                         /* set surface name */
540                         PicoSetSurfaceName( pers->surface,surfaceName );
541
542                         /* continue mess with object's sub chunks */
543                         DoNextEditorDataChunk( pers,nextofs );
544                         continue;
545                 }
546                 if ( chunk->id == CHUNK_OBJECT_MESH ) {
547                         /* continue mess with mesh's sub chunks */
548                         if ( !DoNextEditorDataChunk( pers,nextofs ) ) {
549                                 return 0;
550                         }
551                         continue;
552                 }
553                 if ( chunk->id == CHUNK_OBJECT_VERTICES ) {
554                         if ( !GetMeshVertices( pers ) ) {
555                                 return 0;
556                         }
557                         continue;
558                 }
559                 if ( chunk->id == CHUNK_OBJECT_FACES ) {
560                         if ( !GetMeshFaces( pers ) ) {
561                                 return 0;
562                         }
563                         continue;
564                 }
565                 if ( chunk->id == CHUNK_OBJECT_UV ) {
566                         if ( !GetMeshTexCoords( pers ) ) {
567                                 return 0;
568                         }
569                         continue;
570                 }
571                 if ( chunk->id == CHUNK_OBJECT_MATERIAL ) {
572                         if ( !GetMeshShader( pers ) ) {
573                                 return 0;
574                         }
575                         continue;
576                 }
577                 /*** materials ***/
578                 if ( chunk->id == CHUNK_MATERIAL ) {
579                         /* new shader specific things should be */
580                         /* initialized right here */
581                         picoShader_t *shader;
582
583                         /* allocate a pico shader */
584                         shader = PicoNewShader( pers->model );  /* ydnar */
585                         if ( shader == NULL ) {
586                                 pers->shader = NULL;
587                                 return 0; /* this is bad too */
588                         }
589
590                         /* assign ptr to current shader */
591                         pers->shader = shader;
592
593                         /* continue and process the material's sub chunks */
594                         DoNextEditorDataChunk( pers,nextofs );
595                         continue;
596                 }
597                 if ( chunk->id == CHUNK_MATNAME ) {
598                         /* new material's names should be stored here. note that */
599                         /* GetMeshMaterial returns the name of the material that */
600                         /* is used by the mesh. new material names are set HERE. */
601                         /* but for now we skip the new material's name ... */
602                         if ( pers->shader ) {
603                                 char *name = (char*) ( pers->bufptr + pers->cofs );
604                                 char *cleanedName = _pico_clone_alloc( name );
605                                 _pico_first_token( cleanedName );
606                                 PicoSetShaderName( pers->shader, cleanedName );
607 #ifdef DEBUG_PM_3DS
608                                 printf( "NewShader: '%s'\n", cleanedName );
609 #endif
610                                 _pico_free( cleanedName );
611                         }
612                 }
613                 if ( chunk->id == CHUNK_MATDIFFUSE ) {
614                         /* todo: color for last inserted new material should be */
615                         /* stored somewhere by GetDiffuseColor */
616                         if ( !GetDiffuseColor( pers ) ) {
617                                 return 0;
618                         }
619
620                         /* rest of chunk is skipped here */
621                 }
622                 if ( chunk->id == CHUNK_MATMAP ) {
623                         /* continue and process the material map sub chunks */
624                         DoNextEditorDataChunk( pers,nextofs );
625                         continue;
626                 }
627                 if ( chunk->id == CHUNK_MATMAPFILE ) {
628                         /* map file name for last inserted new material should */
629                         /* be stored here. but for now we skip this too ... */
630                         if ( pers->shader ) {
631                                 char *name = (char *)( pers->bufptr + pers->cofs );
632                                 PicoSetShaderMapName( pers->shader,name );
633 #ifdef DEBUG_PM_3DS
634                                 printf( "NewShaderMapfile: '%s'\n",name );
635 #endif
636                         }
637                 }
638                 /*** keyframes ***/
639                 if ( chunk->id == CHUNK_KEYFRAME_DATA ) {
640                         /* well umm, this is a bit too much since we don't really */
641                         /* need model animation sequences right now. we skip this */
642 #ifdef DEBUG_PM_3DS
643                         printf( "KeyframeData: len %d\n",chunk->len );
644 #endif
645                 }
646                 /* skip unknown chunk */
647                 pers->cofs = nextofs;
648                 if ( pers->cofs >= pers->maxofs ) {
649                         break;
650                 }
651         }
652         return 1;
653 }
654
655 static int DoNextChunk( T3dsLoaderPers *pers, int endofs ){
656         T3dsChunk *chunk;
657
658 #ifdef DEBUG_PM_3DS
659         printf( "DoNextChunk: endofs %d\n",endofs );
660 #endif
661         while ( pers->cofs < endofs )
662         {
663                 long nextofs = pers->cofs;
664                 if ( ( chunk = GetChunk( pers ) ) == NULL ) {
665                         return 0;
666                 }
667                 if ( !chunk->len ) {
668                         return 0;
669                 }
670                 nextofs += chunk->len;
671
672 #ifdef DEBUG_PM_3DS_EX
673                 printf( "Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName( chunk->id ),chunk->len,pers->cofs );
674 #endif
675                 /*** version ***/
676                 if ( chunk->id == CHUNK_VERSION ) {
677                         /* at this point i get the 3ds file version. since there */
678                         /* might be new additions to the 3ds file format in 4.0 */
679                         /* it might be a good idea to store the version somewhere */
680                         /* for later handling or message displaying */
681
682                         /* get the version */
683                         int version;
684                         version = GetWord( pers );
685                         GetWord( pers );
686 #ifdef DEBUG_PM_3DS
687                         printf( "FileVersion: %d\n",version );
688 #endif
689
690                         /* throw out a warning for version 4 models */
691                         if ( version == 4 ) {
692                                 _pico_printf( PICO_WARNING,
693                                                           "3DS version is 4. Model might load incorrectly." );
694                         }
695                         /* store the 3ds file version in pico special field 0 */
696                         /* PicoSetSurfaceSpecial(pers->surface,0,version); */           /* ydnar: this was causing a crash accessing uninitialized surface */
697
698                         /* rest of chunk is skipped here */
699                 }
700                 /*** editor data ***/
701                 if ( chunk->id == CHUNK_EDITOR_DATA ) {
702                         if ( !DoNextEditorDataChunk( pers,nextofs ) ) {
703                                 return 0;
704                         }
705                         continue;
706                 }
707                 /* skip unknown chunk */
708                 pers->cofs = nextofs;
709                 if ( pers->cofs >= pers->maxofs ) {
710                         break;
711                 }
712         }
713         return 1;
714 }
715
716 /* _3ds_load:
717  *  loads an autodesk 3ds model file.
718  */
719 static picoModel_t *_3ds_load( PM_PARAMS_LOAD ){
720         T3dsLoaderPers pers;
721         picoModel_t    *model;
722         char basename[128];
723
724         /* create a new pico model */
725         model = PicoNewModel();
726         if ( model == NULL ) {
727                 /* user must have some serious ram problems ;) */
728                 return NULL;
729         }
730         /* get model's base name (eg. jeep from c:\models\jeep.3ds) */
731         memset( basename,0,sizeof( basename ) );
732         strncpy( basename,_pico_nopath( fileName ),sizeof( basename ) );
733         _pico_setfext( basename,"" );
734
735         /* initialize persistant vars (formerly static) */
736         pers.model    =  model;
737         pers.bufptr   = (picoByte_t *)_pico_alloc( bufSize );
738         memcpy( pers.bufptr, buffer, bufSize );
739         pers.basename = (char *)basename;
740         pers.maxofs   =  bufSize;
741         pers.cofs     =  0L;
742
743         /* do model setup */
744         PicoSetModelFrameNum( model,frameNum );
745         PicoSetModelName( model,fileName );
746         PicoSetModelFileName( model,fileName );
747
748         /* skip first chunk in file (magic) */
749         GetChunk( &pers );
750
751         /* process chunks */
752         if ( !DoNextChunk( &pers,pers.maxofs ) ) {
753                 /* well, bleh i guess */
754                 PicoFreeModel( model );
755                 return NULL;
756         }
757         /* return allocated pico model */
758         return model;
759 }
760
761 /* pico file format module definition */
762 const picoModule_t picoModule3DS =
763 {
764         "0.86-b",                   /* module version string */
765         "Autodesk 3Dstudio",        /* module display name */
766         "seaw0lf",                  /* author's name */
767         "2002 seaw0lf",             /* module copyright */
768         {
769                 "3ds",NULL,NULL,NULL    /* default extensions to use */
770         },
771         _3ds_canload,               /* validation routine */
772         _3ds_load,                  /* load routine */
773         NULL,                       /* save validation routine */
774         NULL                        /* save routine */
775 };