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