]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/pm_3ds.c
set eol-style
[xonotic/netradiant.git] / libs / picomodel / pm_3ds.c
index 1ad95b1c26f89bdfbf675260d2e92d6d7185902b..0f2b41f7148453649943985b03e73eeb0693896f 100644 (file)
-/* -----------------------------------------------------------------------------\r
-\r
-PicoModel Library\r
-\r
-Copyright (c) 2002, Randy Reddig & seaw0lf\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification,\r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list\r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the names of the copyright holders nor the names of its contributors may\r
-be used to endorse or promote products derived from this software without\r
-specific prior written permission.\r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
------------------------------------------------------------------------------ */\r
-\r
-\r
-\r
-/* marker */\r
-#define PM_3DS_C\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-\r
-/* ydnar */\r
-static picoColor_t white = { 255,255,255,255 };\r
-\r
-/* remarks:\r
- * - 3ds file version is stored in pico special field 0 on load (ydnar: removed)\r
- * todo:\r
- * - sometimes there is one unnamed surface 0 having 0 verts as\r
- *   well as 0 faces. this error occurs since pm 0.6 (ydnar?)\r
- */\r
-/* uncomment when debugging this module */\r
-/* #define DEBUG_PM_3DS\r
-#define DEBUG_PM_3DS_EX */\r
-\r
-/* structure holding persistent 3ds loader specific data used */\r
-/* to store formerly static vars to keep the module reentrant */\r
-/* safe. put everything that needs to be static in here. */\r
-typedef struct S3dsLoaderPers\r
-{\r
-       picoModel_t        *model;                      /* ptr to output model */\r
-       picoSurface_t  *surface;                /* ptr to current surface */\r
-       picoShader_t   *shader;                 /* ptr to current shader */\r
-       picoByte_t         *bufptr;                     /* ptr to raw data */\r
-       char               *basename;           /* ptr to model base name (eg. jeep) */\r
-       int                             cofs;\r
-       int                             maxofs;\r
-}\r
-T3dsLoaderPers;\r
-\r
-/* 3ds chunk types that we use */\r
-enum {\r
-    /* primary chunk */\r
-    CHUNK_MAIN                         = 0x4D4D,\r
-\r
-       /* main chunks */\r
-       CHUNK_VERSION           = 0x0002,\r
-       CHUNK_EDITOR_CONFIG             = 0x3D3E,\r
-       CHUNK_EDITOR_DATA       = 0x3D3D,\r
-       CHUNK_KEYFRAME_DATA             = 0xB000,\r
-\r
-       /* editor data sub chunks */\r
-       CHUNK_MATERIAL                  = 0xAFFF,\r
-       CHUNK_OBJECT            = 0x4000,\r
-\r
-       /* material sub chunks */\r
-       CHUNK_MATNAME                   = 0xA000,\r
-       CHUNK_MATDIFFUSE                = 0xA020,\r
-       CHUNK_MATMAP                    = 0xA200,\r
-       CHUNK_MATMAPFILE                = 0xA300,\r
-\r
-       /* lets us know we're reading a new object */\r
-       CHUNK_OBJECT_MESH               = 0x4100,\r
-\r
-       /* object mesh sub chunks */\r
-       CHUNK_OBJECT_VERTICES   = 0x4110,\r
-       CHUNK_OBJECT_FACES              = 0x4120,\r
-       CHUNK_OBJECT_MATERIAL   = 0x4130,\r
-       CHUNK_OBJECT_UV                 = 0x4140,\r
-};\r
-#ifdef DEBUG_PM_3DS\r
-static struct\r
-{\r
-       int     id;\r
-       char   *name;\r
-}\r
-debugChunkNames[] =\r
-{\r
-       { CHUNK_MAIN                    , "CHUNK_MAIN"                          },\r
-       { CHUNK_VERSION                 , "CHUNK_VERSION"                       },\r
-       { CHUNK_EDITOR_CONFIG   , "CHUNK_EDITOR_CONFIG"         },\r
-       { CHUNK_EDITOR_DATA             , "CHUNK_EDITOR_DATA"           },\r
-       { CHUNK_KEYFRAME_DATA   , "CHUNK_KEYFRAME_DATA"         },\r
-       { CHUNK_MATERIAL                , "CHUNK_MATERIAL"                      },\r
-       { CHUNK_OBJECT                  , "CHUNK_OBJECT"                        },\r
-       { CHUNK_MATNAME                 , "CHUNK_MATNAME"                       },\r
-       { CHUNK_MATDIFFUSE              , "CHUNK_MATDIFFUSE"            },\r
-       { CHUNK_MATMAP                  , "CHUNK_MATMAP"                        },\r
-       { CHUNK_MATMAPFILE              , "CHUNK_MATMAPFILE"            },\r
-       { CHUNK_OBJECT_MESH             , "CHUNK_OBJECT_MESH"           },\r
-       { CHUNK_OBJECT_VERTICES , "CHUNK_OBJECT_VERTICES"       },\r
-       { CHUNK_OBJECT_FACES    , "CHUNK_OBJECT_FACES"          },\r
-       { CHUNK_OBJECT_MATERIAL , "CHUNK_OBJECT_MATERIAL"       },\r
-       { CHUNK_OBJECT_UV               , "CHUNK_OBJECT_UV"                     },\r
-       { 0                                             ,  NULL                                         }\r
-};\r
-static char *DebugGetChunkName (int id)\r
-{\r
-       int i,max;      /* imax? ;) */\r
-       max = sizeof(debugChunkNames) / sizeof(debugChunkNames[0]);\r
-\r
-       for (i=0; i<max; i++)\r
-       {\r
-               if (debugChunkNames[i].id == id)\r
-               {\r
-                       /* gaynux update -sea */\r
-                       return _pico_strlwr( debugChunkNames[i].name );\r
-               }\r
-       }\r
-       return "chunk_unknown";\r
-}\r
-#endif /*DEBUG_PM_3DS*/\r
-\r
-/* this funky loader needs byte alignment */\r
-#pragma pack(push, 1)\r
-\r
-typedef struct S3dsIndices\r
-{\r
-       unsigned short  a,b,c;\r
-       unsigned short  visible;\r
-}\r
-T3dsIndices;\r
-\r
-typedef struct S3dsChunk\r
-{\r
-       unsigned short  id;\r
-       unsigned int    len;\r
-}\r
-T3dsChunk;\r
-\r
-/* restore previous data alignment */\r
-#pragma pack(pop)\r
-\r
-/* _3ds_canload:\r
- *  validates an autodesk 3ds model file.\r
- */\r
-static int _3ds_canload( PM_PARAMS_CANLOAD )\r
-{\r
-       T3dsChunk *chunk;\r
-\r
-       /* to keep the compiler happy */\r
-       *fileName = *fileName;\r
-\r
-       /* sanity check */\r
-       if (bufSize < sizeof(T3dsChunk))\r
-               return PICO_PMV_ERROR_SIZE;\r
-\r
-       /* get pointer to 3ds header chunk */\r
-       chunk = (T3dsChunk *)buffer;\r
-\r
-       /* check data length */\r
-       if (bufSize < _pico_little_long(chunk->len))\r
-               return PICO_PMV_ERROR_SIZE;\r
-\r
-       /* check 3ds magic */\r
-       if (_pico_little_short(chunk->id) != CHUNK_MAIN)\r
-               return PICO_PMV_ERROR_IDENT;\r
-\r
-       /* file seems to be a valid 3ds */\r
-       return PICO_PMV_OK;\r
-}\r
-\r
-static T3dsChunk *GetChunk (T3dsLoaderPers *pers)\r
-{\r
-       T3dsChunk *chunk;\r
-\r
-       /* sanity check */\r
-       if (pers->cofs > pers->maxofs) return 0;\r
-\r
-#ifdef DEBUG_PM_3DS\r
-/*     printf("GetChunk: pers->cofs %x\n",pers->cofs); */\r
-#endif\r
-       /* fill in pointer to chunk */\r
-       chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];\r
-       if (!chunk) return NULL;\r
-\r
-       chunk->id  = _pico_little_short(chunk->id );\r
-       chunk->len = _pico_little_long (chunk->len);\r
-\r
-       /* advance in buffer */\r
-       pers->cofs += sizeof(T3dsChunk);\r
-\r
-       /* this means yay */\r
-       return chunk;\r
-}\r
-\r
-static int GetASCIIZ (T3dsLoaderPers *pers, char *dest, int max)\r
-{\r
-       int pos = 0;\r
-       int ch;\r
-\r
-       for (;;)\r
-       {\r
-               ch = pers->bufptr[ pers->cofs++ ];\r
-               if (ch == '\0') break;\r
-               if (pers->cofs >= pers->maxofs)\r
-               {\r
-                       dest[ pos ] = '\0';\r
-                       return 0;\r
-               }\r
-               dest[ pos++ ] = ch;\r
-               if (pos >= max) break;\r
-       }\r
-       dest[ pos ] = '\0';\r
-       return 1;\r
-}\r
-\r
-static picoByte_t GetByte (T3dsLoaderPers *pers)\r
-{\r
-       picoByte_t *value;\r
-\r
-       /* sanity check */\r
-       if (pers->cofs > pers->maxofs) return 0;\r
-\r
-       /* get and return value */\r
-       value = (picoByte_t *)(pers->bufptr + pers->cofs);\r
-       pers->cofs += 1;\r
-       return *value;\r
-}\r
-\r
-static int GetWord (T3dsLoaderPers *pers)\r
-{\r
-       unsigned short *value;\r
-\r
-       /* sanity check */\r
-       if (pers->cofs > pers->maxofs) return 0;\r
-\r
-       /* get and return value */\r
-       value = (unsigned short *)(pers->bufptr + pers->cofs);\r
-       pers->cofs += 2;\r
-       return _pico_little_short(*value);\r
-}\r
-\r
-static float GetFloat (T3dsLoaderPers *pers)\r
-{\r
-       float *value;\r
-\r
-       /* sanity check */\r
-       if (pers->cofs > pers->maxofs) return 0;\r
-\r
-       /* get and return value */\r
-       value = (float *)(pers->bufptr + pers->cofs);\r
-       pers->cofs += 4;\r
-       return _pico_little_float(*value);\r
-}\r
-\r
-static int GetMeshVertices (T3dsLoaderPers *pers)\r
-{\r
-       int numVerts;\r
-       int i;\r
-\r
-       /* get number of verts for this surface */\r
-       numVerts = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
-       printf("GetMeshVertices: numverts %d\n",numVerts);\r
-#endif\r
-       /* read in vertices for current surface */\r
-       for (i=0; i<numVerts; i++)\r
-       {\r
-               picoVec3_t v;\r
-               v[0] = GetFloat( pers );\r
-               v[1] = GetFloat( pers );        /* ydnar: unflipped */\r
-               v[2] = GetFloat( pers );        /* ydnar: unflipped and negated */\r
-               \r
-               /* add current vertex */\r
-               PicoSetSurfaceXYZ( pers->surface,i,v );\r
-               PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
-               printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);\r
-#endif\r
-       }\r
-       /* success (no errors occured) */\r
-       return 1;\r
-}\r
-\r
-static int GetMeshFaces (T3dsLoaderPers *pers)\r
-{\r
-       int numFaces;\r
-       int i;\r
-\r
-       /* get number of faces for this surface */\r
-       numFaces = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
-       printf("GetMeshFaces: numfaces %d\n",numFaces);\r
-#endif\r
-       /* read in vertex indices for current surface */\r
-       for (i=0; i<numFaces; i++)\r
-       {\r
-               /* remember, we only need 3 of 4 values read in for each */\r
-               /* face. the 4th value is a vis flag for 3dsmax which is */\r
-               /* being ignored by us here */\r
-               T3dsIndices face;\r
-               face.a           = GetWord(pers);\r
-               face.c           = GetWord(pers);       /* ydnar: flipped order */\r
-               face.b           = GetWord(pers);       /* ydnar: flipped order */\r
-               face.visible = GetWord(pers);\r
-\r
-               /* copy indexes */\r
-               PicoSetSurfaceIndex( pers->surface, (i * 3 + 0), (picoIndex_t)face.a );\r
-               PicoSetSurfaceIndex( pers->surface, (i * 3 + 1), (picoIndex_t)face.b );\r
-               PicoSetSurfaceIndex( pers->surface, (i * 3 + 2), (picoIndex_t)face.c );\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
-               printf("Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible);\r
-#endif\r
-       }\r
-       /* success (no errors occured) */\r
-       return 1;\r
-}\r
-\r
-static int GetMeshTexCoords (T3dsLoaderPers *pers)\r
-{\r
-       int numTexCoords;\r
-       int i;\r
-\r
-       /* get number of uv coords for this surface */\r
-       numTexCoords = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
-       printf("GetMeshTexCoords: numcoords %d\n",numTexCoords);\r
-#endif\r
-       /* read in uv coords for current surface */\r
-       for (i=0; i<numTexCoords; i++)\r
-       {\r
-               picoVec2_t uv;\r
-               uv[0] =  GetFloat( pers );\r
-               uv[1] = -GetFloat( pers );      /* ydnar: we use origin at bottom */\r
-\r
-               /* to make sure we don't mess up memory */\r
-               if (pers->surface == NULL)\r
-                       continue;\r
-               \r
-               /* add current uv */\r
-               PicoSetSurfaceST( pers->surface,0,i,uv );\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
-               printf("u: %f v: %f\n",uv[0],uv[1]);\r
-#endif\r
-       }\r
-       /* success (no errors occured) */\r
-       return 1;\r
-}\r
-\r
-static int GetMeshShader (T3dsLoaderPers *pers)\r
-{\r
-       char shaderName[255] = { 0 };\r
-       picoShader_t  *shader;\r
-       int  numSharedVerts;\r
-       int  setShaderName = 0;\r
-       int  i;\r
-       \r
-       /* the shader is either the color or the texture map of the */\r
-       /* object. it can also hold other information like the brightness, */\r
-       /* shine, etc. stuff we don't really care about. we just want the */\r
-       /* color, or the texture map file name really */\r
-\r
-       /* get in the shader name */\r
-       if (!GetASCIIZ(pers,shaderName,sizeof(shaderName)))\r
-               return 0;\r
-\r
-       /* now that we have the shader name we need to go through all of */\r
-       /* the shaders and check the name against each shader. when we */\r
-       /* find a shader in our shader list that matches this name we */\r
-       /* just read in, then we assign the shader's id of the object to */\r
-       /* that shader */\r
-\r
-       /* get shader id for shader name */\r
-       shader = PicoFindShader( pers->model, shaderName, 1 );\r
-\r
-       /* we've found a matching shader */\r
-       if ((shader != NULL) && pers->surface)\r
-       {\r
-               char  mapName[1024+1];\r
-               char *mapNamePtr;\r
-               memset( mapName,0,sizeof(mapName) );\r
-\r
-               /* get ptr to shader's map name */\r
-               mapNamePtr = PicoGetShaderMapName( shader );\r
-\r
-               /* we have a valid map name ptr */\r
-               if (mapNamePtr != NULL)\r
-               {\r
-                       char  temp[128];\r
-                       char *name;\r
-\r
-                       /* copy map name to local buffer */\r
-                       strcpy( mapName,mapNamePtr );\r
-\r
-                       /* extract file name */\r
-                       name = _pico_nopath( mapName );\r
-                       strncpy( temp, name, sizeof(temp) );\r
-\r
-                       /* remove file extension */\r
-                       /* name = _pico_setfext( name,"" ); */\r
-\r
-                       /* assign default name if no name available */\r
-                       if (strlen(temp) < 1)\r
-                               strcpy(temp,pers->basename);\r
-\r
-                       /* build shader name */\r
-                       _pico_strlwr( temp ); /* gaynux update -sea */\r
-                       sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );\r
-\r
-                       /* set shader name */\r
-                       /* PicoSetShaderName( shader,mapName ); */      /* ydnar: this will screw up the named shader */\r
-\r
-                       /* set surface's shader index */\r
-                       PicoSetSurfaceShader( pers->surface, shader );\r
-\r
-                       setShaderName = 1;\r
-               }\r
-       }\r
-       /* we didn't set a shader name; throw out warning */\r
-       if (!setShaderName)\r
-       {\r
-               _pico_printf( PICO_WARNING,"3DS mesh is missing shader name");\r
-       }\r
-       /* we don't process the list of shared vertices here; there is a */\r
-       /* short int that gives the number of faces of the mesh concerned */\r
-       /* by this shader, then there is the list itself of these faces. */\r
-       /* 0000 means the first face of the (4120) face list */\r
-\r
-       /* get number of shared verts */\r
-       numSharedVerts = GetWord(pers);\r
-\r
-#ifdef DEBUG_PM_3DS\r
-       printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts);\r
-#endif\r
-       /* skip list of shared verts */\r
-       for (i=0; i<numSharedVerts; i++)\r
-       {\r
-               GetWord(pers);\r
-       }\r
-       /* success (no errors occured) */\r
-       return 1;\r
-}\r
-\r
-static int GetDiffuseColor (T3dsLoaderPers *pers)\r
-{\r
-       /* todo: support all 3ds specific color formats; */\r
-       /* that means: rgb,tru,trug,rgbg */\r
-\r
-       /* get rgb color (range 0..255; 3 bytes) */\r
-       picoColor_t color;\r
-\r
-       color[0] = GetByte(pers);\r
-       color[1] = GetByte(pers);\r
-       color[2] = GetByte(pers);\r
-       color[3] = 255;\r
-\r
-       /* store this as the current shader's diffuse color */\r
-       if( pers->shader )\r
-       {\r
-               PicoSetShaderDiffuseColor( pers->shader,color );\r
-       }\r
-#ifdef DEBUG_PM_3DS\r
-       printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]);\r
-#endif\r
-       /* success (no errors occured) */\r
-       return 1;\r
-}\r
-\r
-static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs)\r
-{\r
-       T3dsChunk *chunk;\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
-       printf("DoNextEditorDataChunk: endofs %d\n",endofs);\r
-#endif\r
-       while (pers->cofs < endofs)\r
-       {\r
-               long nextofs = pers->cofs;\r
-               if ((chunk = GetChunk(pers)) == NULL) return 0;\r
-               if (!chunk->len) return 0;\r
-               nextofs += chunk->len;\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
-               printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);\r
-#endif\r
-               /*** meshes ***/\r
-               if (chunk->id == CHUNK_OBJECT)\r
-               {\r
-                       picoSurface_t *surface;\r
-                       char surfaceName[ 0xff ] = { 0 };\r
-\r
-                       /* read in surface name */\r
-                       if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) )\r
-                               return 0; /* this is bad */\r
-\r
-//PicoGetSurfaceName\r
-                       /* ignore NULL name surfaces */\r
-//                     if( surfaceName\r
-\r
-                       /* allocate a pico surface */\r
-                       surface = PicoNewSurface( pers->model );\r
-                       if( surface == NULL )\r
-                       {\r
-                               pers->surface = NULL;\r
-                               return 0; /* this is bad too */\r
-                       }\r
-                       /* assign ptr to current surface */\r
-                       pers->surface = surface;\r
-\r
-                       /* 3ds models surfaces are all triangle meshes */\r
-                       PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );\r
-\r
-                       /* set surface name */\r
-                       PicoSetSurfaceName( pers->surface,surfaceName );\r
-\r
-                       /* continue mess with object's sub chunks */\r
-                       DoNextEditorDataChunk(pers,nextofs);\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_OBJECT_MESH)\r
-               {\r
-                       /* continue mess with mesh's sub chunks */\r
-                       if (!DoNextEditorDataChunk(pers,nextofs)) return 0;\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_OBJECT_VERTICES)\r
-               {\r
-                       if (!GetMeshVertices(pers)) return 0;\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_OBJECT_FACES)\r
-               {\r
-                       if (!GetMeshFaces(pers)) return 0;\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_OBJECT_UV)\r
-               {\r
-                       if (!GetMeshTexCoords(pers)) return 0;\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_OBJECT_MATERIAL)\r
-               {\r
-                       if (!GetMeshShader(pers)) return 0;\r
-                       continue;\r
-               }\r
-               /*** materials ***/\r
-               if (chunk->id == CHUNK_MATERIAL)\r
-               {\r
-                       /* new shader specific things should be */\r
-                       /* initialized right here */\r
-                       picoShader_t *shader;\r
-\r
-                       /* allocate a pico shader */\r
-                       shader = PicoNewShader( pers->model );  /* ydnar */\r
-                       if( shader == NULL )\r
-                       {\r
-                               pers->shader = NULL;\r
-                               return 0; /* this is bad too */\r
-                       }\r
-                       \r
-                       /* assign ptr to current shader */\r
-                       pers->shader = shader;\r
-\r
-                       /* continue and process the material's sub chunks */\r
-                       DoNextEditorDataChunk(pers,nextofs);\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_MATNAME)\r
-               {\r
-                       /* new material's names should be stored here. note that */\r
-                       /* GetMeshMaterial returns the name of the material that */\r
-                       /* is used by the mesh. new material names are set HERE. */\r
-                       /* but for now we skip the new material's name ... */\r
-                       if (pers->shader)\r
-                       {\r
-                               char *name = (char *)(pers->bufptr + pers->cofs);\r
-                               PicoSetShaderName( pers->shader,name );\r
-#ifdef DEBUG_PM_3DS\r
-                               printf("NewShader: '%s'\n",name);\r
-#endif\r
-                       }\r
-               }\r
-               if (chunk->id == CHUNK_MATDIFFUSE)\r
-               {\r
-                       /* todo: color for last inserted new material should be */\r
-                       /* stored somewhere by GetDiffuseColor */\r
-                       if (!GetDiffuseColor(pers)) return 0;\r
-\r
-                       /* rest of chunk is skipped here */\r
-               }\r
-               if (chunk->id == CHUNK_MATMAP)\r
-               {\r
-                       /* continue and process the material map sub chunks */\r
-                       DoNextEditorDataChunk(pers,nextofs);\r
-                       continue;\r
-               }\r
-               if (chunk->id == CHUNK_MATMAPFILE)\r
-               {\r
-                       /* map file name for last inserted new material should */\r
-                       /* be stored here. but for now we skip this too ... */\r
-                       if( pers->shader )\r
-                       {\r
-                               char *name = (char *)(pers->bufptr + pers->cofs);\r
-                               PicoSetShaderMapName( pers->shader,name );\r
-#ifdef DEBUG_PM_3DS\r
-                               printf("NewShaderMapfile: '%s'\n",name);\r
-#endif\r
-                       }\r
-               }\r
-               /*** keyframes ***/\r
-               if (chunk->id == CHUNK_KEYFRAME_DATA)\r
-               {\r
-                       /* well umm, this is a bit too much since we don't really */\r
-                       /* need model animation sequences right now. we skip this */\r
-#ifdef DEBUG_PM_3DS\r
-                       printf("KeyframeData: len %d\n",chunk->len);\r
-#endif\r
-               }\r
-               /* skip unknown chunk */\r
-               pers->cofs = nextofs;\r
-               if (pers->cofs >= pers->maxofs) break;\r
-       }\r
-       return 1;\r
-}\r
-\r
-static int DoNextChunk (T3dsLoaderPers *pers, int endofs)\r
-{\r
-       T3dsChunk *chunk;\r
-\r
-#ifdef DEBUG_PM_3DS\r
-       printf("DoNextChunk: endofs %d\n",endofs);\r
-#endif\r
-       while (pers->cofs < endofs)\r
-       {\r
-               long nextofs = pers->cofs;\r
-               if ((chunk = GetChunk(pers)) == NULL) return 0;\r
-               if (!chunk->len) return 0;\r
-               nextofs += chunk->len;\r
-\r
-#ifdef DEBUG_PM_3DS_EX\r
-               printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);\r
-#endif\r
-               /*** version ***/\r
-               if (chunk->id == CHUNK_VERSION)\r
-               {\r
-                       /* at this point i get the 3ds file version. since there */\r
-                       /* might be new additions to the 3ds file format in 4.0 */\r
-                       /* it might be a good idea to store the version somewhere */\r
-                       /* for later handling or message displaying */\r
-\r
-                       /* get the version */\r
-                       int version;\r
-                       version = GetWord(pers);\r
-                       GetWord(pers);\r
-#ifdef DEBUG_PM_3DS\r
-                       printf("FileVersion: %d\n",version);\r
-#endif\r
-\r
-                       /* throw out a warning for version 4 models */\r
-                       if (version == 4)\r
-                       {\r
-                               _pico_printf( PICO_WARNING,\r
-                                       "3DS version is 4. Model might load incorrectly.");\r
-                       }\r
-                       /* store the 3ds file version in pico special field 0 */\r
-                       /* PicoSetSurfaceSpecial(pers->surface,0,version); */           /* ydnar: this was causing a crash accessing uninitialized surface */\r
-                       \r
-                       /* rest of chunk is skipped here */\r
-               }\r
-               /*** editor data ***/\r
-               if (chunk->id == CHUNK_EDITOR_DATA)\r
-               {\r
-                       if (!DoNextEditorDataChunk(pers,nextofs)) return 0;\r
-                       continue;\r
-               }\r
-               /* skip unknown chunk */\r
-               pers->cofs = nextofs;\r
-               if (pers->cofs >= pers->maxofs) break;\r
-       }\r
-       return 1;\r
-}\r
-\r
-/* _3ds_load:\r
- *  loads an autodesk 3ds model file.\r
-*/\r
-static picoModel_t *_3ds_load( PM_PARAMS_LOAD )\r
-{\r
-       T3dsLoaderPers  pers;\r
-       picoModel_t        *model;\r
-       char                    basename[128];\r
-\r
-       /* create a new pico model */\r
-       model = PicoNewModel();\r
-       if (model == NULL)\r
-       {\r
-               /* user must have some serious ram problems ;) */\r
-               return NULL;\r
-       }\r
-       /* get model's base name (eg. jeep from c:\models\jeep.3ds) */\r
-       memset( basename,0,sizeof(basename) );\r
-       strncpy( basename,_pico_nopath(fileName),sizeof(basename) );\r
-       _pico_setfext( basename,"" );\r
-\r
-       /* initialize persistant vars (formerly static) */\r
-       pers.model    =  model;\r
-       pers.bufptr   = (picoByte_t *)buffer;\r
-       pers.basename = (char *)basename;\r
-       pers.maxofs   =  bufSize;\r
-       pers.cofs     =  0L;\r
-\r
-       /* do model setup */\r
-       PicoSetModelFrameNum( model,frameNum );\r
-       PicoSetModelName( model,fileName );\r
-       PicoSetModelFileName( model,fileName );\r
-\r
-       /* skip first chunk in file (magic) */\r
-       GetChunk(&pers);\r
-\r
-       /* process chunks */\r
-       if (!DoNextChunk(&pers,pers.maxofs))\r
-       {\r
-               /* well, bleh i guess */\r
-               PicoFreeModel(model);\r
-               return NULL;\r
-       }\r
-       /* return allocated pico model */\r
-       return model;\r
-}\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModule3DS =\r
-{\r
-       "0.86-b",                                       /* module version string */\r
-       "Autodesk 3Dstudio",            /* module display name */\r
-       "seaw0lf",                                      /* author's name */\r
-       "2002 seaw0lf",                         /* module copyright */\r
-       {\r
-               "3ds",NULL,NULL,NULL    /* default extensions to use */\r
-       },\r
-       _3ds_canload,                           /* validation routine */\r
-       _3ds_load,                                      /* load routine */\r
-        NULL,                                          /* save validation routine */\r
-        NULL                                           /* save routine */\r
-};\r
+/* -----------------------------------------------------------------------------
+
+PicoModel Library
+
+Copyright (c) 2002, Randy Reddig & seaw0lf
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list
+of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+Neither the names of the copyright holders nor the names of its contributors may
+be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------------- */
+
+
+
+/* marker */
+#define PM_3DS_C
+
+/* dependencies */
+#include "picointernal.h"
+
+/* ydnar */
+static picoColor_t white = { 255,255,255,255 };
+
+/* remarks:
+ * - 3ds file version is stored in pico special field 0 on load (ydnar: removed)
+ * todo:
+ * - sometimes there is one unnamed surface 0 having 0 verts as
+ *   well as 0 faces. this error occurs since pm 0.6 (ydnar?)
+ */
+/* uncomment when debugging this module */
+/* #define DEBUG_PM_3DS
+#define DEBUG_PM_3DS_EX */
+
+/* structure holding persistent 3ds loader specific data used */
+/* to store formerly static vars to keep the module reentrant */
+/* safe. put everything that needs to be static in here. */
+typedef struct S3dsLoaderPers
+{
+       picoModel_t        *model;                      /* ptr to output model */
+       picoSurface_t  *surface;                /* ptr to current surface */
+       picoShader_t   *shader;                 /* ptr to current shader */
+       picoByte_t         *bufptr;                     /* ptr to raw data */
+       char               *basename;           /* ptr to model base name (eg. jeep) */
+       int                             cofs;
+       int                             maxofs;
+}
+T3dsLoaderPers;
+
+/* 3ds chunk types that we use */
+enum {
+    /* primary chunk */
+    CHUNK_MAIN                         = 0x4D4D,
+
+       /* main chunks */
+       CHUNK_VERSION           = 0x0002,
+       CHUNK_EDITOR_CONFIG             = 0x3D3E,
+       CHUNK_EDITOR_DATA       = 0x3D3D,
+       CHUNK_KEYFRAME_DATA             = 0xB000,
+
+       /* editor data sub chunks */
+       CHUNK_MATERIAL                  = 0xAFFF,
+       CHUNK_OBJECT            = 0x4000,
+
+       /* material sub chunks */
+       CHUNK_MATNAME                   = 0xA000,
+       CHUNK_MATDIFFUSE                = 0xA020,
+       CHUNK_MATMAP                    = 0xA200,
+       CHUNK_MATMAPFILE                = 0xA300,
+
+       /* lets us know we're reading a new object */
+       CHUNK_OBJECT_MESH               = 0x4100,
+
+       /* object mesh sub chunks */
+       CHUNK_OBJECT_VERTICES   = 0x4110,
+       CHUNK_OBJECT_FACES              = 0x4120,
+       CHUNK_OBJECT_MATERIAL   = 0x4130,
+       CHUNK_OBJECT_UV                 = 0x4140,
+};
+#ifdef DEBUG_PM_3DS
+static struct
+{
+       int     id;
+       char   *name;
+}
+debugChunkNames[] =
+{
+       { CHUNK_MAIN                    , "CHUNK_MAIN"                          },
+       { CHUNK_VERSION                 , "CHUNK_VERSION"                       },
+       { CHUNK_EDITOR_CONFIG   , "CHUNK_EDITOR_CONFIG"         },
+       { CHUNK_EDITOR_DATA             , "CHUNK_EDITOR_DATA"           },
+       { CHUNK_KEYFRAME_DATA   , "CHUNK_KEYFRAME_DATA"         },
+       { CHUNK_MATERIAL                , "CHUNK_MATERIAL"                      },
+       { CHUNK_OBJECT                  , "CHUNK_OBJECT"                        },
+       { CHUNK_MATNAME                 , "CHUNK_MATNAME"                       },
+       { CHUNK_MATDIFFUSE              , "CHUNK_MATDIFFUSE"            },
+       { CHUNK_MATMAP                  , "CHUNK_MATMAP"                        },
+       { CHUNK_MATMAPFILE              , "CHUNK_MATMAPFILE"            },
+       { CHUNK_OBJECT_MESH             , "CHUNK_OBJECT_MESH"           },
+       { CHUNK_OBJECT_VERTICES , "CHUNK_OBJECT_VERTICES"       },
+       { CHUNK_OBJECT_FACES    , "CHUNK_OBJECT_FACES"          },
+       { CHUNK_OBJECT_MATERIAL , "CHUNK_OBJECT_MATERIAL"       },
+       { CHUNK_OBJECT_UV               , "CHUNK_OBJECT_UV"                     },
+       { 0                                             ,  NULL                                         }
+};
+static char *DebugGetChunkName (int id)
+{
+       int i,max;      /* imax? ;) */
+       max = sizeof(debugChunkNames) / sizeof(debugChunkNames[0]);
+
+       for (i=0; i<max; i++)
+       {
+               if (debugChunkNames[i].id == id)
+               {
+                       /* gaynux update -sea */
+                       return _pico_strlwr( debugChunkNames[i].name );
+               }
+       }
+       return "chunk_unknown";
+}
+#endif /*DEBUG_PM_3DS*/
+
+/* this funky loader needs byte alignment */
+#pragma pack(push, 1)
+
+typedef struct S3dsIndices
+{
+       unsigned short  a,b,c;
+       unsigned short  visible;
+}
+T3dsIndices;
+
+typedef struct S3dsChunk
+{
+       unsigned short  id;
+       unsigned int    len;
+}
+T3dsChunk;
+
+/* restore previous data alignment */
+#pragma pack(pop)
+
+/* _3ds_canload:
+ *  validates an autodesk 3ds model file.
+ */
+static int _3ds_canload( PM_PARAMS_CANLOAD )
+{
+       T3dsChunk *chunk;
+
+       /* to keep the compiler happy */
+       *fileName = *fileName;
+
+       /* sanity check */
+       if (bufSize < sizeof(T3dsChunk))
+               return PICO_PMV_ERROR_SIZE;
+
+       /* get pointer to 3ds header chunk */
+       chunk = (T3dsChunk *)buffer;
+
+       /* check data length */
+       if (bufSize < _pico_little_long(chunk->len))
+               return PICO_PMV_ERROR_SIZE;
+
+       /* check 3ds magic */
+       if (_pico_little_short(chunk->id) != CHUNK_MAIN)
+               return PICO_PMV_ERROR_IDENT;
+
+       /* file seems to be a valid 3ds */
+       return PICO_PMV_OK;
+}
+
+static T3dsChunk *GetChunk (T3dsLoaderPers *pers)
+{
+       T3dsChunk *chunk;
+
+       /* sanity check */
+       if (pers->cofs > pers->maxofs) return 0;
+
+#ifdef DEBUG_PM_3DS
+/*     printf("GetChunk: pers->cofs %x\n",pers->cofs); */
+#endif
+       /* fill in pointer to chunk */
+       chunk = (T3dsChunk *)&pers->bufptr[ pers->cofs ];
+       if (!chunk) return NULL;
+
+       chunk->id  = _pico_little_short(chunk->id );
+       chunk->len = _pico_little_long (chunk->len);
+
+       /* advance in buffer */
+       pers->cofs += sizeof(T3dsChunk);
+
+       /* this means yay */
+       return chunk;
+}
+
+static int GetASCIIZ (T3dsLoaderPers *pers, char *dest, int max)
+{
+       int pos = 0;
+       int ch;
+
+       for (;;)
+       {
+               ch = pers->bufptr[ pers->cofs++ ];
+               if (ch == '\0') break;
+               if (pers->cofs >= pers->maxofs)
+               {
+                       dest[ pos ] = '\0';
+                       return 0;
+               }
+               dest[ pos++ ] = ch;
+               if (pos >= max) break;
+       }
+       dest[ pos ] = '\0';
+       return 1;
+}
+
+static picoByte_t GetByte (T3dsLoaderPers *pers)
+{
+       picoByte_t *value;
+
+       /* sanity check */
+       if (pers->cofs > pers->maxofs) return 0;
+
+       /* get and return value */
+       value = (picoByte_t *)(pers->bufptr + pers->cofs);
+       pers->cofs += 1;
+       return *value;
+}
+
+static int GetWord (T3dsLoaderPers *pers)
+{
+       unsigned short *value;
+
+       /* sanity check */
+       if (pers->cofs > pers->maxofs) return 0;
+
+       /* get and return value */
+       value = (unsigned short *)(pers->bufptr + pers->cofs);
+       pers->cofs += 2;
+       return _pico_little_short(*value);
+}
+
+static float GetFloat (T3dsLoaderPers *pers)
+{
+       float *value;
+
+       /* sanity check */
+       if (pers->cofs > pers->maxofs) return 0;
+
+       /* get and return value */
+       value = (float *)(pers->bufptr + pers->cofs);
+       pers->cofs += 4;
+       return _pico_little_float(*value);
+}
+
+static int GetMeshVertices (T3dsLoaderPers *pers)
+{
+       int numVerts;
+       int i;
+
+       /* get number of verts for this surface */
+       numVerts = GetWord(pers);
+
+#ifdef DEBUG_PM_3DS
+       printf("GetMeshVertices: numverts %d\n",numVerts);
+#endif
+       /* read in vertices for current surface */
+       for (i=0; i<numVerts; i++)
+       {
+               picoVec3_t v;
+               v[0] = GetFloat( pers );
+               v[1] = GetFloat( pers );        /* ydnar: unflipped */
+               v[2] = GetFloat( pers );        /* ydnar: unflipped and negated */
+               
+               /* add current vertex */
+               PicoSetSurfaceXYZ( pers->surface,i,v );
+               PicoSetSurfaceColor( pers->surface,0,i,white ); /* ydnar */
+
+#ifdef DEBUG_PM_3DS_EX
+               printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
+#endif
+       }
+       /* success (no errors occured) */
+       return 1;
+}
+
+static int GetMeshFaces (T3dsLoaderPers *pers)
+{
+       int numFaces;
+       int i;
+
+       /* get number of faces for this surface */
+       numFaces = GetWord(pers);
+
+#ifdef DEBUG_PM_3DS
+       printf("GetMeshFaces: numfaces %d\n",numFaces);
+#endif
+       /* read in vertex indices for current surface */
+       for (i=0; i<numFaces; i++)
+       {
+               /* remember, we only need 3 of 4 values read in for each */
+               /* face. the 4th value is a vis flag for 3dsmax which is */
+               /* being ignored by us here */
+               T3dsIndices face;
+               face.a           = GetWord(pers);
+               face.c           = GetWord(pers);       /* ydnar: flipped order */
+               face.b           = GetWord(pers);       /* ydnar: flipped order */
+               face.visible = GetWord(pers);
+
+               /* copy indexes */
+               PicoSetSurfaceIndex( pers->surface, (i * 3 + 0), (picoIndex_t)face.a );
+               PicoSetSurfaceIndex( pers->surface, (i * 3 + 1), (picoIndex_t)face.b );
+               PicoSetSurfaceIndex( pers->surface, (i * 3 + 2), (picoIndex_t)face.c );
+
+#ifdef DEBUG_PM_3DS_EX
+               printf("Face: a: %d b: %d c: %d (%d)\n",face.a,face.b,face.c,face.visible);
+#endif
+       }
+       /* success (no errors occured) */
+       return 1;
+}
+
+static int GetMeshTexCoords (T3dsLoaderPers *pers)
+{
+       int numTexCoords;
+       int i;
+
+       /* get number of uv coords for this surface */
+       numTexCoords = GetWord(pers);
+
+#ifdef DEBUG_PM_3DS
+       printf("GetMeshTexCoords: numcoords %d\n",numTexCoords);
+#endif
+       /* read in uv coords for current surface */
+       for (i=0; i<numTexCoords; i++)
+       {
+               picoVec2_t uv;
+               uv[0] =  GetFloat( pers );
+               uv[1] = -GetFloat( pers );      /* ydnar: we use origin at bottom */
+
+               /* to make sure we don't mess up memory */
+               if (pers->surface == NULL)
+                       continue;
+               
+               /* add current uv */
+               PicoSetSurfaceST( pers->surface,0,i,uv );
+
+#ifdef DEBUG_PM_3DS_EX
+               printf("u: %f v: %f\n",uv[0],uv[1]);
+#endif
+       }
+       /* success (no errors occured) */
+       return 1;
+}
+
+static int GetMeshShader (T3dsLoaderPers *pers)
+{
+       char shaderName[255] = { 0 };
+       picoShader_t  *shader;
+       int  numSharedVerts;
+       int  setShaderName = 0;
+       int  i;
+       
+       /* the shader is either the color or the texture map of the */
+       /* object. it can also hold other information like the brightness, */
+       /* shine, etc. stuff we don't really care about. we just want the */
+       /* color, or the texture map file name really */
+
+       /* get in the shader name */
+       if (!GetASCIIZ(pers,shaderName,sizeof(shaderName)))
+               return 0;
+
+       /* now that we have the shader name we need to go through all of */
+       /* the shaders and check the name against each shader. when we */
+       /* find a shader in our shader list that matches this name we */
+       /* just read in, then we assign the shader's id of the object to */
+       /* that shader */
+
+       /* get shader id for shader name */
+       shader = PicoFindShader( pers->model, shaderName, 1 );
+
+       /* we've found a matching shader */
+       if ((shader != NULL) && pers->surface)
+       {
+               char  mapName[1024+1];
+               char *mapNamePtr;
+               memset( mapName,0,sizeof(mapName) );
+
+               /* get ptr to shader's map name */
+               mapNamePtr = PicoGetShaderMapName( shader );
+
+               /* we have a valid map name ptr */
+               if (mapNamePtr != NULL)
+               {
+                       char  temp[128];
+                       char *name;
+
+                       /* copy map name to local buffer */
+                       strcpy( mapName,mapNamePtr );
+
+                       /* extract file name */
+                       name = _pico_nopath( mapName );
+                       strncpy( temp, name, sizeof(temp) );
+
+                       /* remove file extension */
+                       /* name = _pico_setfext( name,"" ); */
+
+                       /* assign default name if no name available */
+                       if (strlen(temp) < 1)
+                               strcpy(temp,pers->basename);
+
+                       /* build shader name */
+                       _pico_strlwr( temp ); /* gaynux update -sea */
+                       sprintf( mapName,"models/mapobjects/%s/%s",pers->basename,temp );
+
+                       /* set shader name */
+                       /* PicoSetShaderName( shader,mapName ); */      /* ydnar: this will screw up the named shader */
+
+                       /* set surface's shader index */
+                       PicoSetSurfaceShader( pers->surface, shader );
+
+                       setShaderName = 1;
+               }
+       }
+       /* we didn't set a shader name; throw out warning */
+       if (!setShaderName)
+       {
+               _pico_printf( PICO_WARNING,"3DS mesh is missing shader name");
+       }
+       /* we don't process the list of shared vertices here; there is a */
+       /* short int that gives the number of faces of the mesh concerned */
+       /* by this shader, then there is the list itself of these faces. */
+       /* 0000 means the first face of the (4120) face list */
+
+       /* get number of shared verts */
+       numSharedVerts = GetWord(pers);
+
+#ifdef DEBUG_PM_3DS
+       printf("GetMeshShader: uses shader '%s' (nsv %d)\n",shaderName,numSharedVerts);
+#endif
+       /* skip list of shared verts */
+       for (i=0; i<numSharedVerts; i++)
+       {
+               GetWord(pers);
+       }
+       /* success (no errors occured) */
+       return 1;
+}
+
+static int GetDiffuseColor (T3dsLoaderPers *pers)
+{
+       /* todo: support all 3ds specific color formats; */
+       /* that means: rgb,tru,trug,rgbg */
+
+       /* get rgb color (range 0..255; 3 bytes) */
+       picoColor_t color;
+
+       color[0] = GetByte(pers);
+       color[1] = GetByte(pers);
+       color[2] = GetByte(pers);
+       color[3] = 255;
+
+       /* store this as the current shader's diffuse color */
+       if( pers->shader )
+       {
+               PicoSetShaderDiffuseColor( pers->shader,color );
+       }
+#ifdef DEBUG_PM_3DS
+       printf("GetDiffuseColor: %d %d %d\n",color[0],color[1],color[2]);
+#endif
+       /* success (no errors occured) */
+       return 1;
+}
+
+static int DoNextEditorDataChunk (T3dsLoaderPers *pers, long endofs)
+{
+       T3dsChunk *chunk;
+
+#ifdef DEBUG_PM_3DS_EX
+       printf("DoNextEditorDataChunk: endofs %d\n",endofs);
+#endif
+       while (pers->cofs < endofs)
+       {
+               long nextofs = pers->cofs;
+               if ((chunk = GetChunk(pers)) == NULL) return 0;
+               if (!chunk->len) return 0;
+               nextofs += chunk->len;
+
+#ifdef DEBUG_PM_3DS_EX
+               printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);
+#endif
+               /*** meshes ***/
+               if (chunk->id == CHUNK_OBJECT)
+               {
+                       picoSurface_t *surface;
+                       char surfaceName[ 0xff ] = { 0 };
+
+                       /* read in surface name */
+                       if( !GetASCIIZ(pers,surfaceName,sizeof(surfaceName)) )
+                               return 0; /* this is bad */
+
+//PicoGetSurfaceName
+                       /* ignore NULL name surfaces */
+//                     if( surfaceName
+
+                       /* allocate a pico surface */
+                       surface = PicoNewSurface( pers->model );
+                       if( surface == NULL )
+                       {
+                               pers->surface = NULL;
+                               return 0; /* this is bad too */
+                       }
+                       /* assign ptr to current surface */
+                       pers->surface = surface;
+
+                       /* 3ds models surfaces are all triangle meshes */
+                       PicoSetSurfaceType( pers->surface,PICO_TRIANGLES );
+
+                       /* set surface name */
+                       PicoSetSurfaceName( pers->surface,surfaceName );
+
+                       /* continue mess with object's sub chunks */
+                       DoNextEditorDataChunk(pers,nextofs);
+                       continue;
+               }
+               if (chunk->id == CHUNK_OBJECT_MESH)
+               {
+                       /* continue mess with mesh's sub chunks */
+                       if (!DoNextEditorDataChunk(pers,nextofs)) return 0;
+                       continue;
+               }
+               if (chunk->id == CHUNK_OBJECT_VERTICES)
+               {
+                       if (!GetMeshVertices(pers)) return 0;
+                       continue;
+               }
+               if (chunk->id == CHUNK_OBJECT_FACES)
+               {
+                       if (!GetMeshFaces(pers)) return 0;
+                       continue;
+               }
+               if (chunk->id == CHUNK_OBJECT_UV)
+               {
+                       if (!GetMeshTexCoords(pers)) return 0;
+                       continue;
+               }
+               if (chunk->id == CHUNK_OBJECT_MATERIAL)
+               {
+                       if (!GetMeshShader(pers)) return 0;
+                       continue;
+               }
+               /*** materials ***/
+               if (chunk->id == CHUNK_MATERIAL)
+               {
+                       /* new shader specific things should be */
+                       /* initialized right here */
+                       picoShader_t *shader;
+
+                       /* allocate a pico shader */
+                       shader = PicoNewShader( pers->model );  /* ydnar */
+                       if( shader == NULL )
+                       {
+                               pers->shader = NULL;
+                               return 0; /* this is bad too */
+                       }
+                       
+                       /* assign ptr to current shader */
+                       pers->shader = shader;
+
+                       /* continue and process the material's sub chunks */
+                       DoNextEditorDataChunk(pers,nextofs);
+                       continue;
+               }
+               if (chunk->id == CHUNK_MATNAME)
+               {
+                       /* new material's names should be stored here. note that */
+                       /* GetMeshMaterial returns the name of the material that */
+                       /* is used by the mesh. new material names are set HERE. */
+                       /* but for now we skip the new material's name ... */
+                       if (pers->shader)
+                       {
+                               char *name = (char *)(pers->bufptr + pers->cofs);
+                               PicoSetShaderName( pers->shader,name );
+#ifdef DEBUG_PM_3DS
+                               printf("NewShader: '%s'\n",name);
+#endif
+                       }
+               }
+               if (chunk->id == CHUNK_MATDIFFUSE)
+               {
+                       /* todo: color for last inserted new material should be */
+                       /* stored somewhere by GetDiffuseColor */
+                       if (!GetDiffuseColor(pers)) return 0;
+
+                       /* rest of chunk is skipped here */
+               }
+               if (chunk->id == CHUNK_MATMAP)
+               {
+                       /* continue and process the material map sub chunks */
+                       DoNextEditorDataChunk(pers,nextofs);
+                       continue;
+               }
+               if (chunk->id == CHUNK_MATMAPFILE)
+               {
+                       /* map file name for last inserted new material should */
+                       /* be stored here. but for now we skip this too ... */
+                       if( pers->shader )
+                       {
+                               char *name = (char *)(pers->bufptr + pers->cofs);
+                               PicoSetShaderMapName( pers->shader,name );
+#ifdef DEBUG_PM_3DS
+                               printf("NewShaderMapfile: '%s'\n",name);
+#endif
+                       }
+               }
+               /*** keyframes ***/
+               if (chunk->id == CHUNK_KEYFRAME_DATA)
+               {
+                       /* well umm, this is a bit too much since we don't really */
+                       /* need model animation sequences right now. we skip this */
+#ifdef DEBUG_PM_3DS
+                       printf("KeyframeData: len %d\n",chunk->len);
+#endif
+               }
+               /* skip unknown chunk */
+               pers->cofs = nextofs;
+               if (pers->cofs >= pers->maxofs) break;
+       }
+       return 1;
+}
+
+static int DoNextChunk (T3dsLoaderPers *pers, int endofs)
+{
+       T3dsChunk *chunk;
+
+#ifdef DEBUG_PM_3DS
+       printf("DoNextChunk: endofs %d\n",endofs);
+#endif
+       while (pers->cofs < endofs)
+       {
+               long nextofs = pers->cofs;
+               if ((chunk = GetChunk(pers)) == NULL) return 0;
+               if (!chunk->len) return 0;
+               nextofs += chunk->len;
+
+#ifdef DEBUG_PM_3DS_EX
+               printf("Chunk %04x (%s), len %d pers->cofs %x\n",chunk->id,DebugGetChunkName(chunk->id),chunk->len,pers->cofs);
+#endif
+               /*** version ***/
+               if (chunk->id == CHUNK_VERSION)
+               {
+                       /* at this point i get the 3ds file version. since there */
+                       /* might be new additions to the 3ds file format in 4.0 */
+                       /* it might be a good idea to store the version somewhere */
+                       /* for later handling or message displaying */
+
+                       /* get the version */
+                       int version;
+                       version = GetWord(pers);
+                       GetWord(pers);
+#ifdef DEBUG_PM_3DS
+                       printf("FileVersion: %d\n",version);
+#endif
+
+                       /* throw out a warning for version 4 models */
+                       if (version == 4)
+                       {
+                               _pico_printf( PICO_WARNING,
+                                       "3DS version is 4. Model might load incorrectly.");
+                       }
+                       /* store the 3ds file version in pico special field 0 */
+                       /* PicoSetSurfaceSpecial(pers->surface,0,version); */           /* ydnar: this was causing a crash accessing uninitialized surface */
+                       
+                       /* rest of chunk is skipped here */
+               }
+               /*** editor data ***/
+               if (chunk->id == CHUNK_EDITOR_DATA)
+               {
+                       if (!DoNextEditorDataChunk(pers,nextofs)) return 0;
+                       continue;
+               }
+               /* skip unknown chunk */
+               pers->cofs = nextofs;
+               if (pers->cofs >= pers->maxofs) break;
+       }
+       return 1;
+}
+
+/* _3ds_load:
+ *  loads an autodesk 3ds model file.
+*/
+static picoModel_t *_3ds_load( PM_PARAMS_LOAD )
+{
+       T3dsLoaderPers  pers;
+       picoModel_t        *model;
+       char                    basename[128];
+
+       /* create a new pico model */
+       model = PicoNewModel();
+       if (model == NULL)
+       {
+               /* user must have some serious ram problems ;) */
+               return NULL;
+       }
+       /* get model's base name (eg. jeep from c:\models\jeep.3ds) */
+       memset( basename,0,sizeof(basename) );
+       strncpy( basename,_pico_nopath(fileName),sizeof(basename) );
+       _pico_setfext( basename,"" );
+
+       /* initialize persistant vars (formerly static) */
+       pers.model    =  model;
+       pers.bufptr   = (picoByte_t *)buffer;
+       pers.basename = (char *)basename;
+       pers.maxofs   =  bufSize;
+       pers.cofs     =  0L;
+
+       /* do model setup */
+       PicoSetModelFrameNum( model,frameNum );
+       PicoSetModelName( model,fileName );
+       PicoSetModelFileName( model,fileName );
+
+       /* skip first chunk in file (magic) */
+       GetChunk(&pers);
+
+       /* process chunks */
+       if (!DoNextChunk(&pers,pers.maxofs))
+       {
+               /* well, bleh i guess */
+               PicoFreeModel(model);
+               return NULL;
+       }
+       /* return allocated pico model */
+       return model;
+}
+
+/* pico file format module definition */
+const picoModule_t picoModule3DS =
+{
+       "0.86-b",                                       /* module version string */
+       "Autodesk 3Dstudio",            /* module display name */
+       "seaw0lf",                                      /* author's name */
+       "2002 seaw0lf",                         /* module copyright */
+       {
+               "3ds",NULL,NULL,NULL    /* default extensions to use */
+       },
+       _3ds_canload,                           /* validation routine */
+       _3ds_load,                                      /* load routine */
+        NULL,                                          /* save validation routine */
+        NULL                                           /* save routine */
+};