]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/pm_md2.c
eol style
[xonotic/netradiant.git] / libs / picomodel / pm_md2.c
index 2351ea58f09f667885901965711029cfe500bf5b..8a603a5d0fa09b596993b0e6013f8278c838b757 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
-Nurail: Used pm_md3.c (Randy Reddig) as a template.\r
-*/\r
-\r
-\r
-/* marker */\r
-#define PM_MD2_C\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-\r
-\r
-/* md2 model format */\r
-#define MD2_MAGIC                              "IDP2"\r
-#define MD2_VERSION                            8\r
-\r
-#define MD2_NUMVERTEXNORMALS   162\r
-#define MD2_MAX_SKINNAME               64\r
-#define MD2_MAX_TRIANGLES              4096\r
-#define MD2_MAX_VERTS                  2048\r
-#define MD2_MAX_FRAMES                 512\r
-#define MD2_MAX_MD2SKINS               32\r
-#define MD2_MAX_SKINNAME               64\r
-\r
-#ifndef byte\r
-       #define byte unsigned char\r
-#endif\r
-\r
-typedef struct index_LUT_s\r
-{\r
-       short   Vert;\r
-       short   ST;\r
-       struct  index_LUT_s     *next;\r
-\r
-} index_LUT_t;\r
-\r
-typedef struct index_DUP_LUT_s\r
-{\r
-       short                   ST;\r
-       short                   OldVert;\r
-\r
-} index_DUP_LUT_t;\r
-\r
-typedef struct\r
-{\r
-        short   s;\r
-        short   t;\r
-} md2St_t;\r
-\r
-typedef struct\r
-{\r
-        short   index_xyz[3];\r
-        short   index_st[3];\r
-} md2Triangle_t;\r
-\r
-typedef struct\r
-{\r
-        byte   v[3];                   // scaled byte to fit in frame mins/maxs\r
-        byte   lightnormalindex;\r
-} md2XyzNormal_t;\r
-\r
-typedef struct md2Frame_s\r
-{\r
-        float           scale[3];       // multiply byte verts by this\r
-        float           translate[3];   // then add this\r
-        char            name[16];       // frame name from grabbing\r
-        md2XyzNormal_t  verts[1];       // variable sized\r
-}\r
-md2Frame_t;\r
-\r
-\r
-/* md2 model file md2 structure */\r
-typedef struct md2_s\r
-{\r
-       char    magic[ 4 ];\r
-       int     version;\r
-\r
-       int     skinWidth;\r
-       int     skinHeight;\r
-       int     frameSize;\r
-\r
-       int     numSkins;\r
-       int     numXYZ;\r
-       int     numST;\r
-       int     numTris;\r
-       int     numGLCmds;\r
-       int     numFrames;\r
-\r
-       int     ofsSkins;\r
-       int     ofsST;\r
-       int     ofsTris;\r
-       int     ofsFrames;\r
-       int     ofsGLCmds;\r
-       int     ofsEnd;\r
-}\r
-md2_t;\r
-\r
-float  md2_normals[ MD2_NUMVERTEXNORMALS ][ 3 ] =\r
-{\r
-       { -0.525731f, 0.000000f, 0.850651f },\r
-       { -0.442863f, 0.238856f, 0.864188f }, \r
-       { -0.295242f, 0.000000f, 0.955423f }, \r
-       { -0.309017f, 0.500000f, 0.809017f }, \r
-       { -0.162460f, 0.262866f, 0.951056f }, \r
-       { 0.000000f, 0.000000f, 1.000000f },\r
-       { 0.000000f, 0.850651f, 0.525731f },\r
-       { -0.147621f, 0.716567f, 0.681718f },\r
-       { 0.147621f, 0.716567f, 0.681718f }, \r
-       { 0.000000f, 0.525731f, 0.850651f }, \r
-       { 0.309017f, 0.500000f, 0.809017f }, \r
-       { 0.525731f, 0.000000f, 0.850651f }, \r
-       { 0.295242f, 0.000000f, 0.955423f }, \r
-       { 0.442863f, 0.238856f, 0.864188f }, \r
-       { 0.162460f, 0.262866f, 0.951056f }, \r
-       { -0.681718f, 0.147621f, 0.716567f }, \r
-       { -0.809017f, 0.309017f, 0.500000f }, \r
-       { -0.587785f, 0.425325f, 0.688191f }, \r
-       { -0.850651f, 0.525731f, 0.000000f }, \r
-       { -0.864188f, 0.442863f, 0.238856f }, \r
-       { -0.716567f, 0.681718f, 0.147621f }, \r
-       { -0.688191f, 0.587785f, 0.425325f }, \r
-       { -0.500000f, 0.809017f, 0.309017f }, \r
-       { -0.238856f, 0.864188f, 0.442863f }, \r
-       { -0.425325f, 0.688191f, 0.587785f }, \r
-       { -0.716567f, 0.681718f, -0.147621f }, \r
-       { -0.500000f, 0.809017f, -0.309017f }, \r
-       { -0.525731f, 0.850651f, 0.000000f }, \r
-       { 0.000000f, 0.850651f, -0.525731f }, \r
-       { -0.238856f, 0.864188f, -0.442863f }, \r
-       { 0.000000f, 0.955423f, -0.295242f }, \r
-       { -0.262866f, 0.951056f, -0.162460f }, \r
-       { 0.000000f, 1.000000f, 0.000000f }, \r
-       { 0.000000f, 0.955423f, 0.295242f }, \r
-       { -0.262866f, 0.951056f, 0.162460f }, \r
-       { 0.238856f, 0.864188f, 0.442863f }, \r
-       { 0.262866f, 0.951056f, 0.162460f }, \r
-       { 0.500000f, 0.809017f, 0.309017f }, \r
-       { 0.238856f, 0.864188f, -0.442863f }, \r
-       { 0.262866f, 0.951056f, -0.162460f }, \r
-       { 0.500000f, 0.809017f, -0.309017f }, \r
-       { 0.850651f, 0.525731f, 0.000000f }, \r
-       { 0.716567f, 0.681718f, 0.147621f }, \r
-       { 0.716567f, 0.681718f, -0.147621f }, \r
-       { 0.525731f, 0.850651f, 0.000000f }, \r
-       { 0.425325f, 0.688191f, 0.587785f }, \r
-       { 0.864188f, 0.442863f, 0.238856f }, \r
-       { 0.688191f, 0.587785f, 0.425325f }, \r
-       { 0.809017f, 0.309017f, 0.500000f }, \r
-       { 0.681718f, 0.147621f, 0.716567f }, \r
-       { 0.587785f, 0.425325f, 0.688191f }, \r
-       { 0.955423f, 0.295242f, 0.000000f }, \r
-       { 1.000000f, 0.000000f, 0.000000f }, \r
-       { 0.951056f, 0.162460f, 0.262866f }, \r
-       { 0.850651f, -0.525731f, 0.000000f }, \r
-       { 0.955423f, -0.295242f, 0.000000f }, \r
-       { 0.864188f, -0.442863f, 0.238856f }, \r
-       { 0.951056f, -0.162460f, 0.262866f }, \r
-       { 0.809017f, -0.309017f, 0.500000f }, \r
-       { 0.681718f, -0.147621f, 0.716567f }, \r
-       { 0.850651f, 0.000000f, 0.525731f }, \r
-       { 0.864188f, 0.442863f, -0.238856f }, \r
-       { 0.809017f, 0.309017f, -0.500000f }, \r
-       { 0.951056f, 0.162460f, -0.262866f }, \r
-       { 0.525731f, 0.000000f, -0.850651f }, \r
-       { 0.681718f, 0.147621f, -0.716567f }, \r
-       { 0.681718f, -0.147621f, -0.716567f }, \r
-       { 0.850651f, 0.000000f, -0.525731f }, \r
-       { 0.809017f, -0.309017f, -0.500000f }, \r
-       { 0.864188f, -0.442863f, -0.238856f }, \r
-       { 0.951056f, -0.162460f, -0.262866f }, \r
-       { 0.147621f, 0.716567f, -0.681718f }, \r
-       { 0.309017f, 0.500000f, -0.809017f }, \r
-       { 0.425325f, 0.688191f, -0.587785f }, \r
-       { 0.442863f, 0.238856f, -0.864188f }, \r
-       { 0.587785f, 0.425325f, -0.688191f }, \r
-       { 0.688191f, 0.587785f, -0.425325f }, \r
-       { -0.147621f, 0.716567f, -0.681718f }, \r
-       { -0.309017f, 0.500000f, -0.809017f }, \r
-       { 0.000000f, 0.525731f, -0.850651f }, \r
-       { -0.525731f, 0.000000f, -0.850651f }, \r
-       { -0.442863f, 0.238856f, -0.864188f }, \r
-       { -0.295242f, 0.000000f, -0.955423f }, \r
-       { -0.162460f, 0.262866f, -0.951056f }, \r
-       { 0.000000f, 0.000000f, -1.000000f }, \r
-       { 0.295242f, 0.000000f, -0.955423f }, \r
-       { 0.162460f, 0.262866f, -0.951056f }, \r
-       { -0.442863f, -0.238856f, -0.864188f }, \r
-       { -0.309017f, -0.500000f, -0.809017f }, \r
-       { -0.162460f, -0.262866f, -0.951056f }, \r
-       { 0.000000f, -0.850651f, -0.525731f }, \r
-       { -0.147621f, -0.716567f, -0.681718f }, \r
-       { 0.147621f, -0.716567f, -0.681718f }, \r
-       { 0.000000f, -0.525731f, -0.850651f }, \r
-       { 0.309017f, -0.500000f, -0.809017f }, \r
-       { 0.442863f, -0.238856f, -0.864188f }, \r
-       { 0.162460f, -0.262866f, -0.951056f }, \r
-       { 0.238856f, -0.864188f, -0.442863f }, \r
-       { 0.500000f, -0.809017f, -0.309017f }, \r
-       { 0.425325f, -0.688191f, -0.587785f }, \r
-       { 0.716567f, -0.681718f, -0.147621f }, \r
-       { 0.688191f, -0.587785f, -0.425325f }, \r
-       { 0.587785f, -0.425325f, -0.688191f }, \r
-       { 0.000000f, -0.955423f, -0.295242f }, \r
-       { 0.000000f, -1.000000f, 0.000000f }, \r
-       { 0.262866f, -0.951056f, -0.162460f }, \r
-       { 0.000000f, -0.850651f, 0.525731f }, \r
-       { 0.000000f, -0.955423f, 0.295242f }, \r
-       { 0.238856f, -0.864188f, 0.442863f }, \r
-       { 0.262866f, -0.951056f, 0.162460f }, \r
-       { 0.500000f, -0.809017f, 0.309017f }, \r
-       { 0.716567f, -0.681718f, 0.147621f }, \r
-       { 0.525731f, -0.850651f, 0.000000f }, \r
-       { -0.238856f, -0.864188f, -0.442863f }, \r
-       { -0.500000f, -0.809017f, -0.309017f }, \r
-       { -0.262866f, -0.951056f, -0.162460f }, \r
-       { -0.850651f, -0.525731f, 0.000000f }, \r
-       { -0.716567f, -0.681718f, -0.147621f }, \r
-       { -0.716567f, -0.681718f, 0.147621f }, \r
-       { -0.525731f, -0.850651f, 0.000000f }, \r
-       { -0.500000f, -0.809017f, 0.309017f }, \r
-       { -0.238856f, -0.864188f, 0.442863f }, \r
-       { -0.262866f, -0.951056f, 0.162460f }, \r
-       { -0.864188f, -0.442863f, 0.238856f }, \r
-       { -0.809017f, -0.309017f, 0.500000f }, \r
-       { -0.688191f, -0.587785f, 0.425325f }, \r
-       { -0.681718f, -0.147621f, 0.716567f }, \r
-       { -0.442863f, -0.238856f, 0.864188f }, \r
-       { -0.587785f, -0.425325f, 0.688191f }, \r
-       { -0.309017f, -0.500000f, 0.809017f }, \r
-       { -0.147621f, -0.716567f, 0.681718f }, \r
-       { -0.425325f, -0.688191f, 0.587785f }, \r
-       { -0.162460f, -0.262866f, 0.951056f }, \r
-       { 0.442863f, -0.238856f, 0.864188f }, \r
-       { 0.162460f, -0.262866f, 0.951056f }, \r
-       { 0.309017f, -0.500000f, 0.809017f }, \r
-       { 0.147621f, -0.716567f, 0.681718f }, \r
-       { 0.000000f, -0.525731f, 0.850651f }, \r
-       { 0.425325f, -0.688191f, 0.587785f }, \r
-       { 0.587785f, -0.425325f, 0.688191f }, \r
-       { 0.688191f, -0.587785f, 0.425325f }, \r
-       { -0.955423f, 0.295242f, 0.000000f }, \r
-       { -0.951056f, 0.162460f, 0.262866f }, \r
-       { -1.000000f, 0.000000f, 0.000000f }, \r
-       { -0.850651f, 0.000000f, 0.525731f }, \r
-       { -0.955423f, -0.295242f, 0.000000f }, \r
-       { -0.951056f, -0.162460f, 0.262866f }, \r
-       { -0.864188f, 0.442863f, -0.238856f }, \r
-       { -0.951056f, 0.162460f, -0.262866f }, \r
-       { -0.809017f, 0.309017f, -0.500000f }, \r
-       { -0.864188f, -0.442863f, -0.238856f },\r
-       { -0.951056f, -0.162460f, -0.262866f }, \r
-       { -0.809017f, -0.309017f, -0.500000f }, \r
-       { -0.681718f, 0.147621f, -0.716567f }, \r
-       { -0.681718f, -0.147621f, -0.716567f }, \r
-       { -0.850651f, 0.000000f, -0.525731f }, \r
-       { -0.688191f, 0.587785f, -0.425325f }, \r
-       { -0.587785f, 0.425325f, -0.688191f }, \r
-       { -0.425325f, 0.688191f, -0.587785f }, \r
-       { -0.425325f, -0.688191f, -0.587785f }, \r
-       { -0.587785f, -0.425325f, -0.688191f }, \r
-       { -0.688191f, -0.587785f, -0.425325f }, \r
-};\r
-\r
-\r
-// _md2_canload()\r
-\r
-static int _md2_canload( PM_PARAMS_CANLOAD )\r
-{\r
-       md2_t   *md2;\r
-\r
-       /* to keep the compiler happy */\r
-       *fileName = *fileName;\r
-\r
-       /* sanity check */\r
-       if( bufSize < ( sizeof( *md2 ) * 2) )\r
-               return PICO_PMV_ERROR_SIZE;\r
-       \r
-       /* set as md2 */\r
-       md2 = (md2_t*) buffer;\r
-       \r
-       /* check md2 magic */\r
-       if( *((int*) md2->magic) != *((int*) MD2_MAGIC) ) \r
-               return PICO_PMV_ERROR_IDENT;\r
-       \r
-       /* check md2 version */\r
-       if( _pico_little_long( md2->version ) != MD2_VERSION )\r
-               return PICO_PMV_ERROR_VERSION;\r
-\r
-       /* file seems to be a valid md2 */\r
-       return PICO_PMV_OK;\r
-}\r
-\r
-\r
-\r
-// _md2_load() loads a quake2 md2 model file.\r
-\r
-\r
-static picoModel_t *_md2_load( PM_PARAMS_LOAD )\r
-{\r
-       int                             i, j, dups, dup_index;\r
-       short                   tot_numVerts;\r
-       index_LUT_t             *p_index_LUT, *p_index_LUT2, *p_index_LUT3;\r
-       index_DUP_LUT_t *p_index_LUT_DUPS;\r
-       md2Triangle_t   *p_md2Triangle;\r
-\r
-       char                    skinname[ MD2_MAX_SKINNAME ];\r
-       md2_t                   *md2;\r
-       md2St_t                 *texCoord;\r
-       md2Frame_t              *frame;\r
-       md2Triangle_t   *triangle;\r
-       md2XyzNormal_t  *vertex;\r
-\r
-       picoByte_t      *bb;\r
-       picoModel_t             *picoModel;\r
-       picoSurface_t   *picoSurface;\r
-       picoShader_t    *picoShader;\r
-       picoVec3_t              xyz, normal;\r
-       picoVec2_t              st;\r
-       picoColor_t             color;\r
-       \r
-\r
-       // md2 loading\r
-       _pico_printf( PICO_NORMAL, "Loading \"%s\"", fileName );\r
-\r
-       /* set as md2 */\r
-       bb = (picoByte_t*) buffer;\r
-       md2     = (md2_t*) buffer;\r
-\r
-       /* check ident and version */\r
-       if( *((int*) md2->magic) != *((int*) MD2_MAGIC) || _pico_little_long( md2->version ) != MD2_VERSION )\r
-       {\r
-               /* not an md2 file (todo: set error) */\r
-               _pico_printf( PICO_ERROR, "%s is not an MD2 File!", fileName );\r
-               return NULL;\r
-       }\r
-       \r
-       // swap md2\r
-       md2->version = _pico_little_long( md2->version );\r
-\r
-       md2->skinWidth = _pico_little_long( md2->skinWidth );\r
-       md2->skinHeight = _pico_little_long( md2->skinHeight );\r
-       md2->frameSize = _pico_little_long( md2->frameSize );\r
-\r
-       md2->numSkins = _pico_little_long( md2->numSkins );\r
-       md2->numXYZ = _pico_little_long( md2->numXYZ );\r
-       md2->numST = _pico_little_long( md2->numST );\r
-       md2->numTris = _pico_little_long( md2->numTris );\r
-       md2->numGLCmds = _pico_little_long( md2->numGLCmds );\r
-       md2->numFrames = _pico_little_long( md2->numFrames );\r
-\r
-       md2->ofsSkins = _pico_little_long( md2->ofsSkins );\r
-       md2->ofsST = _pico_little_long( md2->ofsST );\r
-       md2->ofsTris = _pico_little_long( md2->ofsTris );\r
-       md2->ofsFrames = _pico_little_long( md2->ofsFrames );\r
-       md2->ofsGLCmds = _pico_little_long( md2->ofsGLCmds );\r
-       md2->ofsEnd = _pico_little_long( md2->ofsEnd );\r
-\r
-       // do frame check\r
-       if( md2->numFrames < 1 )\r
-       {\r
-               _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );\r
-               return NULL;\r
-       }\r
-       \r
-       if( frameNum < 0 || frameNum >= md2->numFrames )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Invalid or out-of-range MD2 frame specified" );\r
-               return NULL;\r
-       }\r
-\r
-       // Setup Frame\r
-       frame = (md2Frame_t *) (bb + md2->ofsFrames + (sizeof(md2Frame_t) * frameNum));\r
-\r
-       // swap frame scale and translation\r
-       for( i = 0; i < 3; i++ )\r
-       {\r
-               frame->scale[ i ] = _pico_little_float( frame->scale[ i ] );\r
-               frame->translate[ i ] = _pico_little_float( frame->translate[ i ] );\r
-       }\r
-\r
-       // swap triangles\r
-       triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) );\r
-       for( i = 0; i < md2->numTris; i++, triangle++ )\r
-       {\r
-               for( j = 0; j < 3; j++ )\r
-               {\r
-                       triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );\r
-                       triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );\r
-               }\r
-       }\r
-\r
-       // swap st coords\r
-       texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) );\r
-       for( i = 0; i < md2->numST; i++, texCoord++ )\r
-       {\r
-               texCoord->s = _pico_little_short( texCoord->s );\r
-               texCoord->t = _pico_little_short( texCoord->t );\r
-       }\r
-\r
-       // set Skin Name\r
-       strncpy(skinname, (bb + md2->ofsSkins), MD2_MAX_SKINNAME );\r
-\r
-       // Print out md2 values\r
-       _pico_printf(PICO_VERBOSE,"Skins: %d  Verts: %d  STs: %d  Triangles: %d  Frames: %d\nSkin Name \"%s\"\n", md2->numSkins, md2->numXYZ, md2->numST, md2->numTris, md2->numFrames, &skinname );\r
-\r
-       // detox Skin name\r
-       _pico_setfext( skinname, "" );\r
-       _pico_unixify( skinname );\r
-\r
-       /* create new pico model */\r
-       picoModel = PicoNewModel();\r
-       if( picoModel == NULL )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Unable to allocate a new model" );\r
-               return NULL;\r
-       }\r
-\r
-       /* do model setup */\r
-       PicoSetModelFrameNum( picoModel, frameNum );\r
-       PicoSetModelNumFrames( picoModel, md2->numFrames ); /* sea */\r
-       PicoSetModelName( picoModel, fileName );\r
-       PicoSetModelFileName( picoModel, fileName );\r
-\r
-       // allocate new pico surface\r
-       picoSurface = PicoNewSurface( picoModel );\r
-       if( picoSurface == NULL )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );\r
-               PicoFreeModel( picoModel );\r
-               return NULL;\r
-       }\r
-\r
-\r
-       PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );\r
-       PicoSetSurfaceName( picoSurface, frame->name );\r
-       picoShader = PicoNewShader( picoModel );\r
-       if( picoShader == NULL )\r
-       {\r
-               _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );\r
-               PicoFreeModel( picoModel );\r
-               return NULL;\r
-       }\r
-\r
-       PicoSetShaderName( picoShader, skinname );\r
-\r
-       // associate current surface with newly created shader\r
-       PicoSetSurfaceShader( picoSurface, picoShader );\r
-\r
-       // Init LUT for Verts\r
-       p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * md2->numXYZ);\r
-       for(i=0; i<md2->numXYZ; i++)\r
-       {\r
-               p_index_LUT[i].Vert = -1;\r
-               p_index_LUT[i].ST = -1;\r
-               p_index_LUT[i].next = NULL;\r
-       }\r
-\r
-       // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.\r
-       tot_numVerts = md2->numXYZ;\r
-       dups = 0;\r
-       for(i=0; i<md2->numTris; i++)\r
-       {\r
-               p_md2Triangle = (md2Triangle_t *) ( bb + md2->ofsTris + (sizeof(md2Triangle_t)*i));\r
-               for(j=0; j<3; j++)\r
-               {\r
-                       if (p_index_LUT[p_md2Triangle->index_xyz[j]].ST == -1) // No Main Entry\r
-                               p_index_LUT[p_md2Triangle->index_xyz[j]].ST = p_md2Triangle->index_st[j];\r
-\r
-                       else if (p_md2Triangle->index_st[j] == p_index_LUT[p_md2Triangle->index_xyz[j]].ST ) // Equal to Main Entry\r
-                               continue;\r
-\r
-                       else if ( (p_index_LUT[p_md2Triangle->index_xyz[j]].next == NULL) )  // Not equal to Main entry, and no LL entry\r
-                       {       // Add first entry of LL from Main\r
-                               p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));\r
-                               if (p_index_LUT2 == NULL)\r
-                                       _pico_printf( PICO_ERROR," Couldn't allocate memory!\n");\r
-                               p_index_LUT[p_md2Triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;\r
-                               p_index_LUT2->Vert = dups;\r
-                               p_index_LUT2->ST = p_md2Triangle->index_st[j];\r
-                               p_index_LUT2->next = NULL;\r
-                               p_md2Triangle->index_xyz[j] = dups + md2->numXYZ; // Make change in Tri hunk\r
-                               dups++;\r
-                       }\r
-                       else // Try to find in LL from Main Entry\r
-                       {\r
-                               p_index_LUT3 = p_index_LUT2 = p_index_LUT[p_md2Triangle->index_xyz[j]].next;\r
-                               while ( (p_index_LUT2 != NULL) && (p_md2Triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL\r
-                               {\r
-                                       p_index_LUT3 = p_index_LUT2;\r
-                                       p_index_LUT2 = p_index_LUT2->next;\r
-                               }\r
-                               p_index_LUT2 = p_index_LUT3;\r
-\r
-                               if ( p_md2Triangle->index_st[j] == p_index_LUT2->ST ) // Found it\r
-                               {\r
-                                       p_md2Triangle->index_xyz[j] = p_index_LUT2->Vert + md2->numXYZ; // Make change in Tri hunk\r
-                                       continue;\r
-                               }\r
-\r
-                               if ( p_index_LUT2->next == NULL)  // Didn't find it. Add entry to LL.\r
-                               {\r
-                                       // Add the Entry\r
-                                       p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));\r
-                                       if (p_index_LUT3 == NULL)\r
-                                               _pico_printf( PICO_ERROR," Couldn't allocate memory!\n");\r
-                                       p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;\r
-                                       p_index_LUT3->Vert = p_md2Triangle->index_xyz[j];\r
-                                       p_index_LUT3->ST = p_md2Triangle->index_st[j];\r
-                                       p_index_LUT3->next = NULL;\r
-                                       p_md2Triangle->index_xyz[j] = dups + md2->numXYZ; // Make change in Tri hunk\r
-                                       dups++;\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       // malloc and build array for Dup STs\r
-       p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups);\r
-       if (p_index_LUT_DUPS == NULL)\r
-               _pico_printf( PICO_ERROR," Couldn't allocate memory!\n");\r
-\r
-       dup_index = 0;\r
-       for(i=0; i<md2->numXYZ; i++)\r
-       {\r
-               p_index_LUT2 = p_index_LUT[i].next;\r
-               while (p_index_LUT2 != NULL)\r
-               {\r
-                       p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;\r
-                       p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;\r
-                       dup_index++;\r
-                       p_index_LUT2 = p_index_LUT2->next;\r
-               }\r
-       }\r
-\r
-       // Build Picomodel\r
-       triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) );\r
-       texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) );\r
-       vertex = (md2XyzNormal_t*) ((picoByte_t*) (frame->verts) );\r
-       for( j = 0; j < md2->numTris; j++, triangle++ )\r
-       {\r
-               PicoSetSurfaceIndex( picoSurface, j*3   , triangle->index_xyz[0] );\r
-               PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] );\r
-               PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] );\r
-       }\r
-\r
-       for(i=0; i< md2->numXYZ; i++, vertex++)\r
-       {\r
-               /* set vertex origin */\r
-               xyz[ 0 ] = vertex->v[0] * frame->scale[0] + frame->translate[0];\r
-               xyz[ 1 ] = vertex->v[1] * frame->scale[1] + frame->translate[1];\r
-               xyz[ 2 ] = vertex->v[2] * frame->scale[2] + frame->translate[2];\r
-               PicoSetSurfaceXYZ( picoSurface, i , xyz );\r
-\r
-               /* set normal */\r
-               normal[ 0 ] = md2_normals[vertex->lightnormalindex][0];\r
-               normal[ 1 ] = md2_normals[vertex->lightnormalindex][1];\r
-               normal[ 2 ] = md2_normals[vertex->lightnormalindex][2];\r
-               PicoSetSurfaceNormal( picoSurface, i , normal );\r
-\r
-               /* set st coords */\r
-               st[ 0 ] =  ((texCoord[p_index_LUT[i].ST].s) / ((float)md2->skinWidth));\r
-               st[ 1 ] =  (texCoord[p_index_LUT[i].ST].t / ((float)md2->skinHeight));\r
-               PicoSetSurfaceST( picoSurface, 0, i , st );\r
-       }\r
-\r
-       if (dups)\r
-       {\r
-               for(i=0; i<dups; i++)\r
-               {\r
-                       j = p_index_LUT_DUPS[i].OldVert;\r
-                       /* set vertex origin */\r
-                       xyz[ 0 ] = frame->verts[j].v[0] * frame->scale[0] + frame->translate[0];\r
-                       xyz[ 1 ] = frame->verts[j].v[1] * frame->scale[1] + frame->translate[1];\r
-                       xyz[ 2 ] = frame->verts[j].v[2] * frame->scale[2] + frame->translate[2];\r
-                       PicoSetSurfaceXYZ( picoSurface, i + md2->numXYZ , xyz );\r
-\r
-                       /* set normal */\r
-                       normal[ 0 ] = md2_normals[frame->verts[j].lightnormalindex][0];\r
-                       normal[ 1 ] = md2_normals[frame->verts[j].lightnormalindex][1];\r
-                       normal[ 2 ] = md2_normals[frame->verts[j].lightnormalindex][2];\r
-                       PicoSetSurfaceNormal( picoSurface, i + md2->numXYZ , normal );\r
-\r
-                       /* set st coords */\r
-                       st[ 0 ] =  ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)md2->skinWidth));\r
-                       st[ 1 ] =  (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)md2->skinHeight));\r
-                       PicoSetSurfaceST( picoSurface, 0, i + md2->numXYZ , st );\r
-               }\r
-       }\r
-\r
-       /* set color */\r
-       PicoSetSurfaceColor( picoSurface, 0, 0, color );\r
-\r
-       // Free up malloc'ed LL entries\r
-       for(i=0; i<md2->numXYZ; i++)\r
-       {\r
-               if(p_index_LUT[i].next != NULL)\r
-               {\r
-                       p_index_LUT2 = p_index_LUT[i].next;\r
-                       do {\r
-                               p_index_LUT3 = p_index_LUT2->next;\r
-                               _pico_free(p_index_LUT2);\r
-                               p_index_LUT2 = p_index_LUT3;\r
-                               dups--;\r
-                       } while (p_index_LUT2 != NULL);\r
-               }\r
-       }\r
-\r
-       if (dups)\r
-               _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n");\r
-\r
-       // Free malloc'ed LUTs\r
-       _pico_free(p_index_LUT);\r
-       _pico_free(p_index_LUT_DUPS);\r
-\r
-       /* return the new pico model */\r
-       return picoModel;\r
-\r
-}\r
-\r
-\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModuleMD2 =\r
-{\r
-       "0.875",                                                /* module version string */\r
-       "Quake 2 MD2",                                  /* module display name */\r
-       "Nurail",                                               /* author's name */\r
-       "2003 Nurail",                                  /* module copyright */\r
-       {\r
-               "md2", NULL, NULL, NULL         /* default extensions to use */\r
-       },\r
-       _md2_canload,                                   /* validation routine */\r
-       _md2_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.
+
+----------------------------------------------------------------------------- */
+
+/*
+Nurail: Used pm_md3.c (Randy Reddig) as a template.
+*/
+
+
+/* marker */
+#define PM_MD2_C
+
+/* dependencies */
+#include "picointernal.h"
+
+
+/* md2 model format */
+#define MD2_MAGIC                              "IDP2"
+#define MD2_VERSION                            8
+
+#define MD2_NUMVERTEXNORMALS   162
+#define MD2_MAX_SKINNAME               64
+#define MD2_MAX_TRIANGLES              4096
+#define MD2_MAX_VERTS                  2048
+#define MD2_MAX_FRAMES                 512
+#define MD2_MAX_MD2SKINS               32
+#define MD2_MAX_SKINNAME               64
+
+#ifndef byte
+       #define byte unsigned char
+#endif
+
+typedef struct index_LUT_s
+{
+       short   Vert;
+       short   ST;
+       struct  index_LUT_s     *next;
+
+} index_LUT_t;
+
+typedef struct index_DUP_LUT_s
+{
+       short                   ST;
+       short                   OldVert;
+
+} index_DUP_LUT_t;
+
+typedef struct
+{
+        short   s;
+        short   t;
+} md2St_t;
+
+typedef struct
+{
+        short   index_xyz[3];
+        short   index_st[3];
+} md2Triangle_t;
+
+typedef struct
+{
+        byte   v[3];                   // scaled byte to fit in frame mins/maxs
+        byte   lightnormalindex;
+} md2XyzNormal_t;
+
+typedef struct md2Frame_s
+{
+        float           scale[3];       // multiply byte verts by this
+        float           translate[3];   // then add this
+        char            name[16];       // frame name from grabbing
+        md2XyzNormal_t  verts[1];       // variable sized
+}
+md2Frame_t;
+
+
+/* md2 model file md2 structure */
+typedef struct md2_s
+{
+       char    magic[ 4 ];
+       int     version;
+
+       int     skinWidth;
+       int     skinHeight;
+       int     frameSize;
+
+       int     numSkins;
+       int     numXYZ;
+       int     numST;
+       int     numTris;
+       int     numGLCmds;
+       int     numFrames;
+
+       int     ofsSkins;
+       int     ofsST;
+       int     ofsTris;
+       int     ofsFrames;
+       int     ofsGLCmds;
+       int     ofsEnd;
+}
+md2_t;
+
+float  md2_normals[ MD2_NUMVERTEXNORMALS ][ 3 ] =
+{
+       { -0.525731f, 0.000000f, 0.850651f },
+       { -0.442863f, 0.238856f, 0.864188f }, 
+       { -0.295242f, 0.000000f, 0.955423f }, 
+       { -0.309017f, 0.500000f, 0.809017f }, 
+       { -0.162460f, 0.262866f, 0.951056f }, 
+       { 0.000000f, 0.000000f, 1.000000f },
+       { 0.000000f, 0.850651f, 0.525731f },
+       { -0.147621f, 0.716567f, 0.681718f },
+       { 0.147621f, 0.716567f, 0.681718f }, 
+       { 0.000000f, 0.525731f, 0.850651f }, 
+       { 0.309017f, 0.500000f, 0.809017f }, 
+       { 0.525731f, 0.000000f, 0.850651f }, 
+       { 0.295242f, 0.000000f, 0.955423f }, 
+       { 0.442863f, 0.238856f, 0.864188f }, 
+       { 0.162460f, 0.262866f, 0.951056f }, 
+       { -0.681718f, 0.147621f, 0.716567f }, 
+       { -0.809017f, 0.309017f, 0.500000f }, 
+       { -0.587785f, 0.425325f, 0.688191f }, 
+       { -0.850651f, 0.525731f, 0.000000f }, 
+       { -0.864188f, 0.442863f, 0.238856f }, 
+       { -0.716567f, 0.681718f, 0.147621f }, 
+       { -0.688191f, 0.587785f, 0.425325f }, 
+       { -0.500000f, 0.809017f, 0.309017f }, 
+       { -0.238856f, 0.864188f, 0.442863f }, 
+       { -0.425325f, 0.688191f, 0.587785f }, 
+       { -0.716567f, 0.681718f, -0.147621f }, 
+       { -0.500000f, 0.809017f, -0.309017f }, 
+       { -0.525731f, 0.850651f, 0.000000f }, 
+       { 0.000000f, 0.850651f, -0.525731f }, 
+       { -0.238856f, 0.864188f, -0.442863f }, 
+       { 0.000000f, 0.955423f, -0.295242f }, 
+       { -0.262866f, 0.951056f, -0.162460f }, 
+       { 0.000000f, 1.000000f, 0.000000f }, 
+       { 0.000000f, 0.955423f, 0.295242f }, 
+       { -0.262866f, 0.951056f, 0.162460f }, 
+       { 0.238856f, 0.864188f, 0.442863f }, 
+       { 0.262866f, 0.951056f, 0.162460f }, 
+       { 0.500000f, 0.809017f, 0.309017f }, 
+       { 0.238856f, 0.864188f, -0.442863f }, 
+       { 0.262866f, 0.951056f, -0.162460f }, 
+       { 0.500000f, 0.809017f, -0.309017f }, 
+       { 0.850651f, 0.525731f, 0.000000f }, 
+       { 0.716567f, 0.681718f, 0.147621f }, 
+       { 0.716567f, 0.681718f, -0.147621f }, 
+       { 0.525731f, 0.850651f, 0.000000f }, 
+       { 0.425325f, 0.688191f, 0.587785f }, 
+       { 0.864188f, 0.442863f, 0.238856f }, 
+       { 0.688191f, 0.587785f, 0.425325f }, 
+       { 0.809017f, 0.309017f, 0.500000f }, 
+       { 0.681718f, 0.147621f, 0.716567f }, 
+       { 0.587785f, 0.425325f, 0.688191f }, 
+       { 0.955423f, 0.295242f, 0.000000f }, 
+       { 1.000000f, 0.000000f, 0.000000f }, 
+       { 0.951056f, 0.162460f, 0.262866f }, 
+       { 0.850651f, -0.525731f, 0.000000f }, 
+       { 0.955423f, -0.295242f, 0.000000f }, 
+       { 0.864188f, -0.442863f, 0.238856f }, 
+       { 0.951056f, -0.162460f, 0.262866f }, 
+       { 0.809017f, -0.309017f, 0.500000f }, 
+       { 0.681718f, -0.147621f, 0.716567f }, 
+       { 0.850651f, 0.000000f, 0.525731f }, 
+       { 0.864188f, 0.442863f, -0.238856f }, 
+       { 0.809017f, 0.309017f, -0.500000f }, 
+       { 0.951056f, 0.162460f, -0.262866f }, 
+       { 0.525731f, 0.000000f, -0.850651f }, 
+       { 0.681718f, 0.147621f, -0.716567f }, 
+       { 0.681718f, -0.147621f, -0.716567f }, 
+       { 0.850651f, 0.000000f, -0.525731f }, 
+       { 0.809017f, -0.309017f, -0.500000f }, 
+       { 0.864188f, -0.442863f, -0.238856f }, 
+       { 0.951056f, -0.162460f, -0.262866f }, 
+       { 0.147621f, 0.716567f, -0.681718f }, 
+       { 0.309017f, 0.500000f, -0.809017f }, 
+       { 0.425325f, 0.688191f, -0.587785f }, 
+       { 0.442863f, 0.238856f, -0.864188f }, 
+       { 0.587785f, 0.425325f, -0.688191f }, 
+       { 0.688191f, 0.587785f, -0.425325f }, 
+       { -0.147621f, 0.716567f, -0.681718f }, 
+       { -0.309017f, 0.500000f, -0.809017f }, 
+       { 0.000000f, 0.525731f, -0.850651f }, 
+       { -0.525731f, 0.000000f, -0.850651f }, 
+       { -0.442863f, 0.238856f, -0.864188f }, 
+       { -0.295242f, 0.000000f, -0.955423f }, 
+       { -0.162460f, 0.262866f, -0.951056f }, 
+       { 0.000000f, 0.000000f, -1.000000f }, 
+       { 0.295242f, 0.000000f, -0.955423f }, 
+       { 0.162460f, 0.262866f, -0.951056f }, 
+       { -0.442863f, -0.238856f, -0.864188f }, 
+       { -0.309017f, -0.500000f, -0.809017f }, 
+       { -0.162460f, -0.262866f, -0.951056f }, 
+       { 0.000000f, -0.850651f, -0.525731f }, 
+       { -0.147621f, -0.716567f, -0.681718f }, 
+       { 0.147621f, -0.716567f, -0.681718f }, 
+       { 0.000000f, -0.525731f, -0.850651f }, 
+       { 0.309017f, -0.500000f, -0.809017f }, 
+       { 0.442863f, -0.238856f, -0.864188f }, 
+       { 0.162460f, -0.262866f, -0.951056f }, 
+       { 0.238856f, -0.864188f, -0.442863f }, 
+       { 0.500000f, -0.809017f, -0.309017f }, 
+       { 0.425325f, -0.688191f, -0.587785f }, 
+       { 0.716567f, -0.681718f, -0.147621f }, 
+       { 0.688191f, -0.587785f, -0.425325f }, 
+       { 0.587785f, -0.425325f, -0.688191f }, 
+       { 0.000000f, -0.955423f, -0.295242f }, 
+       { 0.000000f, -1.000000f, 0.000000f }, 
+       { 0.262866f, -0.951056f, -0.162460f }, 
+       { 0.000000f, -0.850651f, 0.525731f }, 
+       { 0.000000f, -0.955423f, 0.295242f }, 
+       { 0.238856f, -0.864188f, 0.442863f }, 
+       { 0.262866f, -0.951056f, 0.162460f }, 
+       { 0.500000f, -0.809017f, 0.309017f }, 
+       { 0.716567f, -0.681718f, 0.147621f }, 
+       { 0.525731f, -0.850651f, 0.000000f }, 
+       { -0.238856f, -0.864188f, -0.442863f }, 
+       { -0.500000f, -0.809017f, -0.309017f }, 
+       { -0.262866f, -0.951056f, -0.162460f }, 
+       { -0.850651f, -0.525731f, 0.000000f }, 
+       { -0.716567f, -0.681718f, -0.147621f }, 
+       { -0.716567f, -0.681718f, 0.147621f }, 
+       { -0.525731f, -0.850651f, 0.000000f }, 
+       { -0.500000f, -0.809017f, 0.309017f }, 
+       { -0.238856f, -0.864188f, 0.442863f }, 
+       { -0.262866f, -0.951056f, 0.162460f }, 
+       { -0.864188f, -0.442863f, 0.238856f }, 
+       { -0.809017f, -0.309017f, 0.500000f }, 
+       { -0.688191f, -0.587785f, 0.425325f }, 
+       { -0.681718f, -0.147621f, 0.716567f }, 
+       { -0.442863f, -0.238856f, 0.864188f }, 
+       { -0.587785f, -0.425325f, 0.688191f }, 
+       { -0.309017f, -0.500000f, 0.809017f }, 
+       { -0.147621f, -0.716567f, 0.681718f }, 
+       { -0.425325f, -0.688191f, 0.587785f }, 
+       { -0.162460f, -0.262866f, 0.951056f }, 
+       { 0.442863f, -0.238856f, 0.864188f }, 
+       { 0.162460f, -0.262866f, 0.951056f }, 
+       { 0.309017f, -0.500000f, 0.809017f }, 
+       { 0.147621f, -0.716567f, 0.681718f }, 
+       { 0.000000f, -0.525731f, 0.850651f }, 
+       { 0.425325f, -0.688191f, 0.587785f }, 
+       { 0.587785f, -0.425325f, 0.688191f }, 
+       { 0.688191f, -0.587785f, 0.425325f }, 
+       { -0.955423f, 0.295242f, 0.000000f }, 
+       { -0.951056f, 0.162460f, 0.262866f }, 
+       { -1.000000f, 0.000000f, 0.000000f }, 
+       { -0.850651f, 0.000000f, 0.525731f }, 
+       { -0.955423f, -0.295242f, 0.000000f }, 
+       { -0.951056f, -0.162460f, 0.262866f }, 
+       { -0.864188f, 0.442863f, -0.238856f }, 
+       { -0.951056f, 0.162460f, -0.262866f }, 
+       { -0.809017f, 0.309017f, -0.500000f }, 
+       { -0.864188f, -0.442863f, -0.238856f },
+       { -0.951056f, -0.162460f, -0.262866f }, 
+       { -0.809017f, -0.309017f, -0.500000f }, 
+       { -0.681718f, 0.147621f, -0.716567f }, 
+       { -0.681718f, -0.147621f, -0.716567f }, 
+       { -0.850651f, 0.000000f, -0.525731f }, 
+       { -0.688191f, 0.587785f, -0.425325f }, 
+       { -0.587785f, 0.425325f, -0.688191f }, 
+       { -0.425325f, 0.688191f, -0.587785f }, 
+       { -0.425325f, -0.688191f, -0.587785f }, 
+       { -0.587785f, -0.425325f, -0.688191f }, 
+       { -0.688191f, -0.587785f, -0.425325f }, 
+};
+
+
+// _md2_canload()
+
+static int _md2_canload( PM_PARAMS_CANLOAD )
+{
+       md2_t   *md2;
+
+       /* to keep the compiler happy */
+       *fileName = *fileName;
+
+       /* sanity check */
+       if( bufSize < ( sizeof( *md2 ) * 2) )
+               return PICO_PMV_ERROR_SIZE;
+       
+       /* set as md2 */
+       md2 = (md2_t*) buffer;
+       
+       /* check md2 magic */
+       if( *((int*) md2->magic) != *((int*) MD2_MAGIC) ) 
+               return PICO_PMV_ERROR_IDENT;
+       
+       /* check md2 version */
+       if( _pico_little_long( md2->version ) != MD2_VERSION )
+               return PICO_PMV_ERROR_VERSION;
+
+       /* file seems to be a valid md2 */
+       return PICO_PMV_OK;
+}
+
+
+
+// _md2_load() loads a quake2 md2 model file.
+
+
+static picoModel_t *_md2_load( PM_PARAMS_LOAD )
+{
+       int                             i, j, dups, dup_index;
+       short                   tot_numVerts;
+       index_LUT_t             *p_index_LUT, *p_index_LUT2, *p_index_LUT3;
+       index_DUP_LUT_t *p_index_LUT_DUPS;
+       md2Triangle_t   *p_md2Triangle;
+
+       char                    skinname[ MD2_MAX_SKINNAME ];
+       md2_t                   *md2;
+       md2St_t                 *texCoord;
+       md2Frame_t              *frame;
+       md2Triangle_t   *triangle;
+       md2XyzNormal_t  *vertex;
+
+       picoByte_t      *bb;
+       picoModel_t             *picoModel;
+       picoSurface_t   *picoSurface;
+       picoShader_t    *picoShader;
+       picoVec3_t              xyz, normal;
+       picoVec2_t              st;
+       picoColor_t             color;
+       
+
+       // md2 loading
+       _pico_printf( PICO_NORMAL, "Loading \"%s\"", fileName );
+
+       /* set as md2 */
+       bb = (picoByte_t*) buffer;
+       md2     = (md2_t*) buffer;
+
+       /* check ident and version */
+       if( *((int*) md2->magic) != *((int*) MD2_MAGIC) || _pico_little_long( md2->version ) != MD2_VERSION )
+       {
+               /* not an md2 file (todo: set error) */
+               _pico_printf( PICO_ERROR, "%s is not an MD2 File!", fileName );
+               return NULL;
+       }
+       
+       // swap md2
+       md2->version = _pico_little_long( md2->version );
+
+       md2->skinWidth = _pico_little_long( md2->skinWidth );
+       md2->skinHeight = _pico_little_long( md2->skinHeight );
+       md2->frameSize = _pico_little_long( md2->frameSize );
+
+       md2->numSkins = _pico_little_long( md2->numSkins );
+       md2->numXYZ = _pico_little_long( md2->numXYZ );
+       md2->numST = _pico_little_long( md2->numST );
+       md2->numTris = _pico_little_long( md2->numTris );
+       md2->numGLCmds = _pico_little_long( md2->numGLCmds );
+       md2->numFrames = _pico_little_long( md2->numFrames );
+
+       md2->ofsSkins = _pico_little_long( md2->ofsSkins );
+       md2->ofsST = _pico_little_long( md2->ofsST );
+       md2->ofsTris = _pico_little_long( md2->ofsTris );
+       md2->ofsFrames = _pico_little_long( md2->ofsFrames );
+       md2->ofsGLCmds = _pico_little_long( md2->ofsGLCmds );
+       md2->ofsEnd = _pico_little_long( md2->ofsEnd );
+
+       // do frame check
+       if( md2->numFrames < 1 )
+       {
+               _pico_printf( PICO_ERROR, "%s has 0 frames!", fileName );
+               return NULL;
+       }
+       
+       if( frameNum < 0 || frameNum >= md2->numFrames )
+       {
+               _pico_printf( PICO_ERROR, "Invalid or out-of-range MD2 frame specified" );
+               return NULL;
+       }
+
+       // Setup Frame
+       frame = (md2Frame_t *) (bb + md2->ofsFrames + (sizeof(md2Frame_t) * frameNum));
+
+       // swap frame scale and translation
+       for( i = 0; i < 3; i++ )
+       {
+               frame->scale[ i ] = _pico_little_float( frame->scale[ i ] );
+               frame->translate[ i ] = _pico_little_float( frame->translate[ i ] );
+       }
+
+       // swap triangles
+       triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) );
+       for( i = 0; i < md2->numTris; i++, triangle++ )
+       {
+               for( j = 0; j < 3; j++ )
+               {
+                       triangle->index_xyz[ j ] = _pico_little_short( triangle->index_xyz[ j ] );
+                       triangle->index_st[ j ] = _pico_little_short( triangle->index_st[ j ] );
+               }
+       }
+
+       // swap st coords
+       texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) );
+       for( i = 0; i < md2->numST; i++, texCoord++ )
+       {
+               texCoord->s = _pico_little_short( texCoord->s );
+               texCoord->t = _pico_little_short( texCoord->t );
+       }
+
+       // set Skin Name
+       strncpy(skinname, (bb + md2->ofsSkins), MD2_MAX_SKINNAME );
+
+       // Print out md2 values
+       _pico_printf(PICO_VERBOSE,"Skins: %d  Verts: %d  STs: %d  Triangles: %d  Frames: %d\nSkin Name \"%s\"\n", md2->numSkins, md2->numXYZ, md2->numST, md2->numTris, md2->numFrames, &skinname );
+
+       // detox Skin name
+       _pico_setfext( skinname, "" );
+       _pico_unixify( skinname );
+
+       /* create new pico model */
+       picoModel = PicoNewModel();
+       if( picoModel == NULL )
+       {
+               _pico_printf( PICO_ERROR, "Unable to allocate a new model" );
+               return NULL;
+       }
+
+       /* do model setup */
+       PicoSetModelFrameNum( picoModel, frameNum );
+       PicoSetModelNumFrames( picoModel, md2->numFrames ); /* sea */
+       PicoSetModelName( picoModel, fileName );
+       PicoSetModelFileName( picoModel, fileName );
+
+       // allocate new pico surface
+       picoSurface = PicoNewSurface( picoModel );
+       if( picoSurface == NULL )
+       {
+               _pico_printf( PICO_ERROR, "Unable to allocate a new model surface" );
+               PicoFreeModel( picoModel );
+               return NULL;
+       }
+
+
+       PicoSetSurfaceType( picoSurface, PICO_TRIANGLES );
+       PicoSetSurfaceName( picoSurface, frame->name );
+       picoShader = PicoNewShader( picoModel );
+       if( picoShader == NULL )
+       {
+               _pico_printf( PICO_ERROR, "Unable to allocate a new model shader" );
+               PicoFreeModel( picoModel );
+               return NULL;
+       }
+
+       PicoSetShaderName( picoShader, skinname );
+
+       // associate current surface with newly created shader
+       PicoSetSurfaceShader( picoSurface, picoShader );
+
+       // Init LUT for Verts
+       p_index_LUT = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t) * md2->numXYZ);
+       for(i=0; i<md2->numXYZ; i++)
+       {
+               p_index_LUT[i].Vert = -1;
+               p_index_LUT[i].ST = -1;
+               p_index_LUT[i].next = NULL;
+       }
+
+       // Fill in Look Up Table, and allocate/fill Linked List from vert array as needed for dup STs per Vert.
+       tot_numVerts = md2->numXYZ;
+       dups = 0;
+       for(i=0; i<md2->numTris; i++)
+       {
+               p_md2Triangle = (md2Triangle_t *) ( bb + md2->ofsTris + (sizeof(md2Triangle_t)*i));
+               for(j=0; j<3; j++)
+               {
+                       if (p_index_LUT[p_md2Triangle->index_xyz[j]].ST == -1) // No Main Entry
+                               p_index_LUT[p_md2Triangle->index_xyz[j]].ST = p_md2Triangle->index_st[j];
+
+                       else if (p_md2Triangle->index_st[j] == p_index_LUT[p_md2Triangle->index_xyz[j]].ST ) // Equal to Main Entry
+                               continue;
+
+                       else if ( (p_index_LUT[p_md2Triangle->index_xyz[j]].next == NULL) )  // Not equal to Main entry, and no LL entry
+                       {       // Add first entry of LL from Main
+                               p_index_LUT2 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
+                               if (p_index_LUT2 == NULL)
+                                       _pico_printf( PICO_ERROR," Couldn't allocate memory!\n");
+                               p_index_LUT[p_md2Triangle->index_xyz[j]].next = (index_LUT_t *)p_index_LUT2;
+                               p_index_LUT2->Vert = dups;
+                               p_index_LUT2->ST = p_md2Triangle->index_st[j];
+                               p_index_LUT2->next = NULL;
+                               p_md2Triangle->index_xyz[j] = dups + md2->numXYZ; // Make change in Tri hunk
+                               dups++;
+                       }
+                       else // Try to find in LL from Main Entry
+                       {
+                               p_index_LUT3 = p_index_LUT2 = p_index_LUT[p_md2Triangle->index_xyz[j]].next;
+                               while ( (p_index_LUT2 != NULL) && (p_md2Triangle->index_xyz[j] != p_index_LUT2->Vert) ) // Walk down LL
+                               {
+                                       p_index_LUT3 = p_index_LUT2;
+                                       p_index_LUT2 = p_index_LUT2->next;
+                               }
+                               p_index_LUT2 = p_index_LUT3;
+
+                               if ( p_md2Triangle->index_st[j] == p_index_LUT2->ST ) // Found it
+                               {
+                                       p_md2Triangle->index_xyz[j] = p_index_LUT2->Vert + md2->numXYZ; // Make change in Tri hunk
+                                       continue;
+                               }
+
+                               if ( p_index_LUT2->next == NULL)  // Didn't find it. Add entry to LL.
+                               {
+                                       // Add the Entry
+                                       p_index_LUT3 = (index_LUT_t *)_pico_alloc(sizeof(index_LUT_t));
+                                       if (p_index_LUT3 == NULL)
+                                               _pico_printf( PICO_ERROR," Couldn't allocate memory!\n");
+                                       p_index_LUT2->next = (index_LUT_t *)p_index_LUT3;
+                                       p_index_LUT3->Vert = p_md2Triangle->index_xyz[j];
+                                       p_index_LUT3->ST = p_md2Triangle->index_st[j];
+                                       p_index_LUT3->next = NULL;
+                                       p_md2Triangle->index_xyz[j] = dups + md2->numXYZ; // Make change in Tri hunk
+                                       dups++;
+                               }
+                       }
+               }
+       }
+
+       // malloc and build array for Dup STs
+       p_index_LUT_DUPS = (index_DUP_LUT_t *)_pico_alloc(sizeof(index_DUP_LUT_t) * dups);
+       if (p_index_LUT_DUPS == NULL)
+               _pico_printf( PICO_ERROR," Couldn't allocate memory!\n");
+
+       dup_index = 0;
+       for(i=0; i<md2->numXYZ; i++)
+       {
+               p_index_LUT2 = p_index_LUT[i].next;
+               while (p_index_LUT2 != NULL)
+               {
+                       p_index_LUT_DUPS[p_index_LUT2->Vert].OldVert = i;
+                       p_index_LUT_DUPS[p_index_LUT2->Vert].ST = p_index_LUT2->ST;
+                       dup_index++;
+                       p_index_LUT2 = p_index_LUT2->next;
+               }
+       }
+
+       // Build Picomodel
+       triangle = (md2Triangle_t *) ((picoByte_t *) (bb + md2->ofsTris) );
+       texCoord = (md2St_t*) ((picoByte_t *) (bb + md2->ofsST) );
+       vertex = (md2XyzNormal_t*) ((picoByte_t*) (frame->verts) );
+       for( j = 0; j < md2->numTris; j++, triangle++ )
+       {
+               PicoSetSurfaceIndex( picoSurface, j*3   , triangle->index_xyz[0] );
+               PicoSetSurfaceIndex( picoSurface, j*3+1 , triangle->index_xyz[1] );
+               PicoSetSurfaceIndex( picoSurface, j*3+2 , triangle->index_xyz[2] );
+       }
+
+       for(i=0; i< md2->numXYZ; i++, vertex++)
+       {
+               /* set vertex origin */
+               xyz[ 0 ] = vertex->v[0] * frame->scale[0] + frame->translate[0];
+               xyz[ 1 ] = vertex->v[1] * frame->scale[1] + frame->translate[1];
+               xyz[ 2 ] = vertex->v[2] * frame->scale[2] + frame->translate[2];
+               PicoSetSurfaceXYZ( picoSurface, i , xyz );
+
+               /* set normal */
+               normal[ 0 ] = md2_normals[vertex->lightnormalindex][0];
+               normal[ 1 ] = md2_normals[vertex->lightnormalindex][1];
+               normal[ 2 ] = md2_normals[vertex->lightnormalindex][2];
+               PicoSetSurfaceNormal( picoSurface, i , normal );
+
+               /* set st coords */
+               st[ 0 ] =  ((texCoord[p_index_LUT[i].ST].s) / ((float)md2->skinWidth));
+               st[ 1 ] =  (texCoord[p_index_LUT[i].ST].t / ((float)md2->skinHeight));
+               PicoSetSurfaceST( picoSurface, 0, i , st );
+       }
+
+       if (dups)
+       {
+               for(i=0; i<dups; i++)
+               {
+                       j = p_index_LUT_DUPS[i].OldVert;
+                       /* set vertex origin */
+                       xyz[ 0 ] = frame->verts[j].v[0] * frame->scale[0] + frame->translate[0];
+                       xyz[ 1 ] = frame->verts[j].v[1] * frame->scale[1] + frame->translate[1];
+                       xyz[ 2 ] = frame->verts[j].v[2] * frame->scale[2] + frame->translate[2];
+                       PicoSetSurfaceXYZ( picoSurface, i + md2->numXYZ , xyz );
+
+                       /* set normal */
+                       normal[ 0 ] = md2_normals[frame->verts[j].lightnormalindex][0];
+                       normal[ 1 ] = md2_normals[frame->verts[j].lightnormalindex][1];
+                       normal[ 2 ] = md2_normals[frame->verts[j].lightnormalindex][2];
+                       PicoSetSurfaceNormal( picoSurface, i + md2->numXYZ , normal );
+
+                       /* set st coords */
+                       st[ 0 ] =  ((texCoord[p_index_LUT_DUPS[i].ST].s) / ((float)md2->skinWidth));
+                       st[ 1 ] =  (texCoord[p_index_LUT_DUPS[i].ST].t / ((float)md2->skinHeight));
+                       PicoSetSurfaceST( picoSurface, 0, i + md2->numXYZ , st );
+               }
+       }
+
+       /* set color */
+       PicoSetSurfaceColor( picoSurface, 0, 0, color );
+
+       // Free up malloc'ed LL entries
+       for(i=0; i<md2->numXYZ; i++)
+       {
+               if(p_index_LUT[i].next != NULL)
+               {
+                       p_index_LUT2 = p_index_LUT[i].next;
+                       do {
+                               p_index_LUT3 = p_index_LUT2->next;
+                               _pico_free(p_index_LUT2);
+                               p_index_LUT2 = p_index_LUT3;
+                               dups--;
+                       } while (p_index_LUT2 != NULL);
+               }
+       }
+
+       if (dups)
+               _pico_printf(PICO_WARNING, " Not all LL mallocs freed\n");
+
+       // Free malloc'ed LUTs
+       _pico_free(p_index_LUT);
+       _pico_free(p_index_LUT_DUPS);
+
+       /* return the new pico model */
+       return picoModel;
+
+}
+
+
+
+/* pico file format module definition */
+const picoModule_t picoModuleMD2 =
+{
+       "0.875",                                                /* module version string */
+       "Quake 2 MD2",                                  /* module display name */
+       "Nurail",                                               /* author's name */
+       "2003 Nurail",                                  /* module copyright */
+       {
+               "md2", NULL, NULL, NULL         /* default extensions to use */
+       },
+       _md2_canload,                                   /* validation routine */
+       _md2_load,                                              /* load routine */
+        NULL,                                                  /* save validation routine */
+        NULL                                                   /* save routine */
+};