]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_3ds.c
my own uncrustify run
[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         const T3dsChunk *chunk;
167
168         /* sanity check */
169         if ( bufSize < (int) sizeof( T3dsChunk ) ) {
170                 return PICO_PMV_ERROR_SIZE;
171         }
172
173         /* get pointer to 3ds header chunk */
174         chunk = (const T3dsChunk *)buffer;
175
176         /* check data length */
177         if ( bufSize < (int) _pico_little_long( chunk->len ) ) {
178                 return PICO_PMV_ERROR_SIZE;
179         }
180
181         /* check 3ds magic */
182         if ( _pico_little_short( chunk->id ) != CHUNK_MAIN ) {
183                 return PICO_PMV_ERROR_IDENT;
184         }
185
186         /* file seems to be a valid 3ds */
187         return PICO_PMV_OK;
188 }
189
190 static T3dsChunk *GetChunk( T3dsLoaderPers *pers ){
191         T3dsChunk *chunk;
192
193         /* sanity check */
194         if ( pers->cofs > pers->maxofs ) {
195                 return 0;
196         }
197
198 #ifdef DEBUG_PM_3DS
199 /*      printf("GetChunk: pers->cofs %x\n",pers->cofs); */
200 #endif
201         /* fill in pointer to chunk */
202         chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];
203         if ( !chunk ) {
204                 return NULL;
205         }
206
207         chunk->id  = _pico_little_short( chunk->id );
208         chunk->len = _pico_little_long( chunk->len );
209
210         /* advance in buffer */
211         pers->cofs += sizeof( T3dsChunk );
212
213         /* this means yay */
214         return chunk;
215 }
216
217 static int GetASCIIZ( T3dsLoaderPers *pers, char *dest, int max ){
218         int pos = 0;
219         int ch;
220
221         for (;; )
222         {
223                 ch = pers->bufptr[ pers->cofs++ ];
224                 if ( ch == '\0' ) {
225                         break;
226                 }
227                 if ( pers->cofs >= pers->maxofs ) {
228                         dest[ pos ] = '\0';
229                         return 0;
230                 }
231                 dest[ pos++ ] = ch;
232                 if ( pos >= max ) {
233                         break;
234                 }
235         }
236         dest[ pos ] = '\0';
237         return 1;
238 }
239
240 static picoByte_t GetByte( T3dsLoaderPers *pers ){
241         picoByte_t *value;
242
243         /* sanity check */
244         if ( pers->cofs > pers->maxofs ) {
245                 return 0;
246         }
247
248         /* get and return value */
249         value = (picoByte_t *)( pers->bufptr + pers->cofs );
250         pers->cofs += 1;
251         return *value;
252 }
253
254 static int GetWord( T3dsLoaderPers *pers ){
255         unsigned short *value;
256
257         /* sanity check */
258         if ( pers->cofs > pers->maxofs ) {
259                 return 0;
260         }
261
262         /* get and return value */
263         value = (unsigned short *)( pers->bufptr + pers->cofs );
264         pers->cofs += 2;
265         return _pico_little_short( *value );
266 }
267
268 static float GetFloat( T3dsLoaderPers *pers ){
269         float *value;
270
271         /* sanity check */
272         if ( pers->cofs > pers->maxofs ) {
273                 return 0;
274         }
275
276         /* get and return value */
277         value = (float *)( pers->bufptr + pers->cofs );
278         pers->cofs += 4;
279         return _pico_little_float( *value );
280 }
281
282 static int GetMeshVertices( T3dsLoaderPers *pers ){
283         int numVerts;
284         int i;
285
286         /* get number of verts for this surface */
287         numVerts = GetWord( pers );
288
289 #ifdef DEBUG_PM_3DS
290         printf( "GetMeshVertices: numverts %d\n",numVerts );
291 #endif
292         /* read in vertices for current surface */
293         for ( i = 0; i < numVerts; i++ )
294         {
295                 picoVec3_t v;
296                 v[0] = GetFloat( pers );
297                 v[1] = GetFloat( pers );    /* ydnar: unflipped */
298                 v[2] = GetFloat( pers );    /* ydnar: unflipped and negated */
299
300                 /* add current vertex */
301                 PicoSetSurfaceXYZ( pers->surface,i,v );
302                 PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */
303
304 #ifdef DEBUG_PM_3DS_EX
305                 printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
306 #endif
307         }
308         /* success (no errors occured) */
309         return 1;
310 }
311
312 static int GetMeshFaces( T3dsLoaderPers *pers ){
313         int numFaces;
314         int i;
315
316         /* get number of faces for this surface */
317         numFaces = GetWord( pers );
318
319 #ifdef DEBUG_PM_3DS
320         printf( "GetMeshFaces: numfaces %d\n",numFaces );
321 #endif
322         /* read in vertex indices for current surface */
323         for ( i = 0; i < numFaces; i++ )
324         {
325                 /* remember, we only need 3 of 4 values read in for each */
326                 /* face. the 4th value is a vis flag for 3dsmax which is */
327                 /* being ignored by us here */
328                 T3dsIndices face;
329                 face.a       = GetWord( pers );
330                 face.c       = GetWord( pers );   /* ydnar: flipped order */
331                 face.b       = GetWord( pers );   /* ydnar: flipped order */
332                 face.visible = GetWord( pers );
333
334                 /* copy indexes */
335                 PicoSetSurfaceIndex( pers->surface, ( i * 3 + 0 ), (picoIndex_t)face.a );
336                 PicoSetSurfaceIndex( pers->surface, ( i * 3 + 1 ), (picoIndex_t)face.b );
337                 PicoSetSurfaceIndex( pers->surface, ( i * 3 + 2 ), (picoIndex_t)face.c );
338
339 #ifdef DEBUG_PM_3DS_EX
340                 printf( "Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible );
341 #endif
342         }
343         /* success (no errors occured) */
344         return 1;
345 }
346
347 static int GetMeshTexCoords( T3dsLoaderPers *pers ){
348         int numTexCoords;
349         int i;
350
351         /* get number of uv coords for this surface */
352         numTexCoords = GetWord( pers );
353
354 #ifdef DEBUG_PM_3DS
355         printf( "GetMeshTexCoords: numcoords %d\n",numTexCoords );
356 #endif
357         /* read in uv coords for current surface */
358         for ( i = 0; i < numTexCoords; i++ )
359         {
360                 picoVec2_t uv;
361                 uv[0] =  GetFloat( pers );
362                 uv[1] = -GetFloat( pers );  /* ydnar: we use origin at bottom */
363
364                 /* to make sure we don't mess up memory */
365                 if ( pers->surface == NULL ) {
366                         continue;
367                 }
368
369                 /* add current uv */
370                 PicoSetSurfaceST( pers->surface,0,i,uv );
371
372 #ifdef DEBUG_PM_3DS_EX
373                 printf( "u: %f v: %f\n",uv[0],uv[1] );
374 #endif
375         }
376         /* success (no errors occured) */
377         return 1;
378 }
379
380 static int GetMeshShader( T3dsLoaderPers *pers ){
381         char shaderName[255] = { 0 };
382         picoShader_t  *shader;
383         int numSharedVerts;
384         int setShaderName = 0;
385         int i;
386
387         /* the shader is either the color or the texture map of the */
388         /* object. it can also hold other information like the brightness, */
389         /* shine, etc. stuff we don't really care about. we just want the */
390         /* color, or the texture map file name really */
391
392         /* get in the shader name */
393         if ( !GetASCIIZ( pers,shaderName,sizeof( shaderName ) ) ) {
394                 return 0;
395         }
396
397         /* ydnar: trim to first whitespace */
398         _pico_first_token( shaderName );
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                         const 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                                 char *cleanedName = _pico_clone_alloc( name );
610                                 _pico_first_token( cleanedName );
611                                 PicoSetShaderName( pers->shader, cleanedName );
612 #ifdef DEBUG_PM_3DS
613                                 printf( "NewShader: '%s'\n", cleanedName );
614 #endif
615                                 _pico_free( cleanedName );
616                         }
617                 }
618                 if ( chunk->id == CHUNK_MATDIFFUSE ) {
619                         /* todo: color for last inserted new material should be */
620                         /* stored somewhere by GetDiffuseColor */
621                         if ( !GetDiffuseColor( pers ) ) {
622                                 return 0;
623                         }
624
625                         /* rest of chunk is skipped here */
626                 }
627                 if ( chunk->id == CHUNK_MATMAP ) {
628                         /* continue and process the material map sub chunks */
629                         DoNextEditorDataChunk( pers,nextofs );
630                         continue;
631                 }
632                 if ( chunk->id == CHUNK_MATMAPFILE ) {
633                         /* map file name for last inserted new material should */
634                         /* be stored here. but for now we skip this too ... */
635                         if ( pers->shader ) {
636                                 char *name = (char *)( pers->bufptr + pers->cofs );
637                                 PicoSetShaderMapName( pers->shader,name );
638 #ifdef DEBUG_PM_3DS
639                                 printf( "NewShaderMapfile: '%s'\n",name );
640 #endif
641                         }
642                 }
643                 /*** keyframes ***/
644                 if ( chunk->id == CHUNK_KEYFRAME_DATA ) {
645                         /* well umm, this is a bit too much since we don't really */
646                         /* need model animation sequences right now. we skip this */
647 #ifdef DEBUG_PM_3DS
648                         printf( "KeyframeData: len %d\n",chunk->len );
649 #endif
650                 }
651                 /* skip unknown chunk */
652                 pers->cofs = nextofs;
653                 if ( pers->cofs >= pers->maxofs ) {
654                         break;
655                 }
656         }
657         return 1;
658 }
659
660 static int DoNextChunk( T3dsLoaderPers *pers, int endofs ){
661         T3dsChunk *chunk;
662
663 #ifdef DEBUG_PM_3DS
664         printf( "DoNextChunk: endofs %d\n",endofs );
665 #endif
666         while ( pers->cofs < endofs )
667         {
668                 long nextofs = pers->cofs;
669                 if ( ( chunk = GetChunk( pers ) ) == NULL ) {
670                         return 0;
671                 }
672                 if ( !chunk->len ) {
673                         return 0;
674                 }
675                 nextofs += chunk->len;
676
677 #ifdef DEBUG_PM_3DS_EX
678                 printf( "Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName( chunk->id ),chunk->len,pers->cofs );
679 #endif
680                 /*** version ***/
681                 if ( chunk->id == CHUNK_VERSION ) {
682                         /* at this point i get the 3ds file version. since there */
683                         /* might be new additions to the 3ds file format in 4.0 */
684                         /* it might be a good idea to store the version somewhere */
685                         /* for later handling or message displaying */
686
687                         /* get the version */
688                         int version;
689                         version = GetWord( pers );
690                         GetWord( pers );
691 #ifdef DEBUG_PM_3DS
692                         printf( "FileVersion: %d\n",version );
693 #endif
694
695                         /* throw out a warning for version 4 models */
696                         if ( version == 4 ) {
697                                 _pico_printf( PICO_WARNING,
698                                                           "3DS version is 4. Model might load incorrectly." );
699                         }
700                         /* store the 3ds file version in pico special field 0 */
701                         /* PicoSetSurfaceSpecial(pers->surface,0,version); */           /* ydnar: this was causing a crash accessing uninitialized surface */
702
703                         /* rest of chunk is skipped here */
704                 }
705                 /*** editor data ***/
706                 if ( chunk->id == CHUNK_EDITOR_DATA ) {
707                         if ( !DoNextEditorDataChunk( pers,nextofs ) ) {
708                                 return 0;
709                         }
710                         continue;
711                 }
712                 /* skip unknown chunk */
713                 pers->cofs = nextofs;
714                 if ( pers->cofs >= pers->maxofs ) {
715                         break;
716                 }
717         }
718         return 1;
719 }
720
721 /* _3ds_load:
722  *  loads an autodesk 3ds model file.
723  */
724 static picoModel_t *_3ds_load( PM_PARAMS_LOAD ){
725         T3dsLoaderPers pers;
726         picoModel_t    *model;
727         char basename[128];
728
729         /* create a new pico model */
730         model = PicoNewModel();
731         if ( model == NULL ) {
732                 /* user must have some serious ram problems ;) */
733                 return NULL;
734         }
735         /* get model's base name (eg. jeep from c:\models\jeep.3ds) */
736         memset( basename,0,sizeof( basename ) );
737         strncpy( basename,_pico_nopath( fileName ),sizeof( basename ) );
738         _pico_setfext( basename,"" );
739
740         /* initialize persistant vars (formerly static) */
741         pers.model    =  model;
742         pers.bufptr   = (picoByte_t *)_pico_alloc( bufSize );
743         memcpy( pers.bufptr, buffer, bufSize );
744         pers.basename = (char *)basename;
745         pers.maxofs   =  bufSize;
746         pers.cofs     =  0L;
747
748         /* do model setup */
749         PicoSetModelFrameNum( model,frameNum );
750         PicoSetModelName( model,fileName );
751         PicoSetModelFileName( model,fileName );
752
753         /* skip first chunk in file (magic) */
754         GetChunk( &pers );
755
756         /* process chunks */
757         if ( !DoNextChunk( &pers,pers.maxofs ) ) {
758                 /* well, bleh i guess */
759                 PicoFreeModel( model );
760                 return NULL;
761         }
762         /* return allocated pico model */
763         return model;
764 }
765
766 /* pico file format module definition */
767 const picoModule_t picoModule3DS =
768 {
769         "0.86-b",                   /* module version string */
770         "Autodesk 3Dstudio",        /* module display name */
771         "seaw0lf",                  /* author's name */
772         "2002 seaw0lf",             /* module copyright */
773         {
774                 "3ds",NULL,NULL,NULL    /* default extensions to use */
775         },
776         _3ds_canload,               /* validation routine */
777         _3ds_load,                  /* load routine */
778         NULL,                       /* save validation routine */
779         NULL                        /* save routine */
780 };