/*******************************************************************
 *
 *  ttfile.c (extended version)                                 2.1
 *
 *    File I/O Component (body).
 *
 *  Copyright 1996, 1997 by
 *  David Turner, Robert Wilhelm, and Werner Lemberg
 *
 *  This file is part of the FreeType project, and may only be used
 *  modified and distributed under the terms of the FreeType project
 *  license, LICENSE.TXT.  By continuing to use, modify, or distribute
 *  this file you indicate that you have read the license and
 *  understand and accept it fully.
 *
 *  NOTES:
 *
 *   This implementation relies on the ANSI libc.  You may wish to
 *   modify it to get rid of libc and go straight to the your
 *   platform's stream routines.
 *
 *   The same source code can be used for thread-safe and re-entrant
 *   builds of the library.
 *
 *  Changes between 2.0 and 2.1 :
 *
 *  - it is now possible to close a stream's file handle explicitely
 *    through the new API "TT_Flush_Stream". This will simply close
 *    a stream's file handle (useful to save system resources when
 *    dealing with lots of opened fonts). Of course, the function
 *    "TT_Use_Stream" will automatically re-open a stream's handle if
 *    necessary.
 *
 *  - added "TT_Stream_Size" to replace "TT_File_Size" which wasn't
 *    used anyway. This one returns the size of any stream, even
 *    flushed one (when the previous TT_File_Size could only return
 *    the size of the current working stream). This is used by the
 *    new "Load_TrueTYpe_Any" function in the tables loader
 *
 ******************************************************************/

#include "ttconfig.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include "freetype.h"
#include "tttypes.h"
#include "tterror.h"
#include "ttengine.h"
#include "ttmutex.h"
#include "ttmemory.h"
#include "ttfile.h"     /* our prototypes */


/* For now, we don't define additional error messages in the core library */
/* to report open-on demand errors. Define these error as standard ones   */

#define  TT_Err_Could_Not_ReOpen_File  TT_Err_Could_Not_Open_File
#define  TT_Err_Could_Not_ReSeek_File  TT_Err_Could_Not_Open_File

  /* This definition is mandatory for each file component! */
  const TFileFrame  TT_Null_FileFrame = { NULL, 0, 0 };

/* It has proven useful to do some bounds checks during development phase. */
/* They should probably be undefined for speed reasons in a later release. */

#if 0
#define CHECK_FRAME( frame, n )                          \
  do {                                                   \
    if ( frame.cursor + n > frame.address + frame.size ) \
      Panic( "Frame boundary error!\n" );                \
  } while ( 0 )
#else
#define CHECK_FRAME( frame, n )  \
  do {                           \
  } while (0)                    
#endif

  /* Because a stream can be flushed, i.e. its file handle can be      */
  /* closed to save system resources, we must keep the stream's file   */
  /* pathname to be able to re-open it on demand when it is flushed    */

  struct  _TStream_Rec;
  typedef struct _TStream_Rec  TStream_Rec;
  typedef TStream_Rec*         PStream_Rec;

  struct  _TStream_Rec
  {
    Bool   opened;                     /* is the stream handle opened ?    */
    char*  name;                       /* the file's pathname              */
    Long   position;                   /* current position within the file */

    FILE*  file;                       /* file handle                      */
    Long   base;                       /* stream base in file              */
    Long   size;                       /* stream size in file              */
  };

  /* We support embedded TrueType files by allowing them to be       */
  /* inside any file, at any location, hence the 'base' argument.    */
  /* Note however that the current implementation does not allow you */
  /* to specify a 'base' index when opening a file.                  */
  /* (will come later)                                               */
  /* I still don't know if this will turn out useful ??   - DavidT   */

#define STREAM2REC( x )  ( (TStream_Rec*)HANDLE_Val( x ) )

  static  TT_Error  Stream_Activate  ( PStream_Rec  stream );
  static  TT_Error  Stream_Deactivate( PStream_Rec  stream );

 
#ifndef TT_CONFIG_REENTRANT

  /*******************************************************************/
  /*******************************************************************/
  /*******************************************************************/
  /****                                                           ****/
  /****  N O N   R E E N T R A N T   I M P L E M E N T A T I O N  ****/
  /****                                                           ****/
  /*******************************************************************/
  /*******************************************************************/
  /*******************************************************************/

  /* in non-rentrant builds, we allocate a single block where we'll  */
  /* place all the frames smaller than FRAME_CACHE_SIZE, rather than */
  /* allocating a new block on each access.  Bigger frames will be   */
  /* malloced normally in the heap.                                  */
  /*                                                                 */
  /* See TT_Access_Frame() and TT_Forget_Frame() for details.        */

#define FRAME_CACHE_SIZE  2048

  /* The TFile_Component structure holds all the data that was */
  /* previously declared static or global in this component.   */
  /*                                                           */
  /* It is accessible through the 'engine.file_component'      */
  /* variable in re-entrant builds, or directly through the    */
  /* static 'files' variable in other builds.                  */

  struct  _TFile_Component
  {
    TMutex          lock;        /* used by the thread-safe build only */
    unsigned char*  frame_cache; /* frame cache     */
    PStream_Rec     stream;      /* current stream  */
    TFileFrame      frame;       /* current frame   */
  };

  typedef struct _TFile_Component  TFile_Component;

  static TFile_Component  files;

#define CUR_Stream  files.stream
#define CUR_Frame   files.frame

#define STREAM_VARS  /* void */
#define STREAM_VAR   /* void */

/* The macro CUR_Stream denotes the current input stream.            */
/* Note that for the re-entrant version, the 'stream' name has been  */
/* chosen according to the macro STREAM_ARGS.                        */

/* The macro CUR_Frame denotes the current file frame.              */
/* Note that for the re-entrant version, the 'frame' name has been  */
/* chosen according to the macro FRAME_ARGS.                        */

/* The macro STREAM_VAR is used when calling public functions */
/* that need an 'optional' stream argument.                   */

/*******************************************************************
 *
 *  Function    :  TTFile_Init
 *
 *  Description :  Initializes the File component.
 *
 ******************************************************************/

 TT_Error  TTFile_Init( PEngine_Instance  engine )
 {
   TT_Error  error;

   MUTEX_Create( files.lock );
   files.stream = NULL;
   ZERO_Frame( files.frame );

   if ( ALLOC( files.frame_cache, FRAME_CACHE_SIZE ) )
     return error;

   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  TTFile_Done
 *
 *  Description :  Finalizes the File component.
 *
 ******************************************************************/

 TT_Error  TTFile_Done( PEngine_Instance  engine )
 {
   FREE( files.frame_cache );
   MUTEX_Destroy( files.lock );
   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    : TT_Use_Stream
 *
 *  Description : Copies or duplicates a given stream.
 *
 *  Input  :  org_stream   original stream
 *            stream       target stream (copy or duplicate)
 *
 *  Output :  Error code.
 *
 ******************************************************************/

  TT_Error  TT_Use_Stream( TT_Stream   org_stream,
                           TT_Stream*  stream )
  {
     MUTEX_Lock( files.lock );                /* lock file mutex    */

     *stream = org_stream;                    /* copy the stream    */
     files.stream = STREAM2REC(org_stream);   /* set current stream */

     Stream_Activate( files.stream );

     return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    : TT_Done_Stream
 *
 *  Description : Releases a given stream.
 *
 *  Input  :  stream  target stream
 *
 *  Output :  Error code.
 *
 ******************************************************************/

 TT_Error  TT_Done_Stream( TT_Stream*  stream )
 {
    HANDLE_Set( *stream, NULL );
    MUTEX_Release( files.lock );
    return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  TT_Access_Frame
 *
 *  Description :  Notifies the component that we're going to read
 *                 'size' bytes from the current file position.
 *                 This function should load/cache/map these bytes
 *                 so that they will be addressed by the GET_xxx
 *                 functions easily.
 *
 *  Input  :  size   number of bytes to access.
 *
 *  Output :  SUCCESS on success. FAILURE on error.
 *
 *  Notes:    The function fails if the byte range is not within the
 *            the file, or if there is not enough memory to cache
 *            the bytes properly (which usually means that `size' is
 *            too big in both cases).
 *
 ******************************************************************/

  TT_Error  TT_Access_Frame( STREAM_ARGS FRAME_ARGS int  size )
  {
    TT_Error  error;

    if ( CUR_Frame.address != NULL )
      return TT_Err_Nested_Frame_Access;

    if ( size <= FRAME_CACHE_SIZE )
    {
      /* use the cache */
      CUR_Frame.address = files.frame_cache;
      CUR_Frame.size    = FRAME_CACHE_SIZE;
    }
    else
    {
      if ( ALLOC( CUR_Frame.address, size ) )
        return error;
      CUR_Frame.size    = size;
    }

    error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
    if (error)
    {
      if ( size > FRAME_CACHE_SIZE )
        FREE( CUR_Frame.address );
      CUR_Frame.address = NULL;
      CUR_Frame.size    = 0;
    }

    CUR_Frame.cursor = CUR_Frame.address;
    return error;
  }


/*******************************************************************
 *
 *  Function    :  TT_Check_And_Access_Frame
 *
 *  Description :  Notifies the component that we're going to read
 *                 `size' bytes from the current file position.
 *                 This function should load/cache/map these bytes
 *                 so that they will be addressed by the GET_xxx
 *                 functions easily.
 *
 *  Input  :  size   number of bytes to access.
 *
 *  Output :  SUCCESS on success. FAILURE on error.
 *
 *  Notes:    The function truncates `size' if the byte range is not 
 *            within the file.
 *
 *            It will fail if there is not enough memory to cache
 *            the bytes properly (which usually means that `size' is
 *            too big).
 *
 *            It will fail if you make two consecutive calls
 *            to TT_Access_Frame(), without a TT_Forget_Frame() between
 *            them.
 *
 *            The only difference with TT_Access_Frame() is that we
 *            check that the frame is within the current file.  We
 *            otherwise truncate it.
 *
 ******************************************************************/

  TT_Error  TT_Check_And_Access_Frame( STREAM_ARGS FRAME_ARGS int  size )
  {
    TT_Error  error;
    Long      readBytes, requested;

    if ( CUR_Frame.address != NULL )
      return TT_Err_Nested_Frame_Access;

    if ( size <= FRAME_CACHE_SIZE )
    {
      /* use the cache */
      CUR_Frame.address = files.frame_cache;
      CUR_Frame.size    = FRAME_CACHE_SIZE;
    }
    else
    {
      if ( ALLOC( CUR_Frame.address, size ) )
        return error;
      CUR_Frame.size    = size;
    }

    requested = size;
    readBytes = CUR_Stream->size - TT_File_Pos( STREAM_VAR );
    if ( size > readBytes ) 
      size = readBytes;

    error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
    if (error)
    {
      if ( requested > FRAME_CACHE_SIZE )
        FREE( CUR_Frame.address );
      CUR_Frame.address = NULL;
      CUR_Frame.size    = 0;
    }

    CUR_Frame.cursor = CUR_Frame.address;
    return error;
  }


/*******************************************************************
 *
 *  Function    :  TT_Forget_Frame
 *
 *  Description :  Releases a cached frame after reading.
 *
 *  Input  :  None
 *
 *  Output :  SUCCESS on success. FAILURE on error.
 *
 ******************************************************************/

  TT_Error  TT_Forget_Frame( FRAME_ARG )
  {
    if ( CUR_Frame.address == NULL )
      return TT_Err_Nested_Frame_Access;

    if ( CUR_Frame.size > FRAME_CACHE_SIZE )
      FREE( CUR_Frame.address );

    ZERO_Frame( CUR_Frame );
    return TT_Err_Ok;
  }


#else /* REENTRANT */

  /*******************************************************************/
  /*******************************************************************/
  /*******************************************************************/
  /********                                                   ********/
  /********  R E E N T R A N T   I M P L E M E N T A T I O N  ********/
  /********                                                   ********/
  /*******************************************************************/
  /*******************************************************************/
  /*******************************************************************/

/* a simple macro to access the file component's data */
#define files  ( *((TFile_Component*)engine.file_component) )

#define CUR_Stream   STREAM2REC( stream )    /* re-entrant macros */
#define CUR_Frame    (*frame)

#define STREAM_VARS  stream,
#define STREAM_VAR   stream


/*******************************************************************
 *
 *  Function    :  TTFile_Init
 *
 *  Description :  Initializes the File component.
 *
 ******************************************************************/

 TT_Error  TTFile_Init( PEngine_Instance  engine )
 {
   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  TTFile_Done
 *
 *  Description :  Finalizes the File component.
 *
 ******************************************************************/

 TT_Error  TTFile_Done( PEngine_Instance  engine )
 {
   return TT_Err_Ok;
 }


/*******************************************************************
 *
 *  Function    :  TT_Use_Stream
 *
 *  Description :  Duplicates a stream for a new usage.
 *
 *  Input  :  input_stream   source stream to duplicate
 *            copy           address of target duplicate stream
 *
 *  Output :  error code.
 *            The target stream is set to NULL in case of failure.
 *
 ******************************************************************/

  TT_Error  TT_Use_Stream( TT_Stream   input_stream,
                           TT_Stream*  copy )
  {
    return TT_Open_Stream( copy, input_stream->name );
  }


/*******************************************************************
 *
 *  Function    : TT_Done_Stream
 *
 *  Description : Releases a given stream.
 *
 *  Input  :  stream  target stream
 *
 *  Output :  
 *
 ******************************************************************/

 TT_Error  TT_Done_Stream( TT_Stream*  stream )
 {
   return TT_Close_Stream( stream );
 }


/*******************************************************************
 *
 *  Function    :  TT_Access_Frame
 *
 *  Description :  Notifies the component that we're going to read
 *                 'size' bytes from the current file position.
 *                 This function should load/cache/map these bytes
 *                 so that they will be addressed by the GET_xxx
 *                 functions easily.
 *
 *  Input  :  size   number of bytes to access.
 *
 *  Output :  SUCCESS on success. FAILURE on error.
 *
 *  Notes:    The function fails if the byte range is not within the
 *            the file, or if there is not enough memory to cache
 *            the bytes properly (which usually means that `size' is
 *            too big in both cases).
 *
 ******************************************************************/

  TT_Error  TT_Access_Frame( STREAM_ARGS FRAME_ARGS int  size )
  {
    TT_Error  error;


    if ( CUR_Frame.address != NULL )
      return TT_Err_Nested_Frame_Access;

    if ( ALLOC( CUR_Frame.address, size ) )
      return error;
    CUR_Frame.size    = size;

    error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
    if ( error )
    {
      FREE( CUR_Frame.address );
      CUR_Frame.size    = 0;
    }

    CUR_Frame.cursor = CUR_Frame.address;
    return error;
  }


/*******************************************************************
 *
 *  Function    :  TT_Check_And_Access_Frame
 *
 *  Description :  Notifies the component that we're going to read
 *                 `size' bytes from the current file position.
 *                 This function should load/cache/map these bytes
 *                 so that they will be addressed by the GET_xxx
 *                 functions easily.
 *
 *  Input  :  size   number of bytes to access.
 *
 *  Output :  SUCCESS on success. FAILURE on error.
 *
 *  Notes:    The function truncates `size' if the byte range is not 
 *            within the file.
 *
 *            It will fail if there is not enough memory to cache
 *            the bytes properly (which usually means that `size' is
 *            too big).
 *
 *            It will fail if you make two consecutive calls
 *            to TT_Access_Frame(), without a TT_Forget_Frame() between
 *            them.
 *
 *            The only difference with TT_Access_Frame() is that we
 *            check that the frame is within the current file.  We
 *            otherwise truncate it.
 *
 ******************************************************************/

  TT_Error  TT_Check_And_Access_Frame( STREAM_ARGS FRAME_ARGS int  size )
  {
    TT_Error  error;
    Long      readBytes;


    if ( CUR_Frame.address != NULL )
      return TT_Err_Nested_Frame_Access;

    if ( ALLOC( CUR_Frame.address, size ) )
      return error;
    CUR_Frame.size    = size;

    readBytes = CUR_Stream->size - TT_File_Pos( STREAM_VAR );
    if ( size > readBytes ) 
      size = readBytes;

    error = TT_Read_File( STREAM_VARS (void*)CUR_Frame.address, size );
    if ( error )
    {
      FREE( CUR_Frame.address );
      CUR_Frame.size    = 0;
    }

    CUR_Frame.cursor = CUR_Frame.address;
    return error;
  }


/*******************************************************************
 *
 *  Function    :  TT_Forget_Frame
 *
 *  Description :  Releases a cached frame after reading.
 *
 *  Input  :  None
 *
 *  Output :  SUCCESS on success.  FAILURE on error.
 *
 ******************************************************************/

  TT_Error  TT_Forget_Frame( FRAME_ARG )
  {
    if ( CUR_Frame.address == NULL )
      return TT_Err_Nested_Frame_Access;

    FREE( CUR_Frame.address );
    ZERO_Frame( CUR_Frame );
    return TT_Err_Ok;
  }

#endif /* REENTRANT */


  /*******************************************************************/
  /*******************************************************************/
  /*******************************************************************/
  /***********                                             ***********/
  /***********  C O M M O N   I M P L E M E N T A T I O N  ***********/
  /***********                                             ***********/
  /*******************************************************************/
  /*******************************************************************/
  /*******************************************************************/

/*******************************************************************
 *
 *  Function    :  Stream_Activate
 *
 *  Description :  activates a stream, this will either :
 *                   - open a new file handle if the stream is closed
 *                   - move the stream to the head of the linked list
 *
 *  Input  :  stream   the stream to activate
 *
 *  Output :  error condition.   
 *
 *  Note   :  this function is also called with fresh new streams
 *            created by TT_Open_Stream. They have their 'size' 
 *            field set to -1.
 *
 ******************************************************************/

  static  TT_Error  Stream_Activate( PStream_Rec  stream )
  {
    if ( !stream->opened )
    {
      if ( !(stream->file = fopen( stream->name, "rb" )) )
        return TT_Err_Could_Not_ReOpen_File;

      stream->opened = TRUE;

      /* A newly created stream has a size field of -1 */
      if ( stream->size < 0 )
      {
        fseek( stream->file, 0, SEEK_END );
        stream->size = ftell( stream->file );
        fseek( stream->file, 0, SEEK_SET );
      }

      /* Reset cursor in file */
      if ( stream->position )
      {
        if ( fseek( stream->file, stream->position, SEEK_SET ) != 0 )
        {
          /* error during seek */
          fclose( stream->file );
          stream->opened = FALSE;
          return TT_Err_Could_Not_ReSeek_File;
        }
      }
    }
    return TT_Err_Ok;
  }

/*******************************************************************
 *
 *  Function    :  Stream_DeActivate
 *
 *  Description :  deactivates a stream, this will :
 *                   - close its file handle if it was opened        
 *                   - remove it from the opened list if necessary 
 *
 *  Input  :  stream   the stream to deactivate
 *
 *  Output :  Error condition
 *
 *  Note   :  the function is called whenever a stream is deleted
 *            (_not_ when a stream handle's is closed due to an 
 *             activation). However, the stream record isn't
 *            destroyed by it..
 *
 ******************************************************************/

  static  TT_Error  Stream_Deactivate( PStream_Rec  stream )
  {
    if ( stream->opened )
    {
      /* Save its current position within the file */
      stream->position = ftell( stream->file );
      fclose( stream->file );
      stream->file   = 0;
      stream->opened = FALSE;
    }

    return TT_Err_Ok;
  }

/*******************************************************************
 *
 *  Function    :  TT_Stream_Size
 *
 *  Description :  Returns the length of a given stream, even if it  
 *                 is flushed.
 *
 *  Input  :  stream     the stream
 *
 *  Output :  lenght of stream in bytes.            
 *
 ******************************************************************/

  Long  TT_Stream_Size( TT_Stream  stream )
  {
    PStream_Rec  rec = STREAM2REC( stream );
    if (rec)
      return rec->size;
    else
      return 0;  /* invalid stream - return 0 */
  }

/*******************************************************************
 *
 *  Function    :  TT_Open_Stream
 *
 *  Description :  Opens the font file and saves the total file size.
 *
 *  Input  :  error          address of stream's error variable
 *                           (re-entrant build only)
 *            filepathname   pathname of the file to open
 *            stream         address of target TT_Stream structure
 *
 *  Output :  SUCCESS on sucess, FAILURE on error.
 *            The target stream is set to -1 in case of failure.
 *
 ******************************************************************/

  TT_Error  TT_Open_Stream( const char*  filepathname, 
                            TT_Stream*   stream )
  {
    int          len;
    TT_Error     error;
    PStream_Rec  stream_rec;

    if ( ALLOC( *stream, sizeof ( TStream_Rec ) ) )
      return error;

    stream_rec = STREAM2REC( *stream );

    stream_rec->file     = NULL;
    stream_rec->size     = -1;
    stream_rec->base     = 0;
    stream_rec->opened   = FALSE;
    stream_rec->position = 0;

    len = strlen(filepathname)+1;
    if ( ALLOC( stream_rec->name, len ) )
      goto Fail;

    strncpy( stream_rec->name, filepathname, len );

    error = Stream_Activate( stream_rec );
    if (error)
      goto Fail_Activate;

#ifndef TT_CONFIG_REENTRANT
    CUR_Stream = stream_rec;
#endif

    return TT_Err_Ok;

  Fail_Activate:
    FREE( stream_rec->name );
  Fail:
    FREE( stream_rec );
    return error;
  }


/*******************************************************************
 *
 *  Function    : TT_Close_Stream
 *
 *  Description : Closes a stream.
 *
 *  Input  :  stream         address of target TT_Stream structure
 *
 *  Output :  SUCCESS (always).
 *
 ******************************************************************/

  TT_Error  TT_Close_Stream( TT_Stream*  stream )
  {
    PStream_Rec  rec = STREAM2REC( *stream );

    Stream_Deactivate( rec );
    FREE( rec->name );
    FREE( rec );

    HANDLE_Set( *stream, NULL );
    return TT_Err_Ok;
  }

/*******************************************************************
 *
 *  Function    : TT_Flush_Stream
 *
 *  Description : Flushes a stream, i.e. closes its file handle
 *
 *  Input  :  stream         address of target TT_Stream structure
 *
 *  Output :  Error code
 *
 *  NOTE : Never flush the current opened stream. This means that
 *         you should _never_ call this function between a
 *         TT_Use_Stream and a TT_Done_Stream !!
 *
 ******************************************************************/

  TT_Error  TT_Flush_Stream( TT_Stream*  stream )
  {
    PStream_Rec  rec = STREAM2REC( *stream );

    if (rec)
    {
      Stream_Deactivate(rec);
      return TT_Err_Ok;
    }
    else
      return TT_Err_Invalid_Argument;
  }

/*******************************************************************
 *
 *  Function    : TT_Seek_File
 *
 *  Description : Seeks the file cursor to a different position.
 *
 *  Input  :  position     new position in file
 *
 *  Output :  SUCCESS on success.  FAILURE if out of range.
 *
 ******************************************************************/

  TT_Error  TT_Seek_File( STREAM_ARGS long  position )
  {
    position += CUR_Stream->base;

    if ( fseek( CUR_Stream->file, position, SEEK_SET ) )
      return TT_Err_Invalid_File_Offset;

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    : TT_Skip_File
 *
 *  Description : Skips forward the file cursor.
 *
 *  Input  :  distance    number of bytes to skip
 *
 *  Output :  see TT_Seek_File()
 *
 ******************************************************************/

  TT_Error  TT_Skip_File( STREAM_ARGS long  distance )
  {
    return TT_Seek_File( STREAM_VARS ftell( CUR_Stream->file ) -
                                     CUR_Stream->base + distance );
  }


/*******************************************************************
 *
 *  Function    : TT_Read_File
 *
 *  Description : Reads a chunk of the file and copies it to memory.
 *
 *  Input  :  buffer    target buffer
 *            count     length in bytes to read
 *
 *  Output :  SUCCESS on success.  FAILURE if out of range.
 *
 ******************************************************************/

  TT_Error  TT_Read_File( STREAM_ARGS void*  buffer, long  count )
  {
    if ( fread( buffer, 1, count, CUR_Stream->file ) != (unsigned long)count )
      return TT_Err_Invalid_File_Read;

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    : TT_Read_At_File
 *
 *  Description : Reads file at a specified position.
 *
 *  Input  :  position  position to seek to before read
 *            buffer    target buffer
 *            count     number of bytes to read
 *
 *  Output :  SUCCESS on success.  FAILURE if error.
 *
 ******************************************************************/

  TT_Error  TT_Read_At_File( STREAM_ARGS long   position, 
                                         void*  buffer, 
                                         long   count )
  {
    TT_Error  error;


    if ( ( error = TT_Seek_File( STREAM_VARS position ) )      || 
         ( error = TT_Read_File( STREAM_VARS buffer, count ) ) )
      return error;

    return TT_Err_Ok;
  }


/*******************************************************************
 *
 *  Function    :  TT_File_Pos
 *
 *  Description :  Returns current file seek pointer.
 *
 *  Input  :  none
 *
 *  Output :  current file position
 *
 ******************************************************************/

  Long  TT_File_Pos( STREAM_ARG )
  {
    return ftell( CUR_Stream->file ) - CUR_Stream->base;
  }


/*******************************************************************
 *
 *  Function    :  GET_Byte
 *
 *  Description :  Extracts a byte from the current file frame.
 *
 *  Input  :  None or current frame
 *
 *  Output :  Extracted Byte.
 *
 ******************************************************************/

  unsigned char  TT_Get_Byte( FRAME_ARG )
  {
    CHECK_FRAME( CUR_Frame, 1 );

    return (unsigned char)(*CUR_Frame.cursor++);
  }


/*******************************************************************
 *
 *  Function    :  GET_Char
 *
 *  Description :  Extracts a signed byte from the current file frame.
 *
 *  Input  :  None or current frame
 *
 *  Output :  Extracted char.
 *
 ******************************************************************/

  char  TT_Get_Char( FRAME_ARG )
  {
    CHECK_FRAME( CUR_Frame, 1 );

    return (char)(*CUR_Frame.cursor++);
  }


/*******************************************************************
 *
 *  Function    :  GET_Short
 *
 *  Description :  Extracts a short from the current file frame.
 *
 *  Input  :  None or current frame
 *
 *  Output :  Extracted short.
 *
 ******************************************************************/

  short  TT_Get_Short( FRAME_ARG )
  {
    short  getshort;


    CHECK_FRAME( CUR_Frame, 2 );

    getshort = ((short)CUR_Frame.cursor[0] << 8) |
                (short)CUR_Frame.cursor[1];

    CUR_Frame.cursor += 2;

    return getshort;
  }


/*******************************************************************
 *
 *  Function    :  GET_UShort
 *
 *  Description :  Extracts an unsigned short from the frame.
 *
 *  Input  :  None or current frame
 *
 *  Output :  Extracted ushort.
 *
 ******************************************************************/

  unsigned short  TT_Get_UShort( FRAME_ARG )
  {
    unsigned short  getshort;


    CHECK_FRAME( CUR_Frame, 2 );

    getshort = ((unsigned short)CUR_Frame.cursor[0] << 8) |
                (unsigned short)CUR_Frame.cursor[1];

    CUR_Frame.cursor += 2;

    return getshort;
  }


/*******************************************************************
 *
 *  Function    :  GET_Long
 *
 *  Description :  Extracts a long from the frame.
 *
 *  Input  :  None or current frame
 *
 *  Output :  Extracted long.
 *
 ******************************************************************/

  long  TT_Get_Long( FRAME_ARG )
  {
    long  getlong;


    CHECK_FRAME( CUR_Frame, 4 );

    getlong = ((long)CUR_Frame.cursor[0] << 24) |
              ((long)CUR_Frame.cursor[1] << 16) |
              ((long)CUR_Frame.cursor[2] << 8 ) |
               (long)CUR_Frame.cursor[3];

    CUR_Frame.cursor += 4;

    return getlong;
  }


/*******************************************************************
 *
 *  Function    :  GET_ULong
 *
 *  Description :  Extracts an unsigned long from the frame.
 *
 *  Input  :  None
 *
 *  Output :  Extracted ulong.
 *
 ******************************************************************/

  unsigned long  TT_Get_ULong( FRAME_ARG )
  {
    unsigned long  getlong;


    CHECK_FRAME( CUR_Frame, 4 );

    getlong = ( ((unsigned long)CUR_Frame.cursor[0] << 24) |
                ((unsigned long)CUR_Frame.cursor[1] << 16) |
                ((unsigned long)CUR_Frame.cursor[2] << 8 ) |
                 (unsigned long)CUR_Frame.cursor[3] );

    CUR_Frame.cursor += 4;

    return getlong;
  }


/* END */
