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