geometryCacheFile.cpp

//-
// ==========================================================================
// Copyright 1995,2006,2008 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+

//
// File Name: geometryCacheFile.cpp
//
// Description : An interface used for reading cache file data, storing it 
// and converting it to ASCII. See geometryCacheFile.h for file format info.
//

// Project includes
//
#include <geometryCacheFile.h>
#include <geometryCacheBlockBase.h>
#include <geometryCacheBlockStringData.h>
#include <geometryCacheBlockIntData.h>
#include <geometryCacheBlockDVAData.h>
#include <geometryCacheBlockFVAData.h>

// Maya includes
//
#include <maya/flib.h>
#include <maya/MString.h>
#include <maya/MGlobal.h>

// Other includes
//
#include <fstream>

#if defined (OSMac_)
#include <stdlib.h>
#else
#include <malloc.h>
#endif

//
// Methods
//

geometryCacheFile::geometryCacheFile( const MString& fileName, MIffFile* iffFile )
    :cacheFileName( fileName )
//
// Description : ( public method )
//      Constructor
//
{   
    readStatus = false;
    iffFilePtr = iffFile;
}

geometryCacheFile::~geometryCacheFile()
//
// Description : ( public method )
//      Destructor
//
{
    // Free all the pointers in the blockList.
    // Start from the back of the list, delete the pointer returned then 
    // pop it off the stack.
    //
    while( blockList.size() )
    {   
        // Get current pointer
        //
        geometryCacheBlockBase * block = blockList.back();

        delete block;

        // Pop the back item of the stack
        //
        blockList.pop_back();
    }
    
    // Close the cache file
    //
    iffFilePtr->close();
}

const MString& geometryCacheFile::fileName()
//
// Description : ( public method )
//      Returns the file name
//
{
    return cacheFileName;
}

const bool& geometryCacheFile::isRead()
//
// Description : ( public method )
//      Indicates if the file has been read
//
{
    return readStatus;
}

bool geometryCacheFile::readCacheFiles()
//
// Description : ( public method )
//      Read the cache file
//
{   
    MStatus status = MS::kSuccess;

    // Attempt to open the file
    //
    status = iffFilePtr->open( cacheFileName );
    if( !status ) return false;

    // Proceed if the file is open
    //
    if( iffFilePtr->isActive() ) {
        // Read the Header
        //
        readStatus  = readHeaderGroup( status );
        if( !readStatus ) return false;

        // Read all the channel groups
        //
        do {
            // Read the next channel group
            //
            readStatus = readChannelGroup( status );
            if( !readStatus ) return false;

        } while ( status == MS::kSuccess );

    } else
        // If file failed to open
        //
        return false;

    return true;
}

bool geometryCacheFile::convertToAscii()
//
// Description : ( public method )
//      Convert the file to Ascii
//
{
    // Generate an output file name by changing the file name extention to txt
    //
    int loc = cacheFileName.rindex('.');
    MString outputFileName = cacheFileName.substring(0, loc-1);
    outputFileName += ".txt";

    // Create an output file steam to flush our data to ascii
    //
    std::ofstream oFile( outputFileName.asChar() );
    if( !oFile.bad() ) 
    {
        // Create an iterator to iterate through the blockList
        //
        cacheBlockIterator blockIt;

        // Write out all the blocks in the blockList
        //
        for( blockIt = blockList.begin();
            blockIt != blockList.end();
            blockIt++ )
        {
            // Get the current block
            //
            geometryCacheBlockBase* block = *blockIt;
            
            // OutputToAscii
            //
            block->outputToAscii( oFile );
        }
    } else
        // If output file stream could not open the file
        //
        return false;

    // Output a message to indicate that the file has been converted
    //
    MGlobal::displayInfo( "Converted file \"" +
                            cacheFileName +
                            "\" to file \"" +
                            outputFileName + 
                            "\"" );
    return true;
}

bool geometryCacheFile::readHeaderGroup( MStatus& status )
//
// Description : ( private method )
//      Read the Header group CACH. 
//      The header of every disk cache file consists of these tags
//
//      CACH    ( header group )
//      VRSN    ( version )
//      STIM    ( startTime in ticks )
//      ETIM    ( endTime in ticks )
//
{
    MIffTag tmpTag;
    MIffTag cachTag;

    status = iffFilePtr->beginReadGroup( tmpTag, cachTag );
    if( status ) {
        if( cachTag == MIffTag('C', 'A', 'C', 'H') ) {
            // Store a "CACH" tag into the blockList
            //
            storeCacheBlock( "CACH" );

            // Read the header version
            //
            readStatus = readHeaderVersion();
            if( !readStatus ) return false;

            // Read the Time Range
            //
            readStatus = readHeaderTimeRange();
            if( !readStatus ) return false;

            // Store an ending "/CACH" tag into the blockList
            //
            storeCacheBlock( "/CACH" );
        }       
    }
    iffFilePtr->endReadGroup();

    return true;
}

bool geometryCacheFile::readHeaderVersion()
//
// Description : ( private method )
//      Read and store the cache file version from the VRSN block.
//
{
    MStatus status = MS::kSuccess;
    MIffTag tmpTag;
    unsigned int byteCount;
    
    status = iffFilePtr->beginGet( tmpTag, (unsigned *)&byteCount );
    if( !status ) return false;

    char *tmpName = (char*)alloca(sizeof(char)* (byteCount+1));
    //pointers allocated by alloca are from the stack and automatically freed when
    //the function exits.  Do not free explicitly.

    if( tmpName && tmpTag == MIffTag('V', 'R', 'S', 'N') ) {
        // read the data from the block
        //
        iffFilePtr->get( tmpName, byteCount, &status);
        if( !status ) return false;

        // Store the cache file version in the blockList
        //
        storeCacheBlock( "VRSN", tmpName );

        status = iffFilePtr->endGet();
        if( !status ) return false;
    }

    return true;
}

bool geometryCacheFile::readHeaderTimeRange()
//
// Description : ( private method )
//      Read and store the start and end time from the STIM and ETIM blocks.
//      The data from these blocks are only used for multiple cache files.
//      If the cache file is a single cache file, then the values will only be 
//      from 0 to 1.
//
{
    MStatus status = MS::kSuccess;
    MIffTag tmpTag;
    unsigned int byteCount;
    
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
    const int *startTime = (int *)iffFilePtr->getChunk( 
                                tmpTag, 
                                (unsigned int *)&byteCount );  
    if( startTime == NULL ||
        !(tmpTag == MIffTag ('S', 'T', 'I', 'M')) || 
        byteCount != sizeof(int) )
        return false;

    // Store the start time in the blockList
    //
    storeCacheBlock( "STIM", *startTime );

    const int *endTime = (int *)iffFilePtr->getChunk( 
                                tmpTag, 
                                (unsigned int *)&byteCount );  
    if( endTime == NULL ||
        !(tmpTag == MIffTag('E', 'T', 'I', 'M')) ||
        byteCount != sizeof(int) )
        return false;               

    // Store the end time in the blockList
    //
    storeCacheBlock( "ETIM", *endTime );
#else
    const int *startTime = (int *)iffFilePtr->getChunk( tmpTag, (unsigned int *)&byteCount );  
    if( startTime == NULL ||
        // There is no != operator for MIffTag
        !(tmpTag == MIffTag ('S', 'T', 'I', 'M')) ||
        byteCount != sizeof(int) )
        return false;

    // Store the start time in the blockList
    //
    storeCacheBlock( "STIM", FLswapword(*startTime) );

    const int *endTime = (int *)iffFilePtr->getChunk( tmpTag, (unsigned int *)&byteCount );  
    if( endTime == NULL ||
        !(tmpTag == MIffTag('E', 'T', 'I', 'M')) ||
        byteCount != sizeof(int) )
        return false;

    // Store the end time in the blockList
    //
    storeCacheBlock( "ETIM", FLswapword(*endTime) );
#endif
    return true;
}

bool geometryCacheFile::readChannelGroup( MStatus& groupStatus )
//
// Description : ( private method )
//      Read the channel group MYCH.
//      The channel group can consist of these following tags
//
//      MYCH    ( time group )
//      TIME    ( time in ticks )
//      CHNM    ( channel name )
//      SIZE    ( size )
//      DVCA    ( geometry point data )
//
{
    MIffTag mychTag;
    MIffTag tmpTag;
    unsigned int byteCount;

    groupStatus = iffFilePtr->beginReadGroup(tmpTag, mychTag);
    if( groupStatus == MS::kSuccess ) {
        if( mychTag == MIffTag('M', 'Y', 'C', 'H')) {
            // Store the "MYCH" group in the blockList
            //
            storeCacheBlock( "MYCH" );

            // Get the next tag
            //
            MStatus stat = iffFilePtr->beginGet(tmpTag, (unsigned *)&byteCount);
            if (!stat) return false;

            // If the tag is TIME, then this is a single file cache file
            // Read the time chunk before the channel name chunk
            //
            if( tmpTag == MIffTag('T', 'I', 'M', 'E') )
            {
                // Read channel time
                //
                readStatus = readChannelTime();
                if ( !readStatus ) return false;

                // End Get
                //
                stat = iffFilePtr->endGet();
                if (!stat) return false;
            }

            // Read Channel
            //
            MStatus channelStatus = MS::kSuccess;
            do {
                // Read channel name, size and data
                //
                readStatus = readChannel( channelStatus );
                if( !readStatus ) return false;

            } while ( channelStatus == MS::kSuccess );

            // Store the ending "/MYCH" in the blockList
            //
            storeCacheBlock( "/MYCH" );
        }
    } 
    iffFilePtr->endReadGroup();
    return readStatus;
}

bool geometryCacheFile::readChannelTime()
//
// Description : ( private method )
//      Read and store the channel time from the TIME block.
//
{
    MIffTag tmpTag;
    unsigned int byteCount;

    const int *tmpNum = (int*) iffFilePtr->getChunk( tmpTag, (unsigned *)&byteCount );
    if( tmpNum && 
        tmpTag == MIffTag('T', 'I', 'M', 'E') && 
        byteCount == sizeof(int)) {
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
        // Store the channel time in the blockList
        //
        storeCacheBlock( "TIME", *tmpNum );
#else
        // Store the channel time in the blockList
        //
        storeCacheBlock( "TIME", FLswapword(*tmpNum) );
#endif      
    } 
    return true;
}

bool geometryCacheFile::readChannel( MStatus& channelStatus )
//
// Description : ( private method )
//      Read and store the channel data.
//
{
    readStatus = readChannelName( channelStatus );
    if( !readStatus ) return false;

    // If channelStatus is not MS::Success then this means that there is no
    // more channel data to read.  Return from function.
    //
    if( !channelStatus ) return true;

    readStatus = readChannelData();
    if( !readStatus ) return false;

    return true;
}
bool geometryCacheFile::readChannelName( MStatus& channelStatus )
//
// Description : ( private method )
//      Read and store the channel name from the CHNM block.
//
{
    MStatus lStatus;
    unsigned int byteCount;
    MIffTag chnmTag;
    
    channelStatus = iffFilePtr->beginGet( chnmTag, (unsigned *)&byteCount );
    if( channelStatus == MS::kSuccess ) {
        char *tmpName = (char*)alloca(sizeof(char)* (byteCount+1));
        //pointers allocated by alloca are from the stack and automatically freed when
        //the function exits.  Do not free explicitly.

        if( tmpName && chnmTag == MIffTag('C', 'H', 'N', 'M') ) {
            iffFilePtr->get( tmpName, byteCount, &lStatus);
            if( !lStatus ) return false;
            MString channelName = tmpName;

            // Store the channel name in the blockList
            //
            storeCacheBlock( "CHNM", channelName );
        } else return false;
    } 
    iffFilePtr->endGet();
    return true;
}

bool geometryCacheFile::readChannelData()
//
// Description : ( private method )
//      Read and store the channel size and geometry point data from the SIZE
//      and DVCA or FVCA blocks.
//
{
    MIffTag tmpTag;
    unsigned int byteCount;
    uint size;

    // Read the channel size
    //
    const int *tmpNum = (int*) iffFilePtr->getChunk(tmpTag, (unsigned *)&byteCount);
    if( tmpNum && 
        tmpTag == MIffTag('S', 'I', 'Z', 'E') && 
        byteCount == sizeof(int)) 
    {
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
            size = *tmpNum;
#else
            size = FLswapword(*tmpNum);
#endif
        // Store the channel size in the blockList
        //
        storeCacheBlock( "SIZE", size );
    } else return false;

    // Read the channel data
    //
    if( size ) {
        const void *tmpVec = (double*) iffFilePtr->getChunk(tmpTag, (unsigned *)&byteCount);

        if( tmpVec && 
            tmpTag == MIffTag('D', 'V', 'C', 'A') &&
            size * sizeof(double)*3 == byteCount )
        {
            double *tmpVecDbl = (double*) tmpVec;
            double *dataArray = new double[size*3];
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
            memcpy(dataArray, tmpVec, byteCount);
#else
            for(unsigned int i = 0; i < size*3; i++) {
                FLswapdouble(tmpVecDbl[i], &(dataArray[i]));
            }
#endif      
            // Store the channel geometry points in the blockList
            //
            storeCacheBlock( "DVCA", dataArray, size );
            delete [] dataArray;
        } else if( tmpVec && 
                   tmpTag == MIffTag('F', 'V', 'C', 'A') &&
                   size * sizeof(float)*3 == byteCount ) {
            float *tmpVecFlt = (float*) tmpVec;
            float *dataArray = new float[size*3];
#if (!defined BYTE_ORDER) || (BYTE_ORDER == BIG_ENDIAN)
            memcpy(dataArray, tmpVec, byteCount);
#else
            for(unsigned int i = 0; i < size*3; i++) {
                FLswapfloat(tmpVecFlt[i], &(dataArray[i]));
            }
#endif      
            // Store the channel geometry points in the blockList
            //
            storeCacheBlock( "FVCA", dataArray, size );
            delete [] dataArray;
        } else {
            return false;
        }
    }
    return true;
}

void geometryCacheFile::storeCacheBlock( const MString& tag )
//
// Description : ( private method )
//      Stores the specified data into the blockList.
//
{
    blockList.push_back( new geometryCacheBlockBase( tag ) );
}

void geometryCacheFile::storeCacheBlock( const MString& tag, const int& value )
//
// Description : ( private method )
//      Stores the specified data into the blockList.
//
{
    blockList.push_back( new geometryCacheBlockIntData( tag, value ) );
}

void geometryCacheFile::storeCacheBlock( const MString& tag, const MString& value  )
//
// Description : ( private method )
//      Stores the specified data into the blockList.
//
{
    blockList.push_back( new geometryCacheBlockStringData( tag, value ) );
}

void geometryCacheFile::storeCacheBlock( const MString& tag, const double* value, const uint& size )
//
// Description : ( private method )
//      Stores the specified data into the blockList.
//
{
    blockList.push_back( new geometryCacheBlockDVAData( tag, value, size ) );
}


void geometryCacheFile::storeCacheBlock( const MString& tag, const float* value, const uint& size )
//
// Description : ( private method )
//      Stores the specified data into the blockList.
//
{
    blockList.push_back( new geometryCacheBlockFVAData( tag, value, size ) );
}