bool LWOFile::LoadSURF(unsigned int iChunkSize)
{
  Surface surf;
  surf.m_vBaseColor[0] = 1.0f;
  surf.m_vBaseColor[1] = 1.0f;
  surf.m_vBaseColor[2] = 1.0f;

  // SURF { name[S0], source[S0], attributes[SUB-CHUNK] * }
  const char *pChunkEndPos=m_pData+iChunkSize;

  // name
  unsigned int iStrBytes=0;
  if(!ReadS0(surf.m_strName,iStrBytes))
  {
    return false;
  }

  // source
  if(!ReadS0(surf.m_strSource,iStrBytes))
  {
    return false;
  }
  
  // while there's data left
  while(m_pData<pChunkEndPos)
  {
    // get chunk ID
    unsigned int iID=0;
    if(!ReadID4(iID)) return false;

    // get chunk size
    unsigned short iChunkSize=0;
    if(!ReadU2(iChunkSize)) return false;

    const char *pSubChunkEndPos=m_pData+iChunkSize;

    // color
    if(iID == ID4("COLR"))
    {
      // COLR { base-color[COL12], envelope[VX]  }

      // read values
      if(!ReadF4(surf.m_vBaseColor[0])) return false;
      if(!ReadF4(surf.m_vBaseColor[1])) return false;
      if(!ReadF4(surf.m_vBaseColor[2])) return false;
    }

    // skip to end of sub-chunk
    m_pData = pSubChunkEndPos;
    if(iChunkSize%2==1) m_pData+=1;
  }

  m_Surfaces.push_back(surf);

  return true;
}
bool LWOFile::LoadUVMap(unsigned int iChunkSize)
{
  Layer *pLayer=GetLastLayer();

  const char *pChunkEndPos=m_pData+iChunkSize-4;

  unsigned short iDimension=0;
  if(!ReadU2(iDimension))
  {
    return false;
  }

  // not 2 floats per vertex
  if(iDimension!=2)
  {
    // just skip
    m_pData+=iChunkSize-4-2;
    AddError("Warning: UVMap has "+ConvertToString(iDimension)+" floats per vertex (2 expected)");
    return true;
  }

  unsigned int iStrBytes=0;
  std::string strName;
  if(!ReadS0(strName,iStrBytes))
  {
    return false;
  }


  // VMAP { type[ID4], dimension[U2], name[S0],
  //  ( vert[VX], value[F4] # dimension )* }

  UVMap *pUVMap=new UVMap();
  pUVMap->m_strName=strName;
  pUVMap->m_Values.resize(2*pLayer->m_iPoints);
  pLayer->m_UVMaps.push_back(pUVMap);

  float *pValues=&(pUVMap->m_Values[0]);
  memset(pValues,0,sizeof(float)*2*pLayer->m_iPoints);

  while(m_pData<pChunkEndPos)
  {
    unsigned int iVertexID=0;
    if(!ReadVX(iVertexID)) return false;
    if(iVertexID>=pLayer->m_iPoints) return false;

    if(!ReadF4(pValues[iVertexID*2+0])) return false;
    if(!ReadF4(pValues[iVertexID*2+1])) return false;

    // flip v coordinate
    pValues[iVertexID*2+1]=1-pValues[iVertexID*2+1];
  }

  return true;
}
bool LWOFile::LoadLAYR(unsigned int iChunkSize)
{
  Layer *pLayer=new Layer();
  m_Layers.push_back(pLayer);

  // LAYR { number[U2], flags[U2], pivot[VEC12], name[S0], parent[U2] ? }
  const char *pChunkEndPos=m_pData+iChunkSize;
  if(!ReadU2(pLayer->m_iID)) return false;
  if(!ReadU2(pLayer->m_iFlags)) return false;
  if(!ReadF4(pLayer->m_vPivot[0])) return false;
  if(!ReadF4(pLayer->m_vPivot[1])) return false;
  if(!ReadF4(pLayer->m_vPivot[2])) return false;
  if(!ReadS0(pLayer->m_strName)) return false;

  // parent id is optional, so read it only if theres data left
  if(m_pData<pChunkEndPos)
  {
    if(!ReadU2(pLayer->m_iParentID)) return false;
  } else {
    pLayer->m_iParentID=0;
  }

  return true;
}
bool LWOFile::LoadVMAD(unsigned int iChunkSize)
{
  Layer *pLayer=GetLastLayer();
  if(pLayer==NULL)
  {
    AddError("No LAYR before VMAD");
    return false;
  }

  const char *pChunkEndPos=m_pData+iChunkSize;

  // VMAD { type[ID4], dimension[U2], name[S0],
  // ( vert[VX], poly[VX], value[F4] # dimension )* }
  unsigned int iType=0;
  if(!ReadID4(iType)) return false;

  // type must be uvmap
  if(iType!=ID4("TXUV"))
  {
    AddError("Warning: Unknown discontinuous vertex map type "+ID4ToString(iType));
    m_pData+=iChunkSize-4;
    return true;
  }

  // dimension
  unsigned short iDimension=0;
  if(!ReadU2(iDimension))
  {
    return false;
  }

  // not 2 floats per vertex
  if(iDimension!=2)
  {
    AddError("Warning: Discontinuous UVMap has "+ConvertToString(iDimension)+" floats per vertex (2 expected)");
    // skip
    m_pData+=iChunkSize-4-2;
    return true;
  }

  // name
  unsigned int iStrBytes=0;
  std::string strName;
  if(!ReadS0(strName,iStrBytes))
  {
    return false;
  }

  // for each uvmap
  UVMap *pUV=NULL;
  for(unsigned int iUV=0;iUV<pLayer->m_UVMaps.size();iUV++)
  {
    if(pLayer->m_UVMaps[iUV]->m_strName==strName)
    {
      pUV=pLayer->m_UVMaps[iUV];
    }
  }

  if(pUV==NULL)
  {
    AddError("No matching UVMap for discontinuous UVMap \""+strName+"\"");
    return false;
  }


  // read rest of chunk to memory
  //
  unsigned int iBytesLeft=iChunkSize-4-2-iStrBytes;

  UVMapD *pUVMap=new UVMapD();
  pUVMap->m_strName=strName;
  pUVMap->m_pUVMap=pUV;
  // worst case estimate
  pUVMap->m_Entries.reserve(iBytesLeft/(2+2+4+4));
  pLayer->m_UVMapDs.push_back(pUVMap);

  while(m_pData<pChunkEndPos)
  {
    unsigned int iVertexID=0;
    if(!ReadVX(iVertexID)) return false;

    unsigned int iPolyID=0;
    if(!ReadVX(iPolyID)) return false;

    UVMapD::Entry e;
    e.iVertex=iVertexID;
    e.iPolygon=iPolyID;
    if(!ReadF4(e.u)) return false;
    if(!ReadF4(e.v)) return false;
    // flip v coordinate
    e.v=1-e.v;
    pUVMap->m_Entries.push_back(e);
  }

  return true;
}
void LWOReader::ReadVMAP( UInt32 pChunkSize, Bool pPerPoly )
{
    UInt32              dataRead = 0;
    UInt16              dimension;
    LWIndex             vertexIndex;
    LWIndex             polygonIndex;
    
    LWVertexMap*        newVertexMap = GD_NEW(LWVertexMap, this, "VertexMap");

    newVertexMap->mPerPoly = pPerPoly;

    dataRead += ReadID4( newVertexMap->mType );
    dataRead += ReadU2( dimension );
    dataRead += ReadS0( newVertexMap->mName );

    while( dataRead < pChunkSize )
    {
        dataRead += ReadVX( vertexIndex );
        newVertexMap->mVertexIndex.push_back( vertexIndex );

        if( pPerPoly )
        {
            dataRead += ReadVX( polygonIndex );
            newVertexMap->mPolygonIndex.push_back( polygonIndex );
        }

        LWMapValue mapping;

        switch( newVertexMap->mType )
        {
        case ID_PICK:
            mapping.mPick = vertexIndex;
            break;

        case ID_WGHT:
            dataRead += ReadF4( mapping.mWeight );
            break;

        case ID_TXUV:   
            dataRead += ReadF4( mapping.mUV.U );
            dataRead += ReadF4( mapping.mUV.V );
            break;

        case ID_RGB:
            dataRead += ReadF4( mapping.mRGB.R );
            dataRead += ReadF4( mapping.mRGB.G );
            dataRead += ReadF4( mapping.mRGB.B );
            break;

        case ID_RGBA:
            dataRead += ReadF4( mapping.mRGBA.R );
            dataRead += ReadF4( mapping.mRGBA.G );
            dataRead += ReadF4( mapping.mRGBA.B );
            dataRead += ReadF4( mapping.mRGBA.A ); 
            break;

        case ID_MORF:
        case ID_SPOT:
        case ID_MNVW:   
        default:       
            dataRead += Skip( dimension * sizeof(Float) );      
            break;
        }   

        newVertexMap->mValues.push_back( mapping );
    }

    GetCurrentLayer()->mVertexMaps.push_back( newVertexMap );
}