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