]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/lwo/lwob.c
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / libs / picomodel / lwo / lwob.c
index 0e386a3017371e076a1790541141b9e3879fa56b..3ae40b43e66974cdaf5a81f39ae267359fb519d9 100644 (file)
-/*\r
-======================================================================\r
-lwob.c\r
-\r
-Functions for an LWOB reader.  LWOB is the LightWave object format\r
-for versions of LW prior to 6.0.\r
-\r
-Ernie Wright  17 Sep 00\r
-====================================================================== */\r
-\r
-#include "../picointernal.h"\r
-#include "lwo2.h"\r
-\r
-/* disable warnings */\r
-#ifdef _WIN32\r
-#pragma warning( disable:4018 )                /* signed/unsigned mismatch */\r
-#endif\r
-\r
-\r
-/* IDs specific to LWOB */\r
-\r
-#define ID_SRFS  LWID_('S','R','F','S')\r
-#define ID_FLAG  LWID_('F','L','A','G')\r
-#define ID_VLUM  LWID_('V','L','U','M')\r
-#define ID_VDIF  LWID_('V','D','I','F')\r
-#define ID_VSPC  LWID_('V','S','P','C')\r
-#define ID_RFLT  LWID_('R','F','L','T')\r
-#define ID_BTEX  LWID_('B','T','E','X')\r
-#define ID_CTEX  LWID_('C','T','E','X')\r
-#define ID_DTEX  LWID_('D','T','E','X')\r
-#define ID_LTEX  LWID_('L','T','E','X')\r
-#define ID_RTEX  LWID_('R','T','E','X')\r
-#define ID_STEX  LWID_('S','T','E','X')\r
-#define ID_TTEX  LWID_('T','T','E','X')\r
-#define ID_TFLG  LWID_('T','F','L','G')\r
-#define ID_TSIZ  LWID_('T','S','I','Z')\r
-#define ID_TCTR  LWID_('T','C','T','R')\r
-#define ID_TFAL  LWID_('T','F','A','L')\r
-#define ID_TVEL  LWID_('T','V','E','L')\r
-#define ID_TCLR  LWID_('T','C','L','R')\r
-#define ID_TVAL  LWID_('T','V','A','L')\r
-#define ID_TAMP  LWID_('T','A','M','P')\r
-#define ID_TIMG  LWID_('T','I','M','G')\r
-#define ID_TAAS  LWID_('T','A','A','S')\r
-#define ID_TREF  LWID_('T','R','E','F')\r
-#define ID_TOPC  LWID_('T','O','P','C')\r
-#define ID_SDAT  LWID_('S','D','A','T')\r
-#define ID_TFP0  LWID_('T','F','P','0')\r
-#define ID_TFP1  LWID_('T','F','P','1')\r
-\r
-\r
-/*\r
-======================================================================\r
-add_clip()\r
-\r
-Add a clip to the clip list.  Used to store the contents of an RIMG or\r
-TIMG surface subchunk.\r
-====================================================================== */\r
-\r
-static int add_clip( char *s, lwClip **clist, int *nclips )\r
-{\r
-   lwClip *clip;\r
-   char *p;\r
-\r
-   clip = _pico_calloc( 1, sizeof( lwClip ));\r
-   if ( !clip ) return 0;\r
-\r
-   clip->contrast.val = 1.0f;\r
-   clip->brightness.val = 1.0f;\r
-   clip->saturation.val = 1.0f;\r
-   clip->gamma.val = 1.0f;\r
-\r
-   if ( p = strstr( s, "(sequence)" )) {\r
-      p[ -1 ] = 0;\r
-      clip->type = ID_ISEQ;\r
-      clip->source.seq.prefix = s;\r
-      clip->source.seq.digits = 3;\r
-   }\r
-   else {\r
-      clip->type = ID_STIL;\r
-      clip->source.still.name = s;\r
-   }\r
-\r
-   *nclips++;\r
-   clip->index = *nclips;\r
-\r
-   lwListAdd( clist, clip );\r
-\r
-   return clip->index;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-add_tvel()\r
-\r
-Add a triple of envelopes to simulate the old texture velocity\r
-parameters.\r
-====================================================================== */\r
-\r
-static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs )\r
-{\r
-   lwEnvelope *env;\r
-   lwKey *key0, *key1;\r
-   int i;\r
-\r
-   for ( i = 0; i < 3; i++ ) {\r
-      env = _pico_calloc( 1, sizeof( lwEnvelope ));\r
-      key0 = _pico_calloc( 1, sizeof( lwKey ));\r
-      key1 = _pico_calloc( 1, sizeof( lwKey ));\r
-      if ( !env || !key0 || !key1 ) return 0;\r
-\r
-      key0->next = key1;\r
-      key0->value = pos[ i ];\r
-      key0->time = 0.0f;\r
-      key1->prev = key0;\r
-      key1->value = pos[ i ] + vel[ i ] * 30.0f;\r
-      key1->time = 1.0f;\r
-      key0->shape = key1->shape = ID_LINE;\r
-\r
-      env->index = *nenvs + i + 1;\r
-      env->type = 0x0301 + i;\r
-      env->name = _pico_alloc( 11 );\r
-      if ( env->name ) {\r
-         strcpy( env->name, "Position.X" );\r
-         env->name[ 9 ] += i;\r
-      }\r
-      env->key = key0;\r
-      env->nkeys = 2;\r
-      env->behavior[ 0 ] = BEH_LINEAR;\r
-      env->behavior[ 1 ] = BEH_LINEAR;\r
-\r
-      lwListAdd( elist, env );\r
-   }\r
-\r
-   *nenvs += 3;\r
-   return env->index - 2;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-get_texture()\r
-\r
-Create a new texture for BTEX, CTEX, etc. subchunks.\r
-====================================================================== */\r
-\r
-static lwTexture *get_texture( char *s )\r
-{\r
-   lwTexture *tex;\r
-\r
-   tex = _pico_calloc( 1, sizeof( lwTexture ));\r
-   if ( !tex ) return NULL;\r
-\r
-   tex->tmap.size.val[ 0 ] =\r
-   tex->tmap.size.val[ 1 ] =\r
-   tex->tmap.size.val[ 2 ] = 1.0f;\r
-   tex->opacity.val = 1.0f;\r
-   tex->enabled = 1;\r
-\r
-   if ( strstr( s, "Image Map" )) {\r
-      tex->type = ID_IMAP;\r
-      if ( strstr( s, "Planar" ))           tex->param.imap.projection = 0;\r
-      else if ( strstr( s, "Cylindrical" )) tex->param.imap.projection = 1;\r
-      else if ( strstr( s, "Spherical" ))   tex->param.imap.projection = 2;\r
-      else if ( strstr( s, "Cubic" ))       tex->param.imap.projection = 3;\r
-      else if ( strstr( s, "Front" ))       tex->param.imap.projection = 4;\r
-      tex->param.imap.aa_strength = 1.0f;\r
-      tex->param.imap.amplitude.val = 1.0f;\r
-      _pico_free( s );\r
-   }\r
-   else {\r
-      tex->type = ID_PROC;\r
-      tex->param.proc.name = s;\r
-   }\r
-\r
-   return tex;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-lwGetSurface5()\r
-\r
-Read an lwSurface from an LWOB file.\r
-====================================================================== */\r
-\r
-lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj )\r
-{\r
-   lwSurface *surf;\r
-   lwTexture *tex;\r
-   lwPlugin *shdr;\r
-   char *s;\r
-   float v[ 3 ];\r
-   unsigned int id, flags;\r
-   unsigned short sz;\r
-   int pos, rlen, i;\r
-\r
-\r
-   /* allocate the Surface structure */\r
-\r
-   surf = _pico_calloc( 1, sizeof( lwSurface ));\r
-   if ( !surf ) goto Fail;\r
-\r
-   /* non-zero defaults */\r
-\r
-   surf->color.rgb[ 0 ] = 0.78431f;\r
-   surf->color.rgb[ 1 ] = 0.78431f;\r
-   surf->color.rgb[ 2 ] = 0.78431f;\r
-   surf->diffuse.val    = 1.0f;\r
-   surf->glossiness.val = 0.4f;\r
-   surf->bump.val       = 1.0f;\r
-   surf->eta.val        = 1.0f;\r
-   surf->sideflags      = 1;\r
-\r
-   /* remember where we started */\r
-\r
-   set_flen( 0 );\r
-   pos = _pico_memstream_tell( fp );\r
-\r
-   /* name */\r
-\r
-   surf->name = getS0( fp );\r
-\r
-   /* first subchunk header */\r
-\r
-   id = getU4( fp );\r
-   sz = getU2( fp );\r
-   if ( 0 > get_flen() ) goto Fail;\r
-\r
-   /* process subchunks as they're encountered */\r
-\r
-   while ( 1 ) {\r
-      sz += sz & 1;\r
-      set_flen( 0 );\r
-\r
-      switch ( id ) {\r
-         case ID_COLR:\r
-            surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f;\r
-            surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f;\r
-            surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f;\r
-            break;\r
-\r
-         case ID_FLAG:\r
-            flags = getU2( fp );\r
-            if ( flags &   4 ) surf->smooth = 1.56207f;\r
-            if ( flags &   8 ) surf->color_hilite.val = 1.0f;\r
-            if ( flags &  16 ) surf->color_filter.val = 1.0f;\r
-            if ( flags & 128 ) surf->dif_sharp.val = 0.5f;\r
-            if ( flags & 256 ) surf->sideflags = 3;\r
-            if ( flags & 512 ) surf->add_trans.val = 1.0f;\r
-            break;\r
-\r
-         case ID_LUMI:\r
-            surf->luminosity.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_VLUM:\r
-            surf->luminosity.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_DIFF:\r
-            surf->diffuse.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_VDIF:\r
-            surf->diffuse.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_SPEC:\r
-            surf->specularity.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_VSPC:\r
-            surf->specularity.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_GLOS:\r
-            surf->glossiness.val = ( float ) log( getU2( fp )) / 20.7944f;\r
-            break;\r
-\r
-         case ID_SMAN:\r
-            surf->smooth = getF4( fp );\r
-            break;\r
-\r
-         case ID_REFL:\r
-            surf->reflection.val.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_RFLT:\r
-            surf->reflection.options = getU2( fp );\r
-            break;\r
-\r
-         case ID_RIMG:\r
-            s = getS0( fp );\r
-            surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips );\r
-            surf->reflection.options = 3;\r
-            break;\r
-\r
-         case ID_RSAN:\r
-            surf->reflection.seam_angle = getF4( fp );\r
-            break;\r
-\r
-         case ID_TRAN:\r
-            surf->transparency.val.val = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_RIND:\r
-            surf->eta.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_BTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->bump.tex, tex );\r
-            break;\r
-\r
-         case ID_CTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->color.tex, tex );\r
-            break;\r
-\r
-         case ID_DTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->diffuse.tex, tex );\r
-            break;\r
-\r
-         case ID_LTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->luminosity.tex, tex );\r
-            break;\r
-\r
-         case ID_RTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->reflection.val.tex, tex );\r
-            break;\r
-\r
-         case ID_STEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->specularity.tex, tex );\r
-            break;\r
-\r
-         case ID_TTEX:\r
-            s = getbytes( fp, sz );\r
-            tex = get_texture( s );\r
-            lwListAdd( &surf->transparency.val.tex, tex );\r
-            break;\r
-\r
-         case ID_TFLG:\r
-            flags = getU2( fp );\r
-\r
-            if ( flags & 1 ) i = 0;\r
-            if ( flags & 2 ) i = 1;\r
-            if ( flags & 4 ) i = 2;\r
-            tex->axis = i;\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.axis = i;\r
-            else\r
-               tex->param.proc.axis = i;\r
-\r
-            if ( flags &  8 ) tex->tmap.coord_sys = 1;\r
-            if ( flags & 16 ) tex->negative = 1;\r
-            if ( flags & 32 ) tex->param.imap.pblend = 1;\r
-            if ( flags & 64 ) {\r
-               tex->param.imap.aa_strength = 1.0f;\r
-               tex->param.imap.aas_flags = 1;\r
-            }\r
-            break;\r
-\r
-         case ID_TSIZ:\r
-            for ( i = 0; i < 3; i++ )\r
-               tex->tmap.size.val[ i ] = getF4( fp );\r
-            break;\r
-\r
-         case ID_TCTR:\r
-            for ( i = 0; i < 3; i++ )\r
-               tex->tmap.center.val[ i ] = getF4( fp );\r
-            break;\r
-\r
-         case ID_TFAL:\r
-            for ( i = 0; i < 3; i++ )\r
-               tex->tmap.falloff.val[ i ] = getF4( fp );\r
-            break;\r
-\r
-         case ID_TVEL:\r
-            for ( i = 0; i < 3; i++ )\r
-               v[ i ] = getF4( fp );\r
-            tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,\r
-               &obj->env, &obj->nenvs );\r
-            break;\r
-\r
-         case ID_TCLR:\r
-            if ( tex->type == ID_PROC )\r
-               for ( i = 0; i < 3; i++ )\r
-                  tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;\r
-            break;\r
-\r
-         case ID_TVAL:\r
-            tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;\r
-            break;\r
-\r
-         case ID_TAMP:\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.amplitude.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_TIMG:\r
-            s = getS0( fp );\r
-            tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );\r
-            break;\r
-\r
-         case ID_TAAS:\r
-            tex->param.imap.aa_strength = getF4( fp );\r
-            tex->param.imap.aas_flags = 1;\r
-            break;\r
-\r
-         case ID_TREF:\r
-            tex->tmap.ref_object = getbytes( fp, sz );\r
-            break;\r
-\r
-         case ID_TOPC:\r
-            tex->opacity.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_TFP0:\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.wrapw.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_TFP1:\r
-            if ( tex->type == ID_IMAP )\r
-               tex->param.imap.wraph.val = getF4( fp );\r
-            break;\r
-\r
-         case ID_SHDR:\r
-            shdr = _pico_calloc( 1, sizeof( lwPlugin ));\r
-            if ( !shdr ) goto Fail;\r
-            shdr->name = getbytes( fp, sz );\r
-            lwListAdd( &surf->shader, shdr );\r
-            surf->nshaders++;\r
-            break;\r
-\r
-         case ID_SDAT:\r
-            shdr->data = getbytes( fp, sz );\r
-            break;\r
-\r
-         default:\r
-            break;\r
-      }\r
-\r
-      /* error while reading current subchunk? */\r
-\r
-      rlen = get_flen();\r
-      if ( rlen < 0 || rlen > sz ) goto Fail;\r
-\r
-      /* skip unread parts of the current subchunk */\r
-\r
-      if ( rlen < sz )\r
-         _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );\r
-\r
-      /* end of the SURF chunk? */\r
-\r
-      if ( cksize <= _pico_memstream_tell( fp ) - pos )\r
-         break;\r
-\r
-      /* get the next subchunk header */\r
-\r
-      set_flen( 0 );\r
-      id = getU4( fp );\r
-      sz = getU2( fp );\r
-      if ( 6 != get_flen() ) goto Fail;\r
-   }\r
-\r
-   return surf;\r
-\r
-Fail:\r
-   if ( surf ) lwFreeSurface( surf );\r
-   return NULL;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-lwGetPolygons5()\r
-\r
-Read polygon records from a POLS chunk in an LWOB file.  The polygons\r
-are added to the array in the lwPolygonList.\r
-====================================================================== */\r
-\r
-int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset )\r
-{\r
-   lwPolygon *pp;\r
-   lwPolVert *pv;\r
-   unsigned char *buf, *bp;\r
-   int i, j, nv, nverts, npols;\r
-\r
-\r
-   if ( cksize == 0 ) return 1;\r
-\r
-   /* read the whole chunk */\r
-\r
-   set_flen( 0 );\r
-   buf = getbytes( fp, cksize );\r
-   if ( !buf ) goto Fail;\r
-\r
-   /* count the polygons and vertices */\r
-\r
-   nverts = 0;\r
-   npols = 0;\r
-   bp = buf;\r
-\r
-   while ( bp < buf + cksize ) {\r
-      nv = sgetU2( &bp );\r
-      nverts += nv;\r
-      npols++;\r
-      bp += 2 * nv;\r
-      i = sgetI2( &bp );\r
-      if ( i < 0 ) bp += 2;      /* detail polygons */\r
-   }\r
-\r
-   if ( !lwAllocPolygons( plist, npols, nverts ))\r
-      goto Fail;\r
-\r
-   /* fill in the new polygons */\r
-\r
-   bp = buf;\r
-   pp = plist->pol + plist->offset;\r
-   pv = plist->pol[ 0 ].v + plist->voffset;\r
-\r
-   for ( i = 0; i < npols; i++ ) {\r
-      nv = sgetU2( &bp );\r
-\r
-      pp->nverts = nv;\r
-      pp->type = ID_FACE;\r
-      if ( !pp->v ) pp->v = pv;\r
-      for ( j = 0; j < nv; j++ )\r
-         pv[ j ].index = sgetU2( &bp ) + ptoffset;\r
-      j = sgetI2( &bp );\r
-      if ( j < 0 ) {\r
-         j = -j;\r
-         bp += 2;\r
-      }\r
-      j -= 1;\r
-      pp->surf = ( lwSurface * ) j;\r
-\r
-      pp++;\r
-      pv += nv;\r
-   }\r
-\r
-   _pico_free( buf );\r
-   return 1;\r
-\r
-Fail:\r
-   if ( buf ) _pico_free( buf );\r
-   lwFreePolygons( plist );\r
-   return 0;\r
-}\r
-\r
-\r
-/*\r
-======================================================================\r
-getLWObject5()\r
-\r
-Returns the contents of an LWOB, given its filename, or NULL if the\r
-file couldn't be loaded.  On failure, failID and failpos can be used\r
-to diagnose the cause.\r
-\r
-1.  If the file isn't an LWOB, failpos will contain 12 and failID will\r
-    be unchanged.\r
-\r
-2.  If an error occurs while reading an LWOB, failID will contain the\r
-    most recently read IFF chunk ID, and failpos will contain the\r
-    value returned by _pico_memstream_tell() at the time of the failure.\r
-\r
-3.  If the file couldn't be opened, or an error occurs while reading\r
-    the first 12 bytes, both failID and failpos will be unchanged.\r
-\r
-If you don't need this information, failID and failpos can be NULL.\r
-====================================================================== */\r
-\r
-lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )\r
-{\r
-   lwObject *object;\r
-   lwLayer *layer;\r
-   lwNode *node;\r
-   unsigned int id, formsize, type, cksize;\r
-\r
-\r
-   /* open the file */\r
-\r
-   if ( !fp ) return NULL;\r
-\r
-   /* read the first 12 bytes */\r
-\r
-   set_flen( 0 );\r
-   id       = getU4( fp );\r
-   formsize = getU4( fp );\r
-   type     = getU4( fp );\r
-   if ( 12 != get_flen() ) {\r
-      return NULL;\r
-   }\r
-\r
-   /* LWOB? */\r
-\r
-   if ( id != ID_FORM || type != ID_LWOB ) {\r
-      if ( failpos ) *failpos = 12;\r
-      return NULL;\r
-   }\r
-\r
-   /* allocate an object and a default layer */\r
-\r
-   object = _pico_calloc( 1, sizeof( lwObject ));\r
-   if ( !object ) goto Fail;\r
-\r
-   layer = _pico_calloc( 1, sizeof( lwLayer ));\r
-   if ( !layer ) goto Fail;\r
-   object->layer = layer;\r
-   object->nlayers = 1;\r
-\r
-   /* get the first chunk header */\r
-\r
-   id = getU4( fp );\r
-   cksize = getU4( fp );\r
-   if ( 0 > get_flen() ) goto Fail;\r
-\r
-   /* process chunks as they're encountered */\r
-\r
-   while ( 1 ) {\r
-      cksize += cksize & 1;\r
-\r
-      switch ( id )\r
-      {\r
-         case ID_PNTS:\r
-            if ( !lwGetPoints( fp, cksize, &layer->point ))\r
-               goto Fail;\r
-            break;\r
-\r
-         case ID_POLS:\r
-            if ( !lwGetPolygons5( fp, cksize, &layer->polygon,\r
-               layer->point.offset ))\r
-               goto Fail;\r
-            break;\r
-\r
-         case ID_SRFS:\r
-            if ( !lwGetTags( fp, cksize, &object->taglist ))\r
-               goto Fail;\r
-            break;\r
-\r
-         case ID_SURF:\r
-            node = ( lwNode * ) lwGetSurface5( fp, cksize, object );\r
-            if ( !node ) goto Fail;\r
-            lwListAdd( &object->surf, node );\r
-            object->nsurfs++;\r
-            break;\r
-\r
-         default:\r
-            _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );\r
-            break;\r
-      }\r
-\r
-      /* end of the file? */\r
-\r
-      if ( formsize <= _pico_memstream_tell( fp ) - 8 ) break;\r
-\r
-      /* get the next chunk header */\r
-\r
-      set_flen( 0 );\r
-      id = getU4( fp );\r
-      cksize = getU4( fp );\r
-      if ( 8 != get_flen() ) goto Fail;\r
-   }\r
-\r
-   lwGetBoundingBox( &layer->point, layer->bbox );\r
-   lwGetPolyNormals( &layer->point, &layer->polygon );\r
-   if ( !lwGetPointPolygons( &layer->point, &layer->polygon )) goto Fail;\r
-   if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,\r
-      &object->surf, &object->nsurfs )) goto Fail;\r
-   lwGetVertNormals( &layer->point, &layer->polygon );\r
-\r
-   return object;\r
-\r
-Fail:\r
-   if ( failID ) *failID = id;\r
-   if ( fp ) {\r
-      if ( failpos ) *failpos = _pico_memstream_tell( fp );\r
-   }\r
-   lwFreeObject( object );\r
-   return NULL;\r
-}\r
-\r
-int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos )\r
-{\r
-   unsigned int id, formsize, type;\r
-\r
-\r
-   /* open the file */\r
-\r
-   if ( !fp ) return PICO_PMV_ERROR_MEMORY;\r
-\r
-   /* read the first 12 bytes */\r
-\r
-   set_flen( 0 );\r
-   id       = getU4( fp );\r
-   formsize = getU4( fp );\r
-   type     = getU4( fp );\r
-   if ( 12 != get_flen() ) {\r
-      return PICO_PMV_ERROR_SIZE;\r
-   }\r
-\r
-   /* LWOB? */\r
-\r
-   if ( id != ID_FORM || type != ID_LWOB ) {\r
-      if ( failpos ) *failpos = 12;\r
-      return PICO_PMV_ERROR_IDENT;\r
-   }\r
-\r
-   return PICO_PMV_OK;\r
-}\r
+/*
+   ======================================================================
+   lwob.c
+
+   Functions for an LWOB reader.  LWOB is the LightWave object format
+   for versions of LW prior to 6.0.
+
+   Ernie Wright  17 Sep 00
+   ====================================================================== */
+
+#include "../picointernal.h"
+#include "lwo2.h"
+
+/* disable warnings */
+#ifdef _WIN32
+#pragma warning( disable:4018 )                /* signed/unsigned mismatch */
+#endif
+
+
+/* IDs specific to LWOB */
+
+#define ID_SRFS  LWID_( 'S','R','F','S' )
+#define ID_FLAG  LWID_( 'F','L','A','G' )
+#define ID_VLUM  LWID_( 'V','L','U','M' )
+#define ID_VDIF  LWID_( 'V','D','I','F' )
+#define ID_VSPC  LWID_( 'V','S','P','C' )
+#define ID_RFLT  LWID_( 'R','F','L','T' )
+#define ID_BTEX  LWID_( 'B','T','E','X' )
+#define ID_CTEX  LWID_( 'C','T','E','X' )
+#define ID_DTEX  LWID_( 'D','T','E','X' )
+#define ID_LTEX  LWID_( 'L','T','E','X' )
+#define ID_RTEX  LWID_( 'R','T','E','X' )
+#define ID_STEX  LWID_( 'S','T','E','X' )
+#define ID_TTEX  LWID_( 'T','T','E','X' )
+#define ID_TFLG  LWID_( 'T','F','L','G' )
+#define ID_TSIZ  LWID_( 'T','S','I','Z' )
+#define ID_TCTR  LWID_( 'T','C','T','R' )
+#define ID_TFAL  LWID_( 'T','F','A','L' )
+#define ID_TVEL  LWID_( 'T','V','E','L' )
+#define ID_TCLR  LWID_( 'T','C','L','R' )
+#define ID_TVAL  LWID_( 'T','V','A','L' )
+#define ID_TAMP  LWID_( 'T','A','M','P' )
+#define ID_TIMG  LWID_( 'T','I','M','G' )
+#define ID_TAAS  LWID_( 'T','A','A','S' )
+#define ID_TREF  LWID_( 'T','R','E','F' )
+#define ID_TOPC  LWID_( 'T','O','P','C' )
+#define ID_SDAT  LWID_( 'S','D','A','T' )
+#define ID_TFP0  LWID_( 'T','F','P','0' )
+#define ID_TFP1  LWID_( 'T','F','P','1' )
+
+
+/*
+   ======================================================================
+   add_clip()
+
+   Add a clip to the clip list.  Used to store the contents of an RIMG or
+   TIMG surface subchunk.
+   ====================================================================== */
+
+static int add_clip( char *s, lwClip **clist, int *nclips ){
+       lwClip *clip;
+       char *p;
+
+       clip = _pico_calloc( 1, sizeof( lwClip ) );
+       if ( !clip ) {
+               return 0;
+       }
+
+       clip->contrast.val = 1.0f;
+       clip->brightness.val = 1.0f;
+       clip->saturation.val = 1.0f;
+       clip->gamma.val = 1.0f;
+
+       if ( ( p = strstr( s, "(sequence)" ) ) != NULL ) {
+               p[ -1 ] = 0;
+               clip->type = ID_ISEQ;
+               clip->source.seq.prefix = s;
+               clip->source.seq.digits = 3;
+       }
+       else {
+               clip->type = ID_STIL;
+               clip->source.still.name = s;
+       }
+
+       ( *nclips )++;
+       clip->index = *nclips;
+
+       lwListAdd( clist, clip );
+
+       return clip->index;
+}
+
+
+/*
+   ======================================================================
+   add_tvel()
+
+   Add a triple of envelopes to simulate the old texture velocity
+   parameters.
+   ====================================================================== */
+
+static int add_tvel( float pos[], float vel[], lwEnvelope **elist, int *nenvs ){
+       lwEnvelope *env;
+       lwKey *key0, *key1;
+       int i;
+
+       for ( i = 0; i < 3; i++ ) {
+               env = _pico_calloc( 1, sizeof( lwEnvelope ) );
+               key0 = _pico_calloc( 1, sizeof( lwKey ) );
+               key1 = _pico_calloc( 1, sizeof( lwKey ) );
+               if ( !env || !key0 || !key1 ) {
+                       return 0;
+               }
+
+               key0->next = key1;
+               key0->value = pos[ i ];
+               key0->time = 0.0f;
+               key1->prev = key0;
+               key1->value = pos[ i ] + vel[ i ] * 30.0f;
+               key1->time = 1.0f;
+               key0->shape = key1->shape = ID_LINE;
+
+               env->index = *nenvs + i + 1;
+               env->type = 0x0301 + i;
+               env->name = _pico_alloc( 11 );
+               if ( env->name ) {
+                       strcpy( env->name, "Position.X" );
+                       env->name[ 9 ] += i;
+               }
+               env->key = key0;
+               env->nkeys = 2;
+               env->behavior[ 0 ] = BEH_LINEAR;
+               env->behavior[ 1 ] = BEH_LINEAR;
+
+               lwListAdd( elist, env );
+       }
+
+       *nenvs += 3;
+       return env->index - 2;
+}
+
+
+/*
+   ======================================================================
+   get_texture()
+
+   Create a new texture for BTEX, CTEX, etc. subchunks.
+   ====================================================================== */
+
+static lwTexture *get_texture( char *s ){
+       lwTexture *tex;
+
+       tex = _pico_calloc( 1, sizeof( lwTexture ) );
+       if ( !tex ) {
+               return NULL;
+       }
+
+       tex->tmap.size.val[ 0 ] =
+               tex->tmap.size.val[ 1 ] =
+                       tex->tmap.size.val[ 2 ] = 1.0f;
+       tex->opacity.val = 1.0f;
+       tex->enabled = 1;
+
+       if ( strstr( s, "Image Map" ) ) {
+               tex->type = ID_IMAP;
+               if ( strstr( s, "Planar" ) ) {
+                       tex->param.imap.projection = 0;
+               }
+               else if ( strstr( s, "Cylindrical" ) ) {
+                       tex->param.imap.projection = 1;
+               }
+               else if ( strstr( s, "Spherical" ) ) {
+                       tex->param.imap.projection = 2;
+               }
+               else if ( strstr( s, "Cubic" ) ) {
+                       tex->param.imap.projection = 3;
+               }
+               else if ( strstr( s, "Front" ) ) {
+                       tex->param.imap.projection = 4;
+               }
+               tex->param.imap.aa_strength = 1.0f;
+               tex->param.imap.amplitude.val = 1.0f;
+               _pico_free( s );
+       }
+       else {
+               tex->type = ID_PROC;
+               tex->param.proc.name = s;
+       }
+
+       return tex;
+}
+
+
+/*
+   ======================================================================
+   lwGetSurface5()
+
+   Read an lwSurface from an LWOB file.
+   ====================================================================== */
+
+lwSurface *lwGetSurface5( picoMemStream_t *fp, int cksize, lwObject *obj ){
+       lwSurface *surf;
+       lwTexture *tex;
+       lwPlugin *shdr;
+       char *s;
+       float v[ 3 ];
+       unsigned int id, flags;
+       unsigned short sz;
+       int pos, rlen, i;
+
+
+       /* allocate the Surface structure */
+
+       surf = _pico_calloc( 1, sizeof( lwSurface ) );
+       if ( !surf ) {
+               goto Fail;
+       }
+
+       /* non-zero defaults */
+
+       surf->color.rgb[ 0 ] = 0.78431f;
+       surf->color.rgb[ 1 ] = 0.78431f;
+       surf->color.rgb[ 2 ] = 0.78431f;
+       surf->diffuse.val    = 1.0f;
+       surf->glossiness.val = 0.4f;
+       surf->bump.val       = 1.0f;
+       surf->eta.val        = 1.0f;
+       surf->sideflags      = 1;
+
+       /* remember where we started */
+
+       set_flen( 0 );
+       pos = _pico_memstream_tell( fp );
+
+       /* name */
+
+       surf->name = getS0( fp );
+
+       /* first subchunk header */
+
+       id = getU4( fp );
+       sz = getU2( fp );
+       if ( 0 > get_flen() ) {
+               goto Fail;
+       }
+
+       /* process subchunks as they're encountered */
+
+       while ( 1 ) {
+               sz += sz & 1;
+               set_flen( 0 );
+
+               switch ( id ) {
+               case ID_COLR:
+                       surf->color.rgb[ 0 ] = getU1( fp ) / 255.0f;
+                       surf->color.rgb[ 1 ] = getU1( fp ) / 255.0f;
+                       surf->color.rgb[ 2 ] = getU1( fp ) / 255.0f;
+                       break;
+
+               case ID_FLAG:
+                       flags = getU2( fp );
+                       if ( flags &   4 ) {
+                               surf->smooth = 1.56207f;
+                       }
+                       if ( flags &   8 ) {
+                               surf->color_hilite.val = 1.0f;
+                       }
+                       if ( flags &  16 ) {
+                               surf->color_filter.val = 1.0f;
+                       }
+                       if ( flags & 128 ) {
+                               surf->dif_sharp.val = 0.5f;
+                       }
+                       if ( flags & 256 ) {
+                               surf->sideflags = 3;
+                       }
+                       if ( flags & 512 ) {
+                               surf->add_trans.val = 1.0f;
+                       }
+                       break;
+
+               case ID_LUMI:
+                       surf->luminosity.val = getI2( fp ) / 256.0f;
+                       break;
+
+               case ID_VLUM:
+                       surf->luminosity.val = getF4( fp );
+                       break;
+
+               case ID_DIFF:
+                       surf->diffuse.val = getI2( fp ) / 256.0f;
+                       break;
+
+               case ID_VDIF:
+                       surf->diffuse.val = getF4( fp );
+                       break;
+
+               case ID_SPEC:
+                       surf->specularity.val = getI2( fp ) / 256.0f;
+                       break;
+
+               case ID_VSPC:
+                       surf->specularity.val = getF4( fp );
+                       break;
+
+               case ID_GLOS:
+                       surf->glossiness.val = ( float ) log( getU2( fp ) ) / 20.7944f;
+                       break;
+
+               case ID_SMAN:
+                       surf->smooth = getF4( fp );
+                       break;
+
+               case ID_REFL:
+                       surf->reflection.val.val = getI2( fp ) / 256.0f;
+                       break;
+
+               case ID_RFLT:
+                       surf->reflection.options = getU2( fp );
+                       break;
+
+               case ID_RIMG:
+                       s = getS0( fp );
+                       surf->reflection.cindex = add_clip( s, &obj->clip, &obj->nclips );
+                       surf->reflection.options = 3;
+                       break;
+
+               case ID_RSAN:
+                       surf->reflection.seam_angle = getF4( fp );
+                       break;
+
+               case ID_TRAN:
+                       surf->transparency.val.val = getI2( fp ) / 256.0f;
+                       break;
+
+               case ID_RIND:
+                       surf->eta.val = getF4( fp );
+                       break;
+
+               case ID_BTEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->bump.tex, tex );
+                       break;
+
+               case ID_CTEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->color.tex, tex );
+                       break;
+
+               case ID_DTEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->diffuse.tex, tex );
+                       break;
+
+               case ID_LTEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->luminosity.tex, tex );
+                       break;
+
+               case ID_RTEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->reflection.val.tex, tex );
+                       break;
+
+               case ID_STEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->specularity.tex, tex );
+                       break;
+
+               case ID_TTEX:
+                       s = getbytes( fp, sz );
+                       tex = get_texture( s );
+                       lwListAdd( &surf->transparency.val.tex, tex );
+                       break;
+
+               case ID_TFLG:
+                       flags = getU2( fp );
+
+                       if ( flags & 1 ) {
+                               i = 0;
+                       }
+                       if ( flags & 2 ) {
+                               i = 1;
+                       }
+                       if ( flags & 4 ) {
+                               i = 2;
+                       }
+                       tex->axis = i;
+                       if ( tex->type == ID_IMAP ) {
+                               tex->param.imap.axis = i;
+                       }
+                       else{
+                               tex->param.proc.axis = i;
+                       }
+
+                       if ( flags &  8 ) {
+                               tex->tmap.coord_sys = 1;
+                       }
+                       if ( flags & 16 ) {
+                               tex->negative = 1;
+                       }
+                       if ( flags & 32 ) {
+                               tex->param.imap.pblend = 1;
+                       }
+                       if ( flags & 64 ) {
+                               tex->param.imap.aa_strength = 1.0f;
+                               tex->param.imap.aas_flags = 1;
+                       }
+                       break;
+
+               case ID_TSIZ:
+                       for ( i = 0; i < 3; i++ )
+                               tex->tmap.size.val[ i ] = getF4( fp );
+                       break;
+
+               case ID_TCTR:
+                       for ( i = 0; i < 3; i++ )
+                               tex->tmap.center.val[ i ] = getF4( fp );
+                       break;
+
+               case ID_TFAL:
+                       for ( i = 0; i < 3; i++ )
+                               tex->tmap.falloff.val[ i ] = getF4( fp );
+                       break;
+
+               case ID_TVEL:
+                       for ( i = 0; i < 3; i++ )
+                               v[ i ] = getF4( fp );
+                       tex->tmap.center.eindex = add_tvel( tex->tmap.center.val, v,
+                                                                                               &obj->env, &obj->nenvs );
+                       break;
+
+               case ID_TCLR:
+                       if ( tex->type == ID_PROC ) {
+                               for ( i = 0; i < 3; i++ )
+                                       tex->param.proc.value[ i ] = getU1( fp ) / 255.0f;
+                       }
+                       break;
+
+               case ID_TVAL:
+                       tex->param.proc.value[ 0 ] = getI2( fp ) / 256.0f;
+                       break;
+
+               case ID_TAMP:
+                       if ( tex->type == ID_IMAP ) {
+                               tex->param.imap.amplitude.val = getF4( fp );
+                       }
+                       break;
+
+               case ID_TIMG:
+                       s = getS0( fp );
+                       tex->param.imap.cindex = add_clip( s, &obj->clip, &obj->nclips );
+                       break;
+
+               case ID_TAAS:
+                       tex->param.imap.aa_strength = getF4( fp );
+                       tex->param.imap.aas_flags = 1;
+                       break;
+
+               case ID_TREF:
+                       tex->tmap.ref_object = getbytes( fp, sz );
+                       break;
+
+               case ID_TOPC:
+                       tex->opacity.val = getF4( fp );
+                       break;
+
+               case ID_TFP0:
+                       if ( tex->type == ID_IMAP ) {
+                               tex->param.imap.wrapw.val = getF4( fp );
+                       }
+                       break;
+
+               case ID_TFP1:
+                       if ( tex->type == ID_IMAP ) {
+                               tex->param.imap.wraph.val = getF4( fp );
+                       }
+                       break;
+
+               case ID_SHDR:
+                       shdr = _pico_calloc( 1, sizeof( lwPlugin ) );
+                       if ( !shdr ) {
+                               goto Fail;
+                       }
+                       shdr->name = getbytes( fp, sz );
+                       lwListAdd( &surf->shader, shdr );
+                       surf->nshaders++;
+                       break;
+
+               case ID_SDAT:
+                       shdr->data = getbytes( fp, sz );
+                       break;
+
+               default:
+                       break;
+               }
+
+               /* error while reading current subchunk? */
+
+               rlen = get_flen();
+               if ( rlen < 0 || rlen > sz ) {
+                       goto Fail;
+               }
+
+               /* skip unread parts of the current subchunk */
+
+               if ( rlen < sz ) {
+                       _pico_memstream_seek( fp, sz - rlen, PICO_SEEK_CUR );
+               }
+
+               /* end of the SURF chunk? */
+
+               if ( cksize <= _pico_memstream_tell( fp ) - pos ) {
+                       break;
+               }
+
+               /* get the next subchunk header */
+
+               set_flen( 0 );
+               id = getU4( fp );
+               sz = getU2( fp );
+               if ( 6 != get_flen() ) {
+                       goto Fail;
+               }
+       }
+
+       return surf;
+
+Fail:
+       if ( surf ) {
+               lwFreeSurface( surf );
+       }
+       return NULL;
+}
+
+
+/*
+   ======================================================================
+   lwGetPolygons5()
+
+   Read polygon records from a POLS chunk in an LWOB file.  The polygons
+   are added to the array in the lwPolygonList.
+   ====================================================================== */
+
+int lwGetPolygons5( picoMemStream_t *fp, int cksize, lwPolygonList *plist, int ptoffset ){
+       lwPolygon *pp;
+       lwPolVert *pv;
+       unsigned char *buf, *bp;
+       int i, j, nv, nverts, npols;
+
+
+       if ( cksize == 0 ) {
+               return 1;
+       }
+
+       /* read the whole chunk */
+
+       set_flen( 0 );
+       buf = getbytes( fp, cksize );
+       if ( !buf ) {
+               goto Fail;
+       }
+
+       /* count the polygons and vertices */
+
+       nverts = 0;
+       npols = 0;
+       bp = buf;
+
+       while ( bp < buf + cksize ) {
+               nv = sgetU2( &bp );
+               nverts += nv;
+               npols++;
+               bp += 2 * nv;
+               i = sgetI2( &bp );
+               if ( i < 0 ) {
+                       bp += 2;             /* detail polygons */
+               }
+       }
+
+       if ( !lwAllocPolygons( plist, npols, nverts ) ) {
+               goto Fail;
+       }
+
+       /* fill in the new polygons */
+
+       bp = buf;
+       pp = plist->pol + plist->offset;
+       pv = plist->pol[ 0 ].v + plist->voffset;
+
+       for ( i = 0; i < npols; i++ ) {
+               nv = sgetU2( &bp );
+
+               pp->nverts = nv;
+               pp->type = ID_FACE;
+               if ( !pp->v ) {
+                       pp->v = pv;
+               }
+               for ( j = 0; j < nv; j++ )
+                       pv[ j ].index = sgetU2( &bp ) + ptoffset;
+               j = sgetI2( &bp );
+               if ( j < 0 ) {
+                       j = -j;
+                       bp += 2;
+               }
+               j -= 1;
+               pp->surf = ( lwSurface * ) ( (size_t)j );
+
+               pp++;
+               pv += nv;
+       }
+
+       _pico_free( buf );
+       return 1;
+
+Fail:
+       if ( buf ) {
+               _pico_free( buf );
+       }
+       lwFreePolygons( plist );
+       return 0;
+}
+
+
+/*
+   ======================================================================
+   getLWObject5()
+
+   Returns the contents of an LWOB, given its filename, or NULL if the
+   file couldn't be loaded.  On failure, failID and failpos can be used
+   to diagnose the cause.
+
+   1.  If the file isn't an LWOB, failpos will contain 12 and failID will
+    be unchanged.
+
+   2.  If an error occurs while reading an LWOB, failID will contain the
+    most recently read IFF chunk ID, and failpos will contain the
+    value returned by _pico_memstream_tell() at the time of the failure.
+
+   3.  If the file couldn't be opened, or an error occurs while reading
+    the first 12 bytes, both failID and failpos will be unchanged.
+
+   If you don't need this information, failID and failpos can be NULL.
+   ====================================================================== */
+
+lwObject *lwGetObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ){
+       lwObject *object;
+       lwLayer *layer;
+       lwNode *node;
+       unsigned int id, formsize, type, cksize;
+
+
+       /* open the file */
+
+       if ( !fp ) {
+               return NULL;
+       }
+
+       /* read the first 12 bytes */
+
+       set_flen( 0 );
+       id       = getU4( fp );
+       formsize = getU4( fp );
+       type     = getU4( fp );
+       if ( 12 != get_flen() ) {
+               return NULL;
+       }
+
+       /* LWOB? */
+
+       if ( id != ID_FORM || type != ID_LWOB ) {
+               if ( failpos ) {
+                       *failpos = 12;
+               }
+               return NULL;
+       }
+
+       /* allocate an object and a default layer */
+
+       object = _pico_calloc( 1, sizeof( lwObject ) );
+       if ( !object ) {
+               goto Fail;
+       }
+
+       layer = _pico_calloc( 1, sizeof( lwLayer ) );
+       if ( !layer ) {
+               goto Fail;
+       }
+       object->layer = layer;
+       object->nlayers = 1;
+
+       /* get the first chunk header */
+
+       id = getU4( fp );
+       cksize = getU4( fp );
+       if ( 0 > get_flen() ) {
+               goto Fail;
+       }
+
+       /* process chunks as they're encountered */
+
+       while ( 1 ) {
+               cksize += cksize & 1;
+
+               switch ( id )
+               {
+               case ID_PNTS:
+                       if ( !lwGetPoints( fp, cksize, &layer->point ) ) {
+                               goto Fail;
+                       }
+                       break;
+
+               case ID_POLS:
+                       if ( !lwGetPolygons5( fp, cksize, &layer->polygon,
+                                                                 layer->point.offset ) ) {
+                               goto Fail;
+                       }
+                       break;
+
+               case ID_SRFS:
+                       if ( !lwGetTags( fp, cksize, &object->taglist ) ) {
+                               goto Fail;
+                       }
+                       break;
+
+               case ID_SURF:
+                       node = ( lwNode * ) lwGetSurface5( fp, cksize, object );
+                       if ( !node ) {
+                               goto Fail;
+                       }
+                       lwListAdd( &object->surf, node );
+                       object->nsurfs++;
+                       break;
+
+               default:
+                       _pico_memstream_seek( fp, cksize, PICO_SEEK_CUR );
+                       break;
+               }
+
+               /* end of the file? */
+
+               if ( formsize <= _pico_memstream_tell( fp ) - 8 ) {
+                       break;
+               }
+
+               /* get the next chunk header */
+
+               set_flen( 0 );
+               id = getU4( fp );
+               cksize = getU4( fp );
+               if ( 8 != get_flen() ) {
+                       goto Fail;
+               }
+       }
+
+       lwGetBoundingBox( &layer->point, layer->bbox );
+       lwGetPolyNormals( &layer->point, &layer->polygon );
+       if ( !lwGetPointPolygons( &layer->point, &layer->polygon ) ) {
+               goto Fail;
+       }
+       if ( !lwResolvePolySurfaces( &layer->polygon, &object->taglist,
+                                                                &object->surf, &object->nsurfs ) ) {
+               goto Fail;
+       }
+       lwGetVertNormals( &layer->point, &layer->polygon );
+
+       return object;
+
+Fail:
+       if ( failID ) {
+               *failID = id;
+       }
+       if ( fp ) {
+               if ( failpos ) {
+                       *failpos = _pico_memstream_tell( fp );
+               }
+       }
+       lwFreeObject( object );
+       return NULL;
+}
+
+int lwValidateObject5( char *filename, picoMemStream_t *fp, unsigned int *failID, int *failpos ){
+       unsigned int id, formsize, type;
+
+
+       /* open the file */
+
+       if ( !fp ) {
+               return PICO_PMV_ERROR_MEMORY;
+       }
+
+       /* read the first 12 bytes */
+
+       set_flen( 0 );
+       id       = getU4( fp );
+       formsize = getU4( fp );
+       type     = getU4( fp );
+       if ( 12 != get_flen() ) {
+               return PICO_PMV_ERROR_SIZE;
+       }
+
+       /* LWOB? */
+
+       if ( id != ID_FORM || type != ID_LWOB ) {
+               if ( failpos ) {
+                       *failpos = 12;
+               }
+               return PICO_PMV_ERROR_IDENT;
+       }
+
+       return PICO_PMV_OK;
+}