(root)/
freetype-2.13.2/
src/
base/
ftstream.c
       1  /****************************************************************************
       2   *
       3   * ftstream.c
       4   *
       5   *   I/O stream support (body).
       6   *
       7   * Copyright (C) 2000-2023 by
       8   * David Turner, Robert Wilhelm, and Werner Lemberg.
       9   *
      10   * This file is part of the FreeType project, and may only be used,
      11   * modified, and distributed under the terms of the FreeType project
      12   * license, LICENSE.TXT.  By continuing to use, modify, or distribute
      13   * this file you indicate that you have read the license and
      14   * understand and accept it fully.
      15   *
      16   */
      17  
      18  
      19  #include <freetype/internal/ftstream.h>
      20  #include <freetype/internal/ftdebug.h>
      21  
      22  
      23    /**************************************************************************
      24     *
      25     * The macro FT_COMPONENT is used in trace mode.  It is an implicit
      26     * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
      27     * messages during execution.
      28     */
      29  #undef  FT_COMPONENT
      30  #define FT_COMPONENT  stream
      31  
      32  
      33    FT_BASE_DEF( void )
      34    FT_Stream_OpenMemory( FT_Stream       stream,
      35                          const FT_Byte*  base,
      36                          FT_ULong        size )
      37    {
      38      stream->base   = (FT_Byte*) base;
      39      stream->size   = size;
      40      stream->pos    = 0;
      41      stream->cursor = NULL;
      42      stream->read   = NULL;
      43      stream->close  = NULL;
      44    }
      45  
      46  
      47    FT_BASE_DEF( void )
      48    FT_Stream_Close( FT_Stream  stream )
      49    {
      50      if ( stream && stream->close )
      51        stream->close( stream );
      52    }
      53  
      54  
      55    FT_BASE_DEF( FT_Error )
      56    FT_Stream_Seek( FT_Stream  stream,
      57                    FT_ULong   pos )
      58    {
      59      FT_Error  error = FT_Err_Ok;
      60  
      61  
      62      if ( stream->read )
      63      {
      64        if ( stream->read( stream, pos, NULL, 0 ) )
      65        {
      66          FT_ERROR(( "FT_Stream_Seek:"
      67                     " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
      68                     pos, stream->size ));
      69  
      70          error = FT_THROW( Invalid_Stream_Operation );
      71        }
      72      }
      73      /* note that seeking to the first position after the file is valid */
      74      else if ( pos > stream->size )
      75      {
      76        FT_ERROR(( "FT_Stream_Seek:"
      77                   " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
      78                   pos, stream->size ));
      79  
      80        error = FT_THROW( Invalid_Stream_Operation );
      81      }
      82  
      83      if ( !error )
      84        stream->pos = pos;
      85  
      86      return error;
      87    }
      88  
      89  
      90    FT_BASE_DEF( FT_Error )
      91    FT_Stream_Skip( FT_Stream  stream,
      92                    FT_Long    distance )
      93    {
      94      if ( distance < 0 )
      95        return FT_THROW( Invalid_Stream_Operation );
      96  
      97      return FT_Stream_Seek( stream, stream->pos + (FT_ULong)distance );
      98    }
      99  
     100  
     101    FT_BASE_DEF( FT_ULong )
     102    FT_Stream_Pos( FT_Stream  stream )
     103    {
     104      return stream->pos;
     105    }
     106  
     107  
     108    FT_BASE_DEF( FT_Error )
     109    FT_Stream_Read( FT_Stream  stream,
     110                    FT_Byte*   buffer,
     111                    FT_ULong   count )
     112    {
     113      return FT_Stream_ReadAt( stream, stream->pos, buffer, count );
     114    }
     115  
     116  
     117    FT_BASE_DEF( FT_Error )
     118    FT_Stream_ReadAt( FT_Stream  stream,
     119                      FT_ULong   pos,
     120                      FT_Byte*   buffer,
     121                      FT_ULong   count )
     122    {
     123      FT_Error  error = FT_Err_Ok;
     124      FT_ULong  read_bytes;
     125  
     126  
     127      if ( pos >= stream->size )
     128      {
     129        FT_ERROR(( "FT_Stream_ReadAt:"
     130                   " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     131                   pos, stream->size ));
     132  
     133        return FT_THROW( Invalid_Stream_Operation );
     134      }
     135  
     136      if ( stream->read )
     137        read_bytes = stream->read( stream, pos, buffer, count );
     138      else
     139      {
     140        read_bytes = stream->size - pos;
     141        if ( read_bytes > count )
     142          read_bytes = count;
     143  
     144        /* Allow "reading" zero bytes without UB even if buffer is NULL */
     145        if ( count )
     146          FT_MEM_COPY( buffer, stream->base + pos, read_bytes );
     147      }
     148  
     149      stream->pos = pos + read_bytes;
     150  
     151      if ( read_bytes < count )
     152      {
     153        FT_ERROR(( "FT_Stream_ReadAt:"
     154                   " invalid read; expected %lu bytes, got %lu\n",
     155                   count, read_bytes ));
     156  
     157        error = FT_THROW( Invalid_Stream_Operation );
     158      }
     159  
     160      return error;
     161    }
     162  
     163  
     164    FT_BASE_DEF( FT_ULong )
     165    FT_Stream_TryRead( FT_Stream  stream,
     166                       FT_Byte*   buffer,
     167                       FT_ULong   count )
     168    {
     169      FT_ULong  read_bytes = 0;
     170  
     171  
     172      if ( stream->pos >= stream->size )
     173        goto Exit;
     174  
     175      if ( stream->read )
     176        read_bytes = stream->read( stream, stream->pos, buffer, count );
     177      else
     178      {
     179        read_bytes = stream->size - stream->pos;
     180        if ( read_bytes > count )
     181          read_bytes = count;
     182  
     183        /* Allow "reading" zero bytes without UB even if buffer is NULL */
     184        if ( count )
     185          FT_MEM_COPY( buffer, stream->base + stream->pos, read_bytes );
     186      }
     187  
     188      stream->pos += read_bytes;
     189  
     190    Exit:
     191      return read_bytes;
     192    }
     193  
     194  
     195    FT_BASE_DEF( FT_Error )
     196    FT_Stream_ExtractFrame( FT_Stream  stream,
     197                            FT_ULong   count,
     198                            FT_Byte**  pbytes )
     199    {
     200      FT_Error  error;
     201  
     202  
     203      error = FT_Stream_EnterFrame( stream, count );
     204      if ( !error )
     205      {
     206        *pbytes = (FT_Byte*)stream->cursor;
     207  
     208        /* equivalent to FT_Stream_ExitFrame(), with no memory block release */
     209        stream->cursor = NULL;
     210        stream->limit  = NULL;
     211      }
     212  
     213      return error;
     214    }
     215  
     216  
     217    FT_BASE_DEF( void )
     218    FT_Stream_ReleaseFrame( FT_Stream  stream,
     219                            FT_Byte**  pbytes )
     220    {
     221      if ( stream && stream->read )
     222      {
     223        FT_Memory  memory = stream->memory;
     224  
     225  
     226  #ifdef FT_DEBUG_MEMORY
     227        ft_mem_free( memory, *pbytes );
     228  #else
     229        FT_FREE( *pbytes );
     230  #endif
     231      }
     232  
     233      *pbytes = NULL;
     234    }
     235  
     236  
     237    FT_BASE_DEF( FT_Error )
     238    FT_Stream_EnterFrame( FT_Stream  stream,
     239                          FT_ULong   count )
     240    {
     241      FT_Error  error = FT_Err_Ok;
     242      FT_ULong  read_bytes;
     243  
     244  
     245      FT_TRACE7(( "FT_Stream_EnterFrame: %ld bytes\n", count ));
     246  
     247      /* check for nested frame access */
     248      FT_ASSERT( stream && stream->cursor == 0 );
     249  
     250      if ( stream->read )
     251      {
     252        /* allocate the frame in memory */
     253        FT_Memory  memory = stream->memory;
     254  
     255  
     256        /* simple sanity check */
     257        if ( count > stream->size )
     258        {
     259          FT_ERROR(( "FT_Stream_EnterFrame:"
     260                     " frame size (%lu) larger than stream size (%lu)\n",
     261                     count, stream->size ));
     262  
     263          error = FT_THROW( Invalid_Stream_Operation );
     264          goto Exit;
     265        }
     266  
     267  #ifdef FT_DEBUG_MEMORY
     268        /* assume `ft_debug_file_` and `ft_debug_lineno_` are already set */
     269        stream->base = (unsigned char*)ft_mem_qalloc( memory,
     270                                                      (FT_Long)count,
     271                                                      &error );
     272        if ( error )
     273          goto Exit;
     274  #else
     275        if ( FT_QALLOC( stream->base, count ) )
     276          goto Exit;
     277  #endif
     278        /* read it */
     279        read_bytes = stream->read( stream, stream->pos,
     280                                   stream->base, count );
     281        if ( read_bytes < count )
     282        {
     283          FT_ERROR(( "FT_Stream_EnterFrame:"
     284                     " invalid read; expected %lu bytes, got %lu\n",
     285                     count, read_bytes ));
     286  
     287          FT_FREE( stream->base );
     288          error = FT_THROW( Invalid_Stream_Operation );
     289        }
     290  
     291        stream->cursor = stream->base;
     292        stream->limit  = FT_OFFSET( stream->cursor, count );
     293        stream->pos   += read_bytes;
     294      }
     295      else
     296      {
     297        /* check current and new position */
     298        if ( stream->pos >= stream->size        ||
     299             stream->size - stream->pos < count )
     300        {
     301          FT_ERROR(( "FT_Stream_EnterFrame:"
     302                     " invalid i/o; pos = 0x%lx, count = %lu, size = 0x%lx\n",
     303                     stream->pos, count, stream->size ));
     304  
     305          error = FT_THROW( Invalid_Stream_Operation );
     306          goto Exit;
     307        }
     308  
     309        /* set cursor */
     310        stream->cursor = stream->base + stream->pos;
     311        stream->limit  = stream->cursor + count;
     312        stream->pos   += count;
     313      }
     314  
     315    Exit:
     316      return error;
     317    }
     318  
     319  
     320    FT_BASE_DEF( void )
     321    FT_Stream_ExitFrame( FT_Stream  stream )
     322    {
     323      /* IMPORTANT: The assertion stream->cursor != 0 was removed, given    */
     324      /*            that it is possible to access a frame of length 0 in    */
     325      /*            some weird fonts (usually, when accessing an array of   */
     326      /*            0 records, like in some strange kern tables).           */
     327      /*                                                                    */
     328      /*  In this case, the loader code handles the 0-length table          */
     329      /*  gracefully; however, stream.cursor is really set to 0 by the      */
     330      /*  FT_Stream_EnterFrame() call, and this is not an error.            */
     331  
     332      FT_TRACE7(( "FT_Stream_ExitFrame\n" ));
     333  
     334      FT_ASSERT( stream );
     335  
     336      if ( stream->read )
     337      {
     338        FT_Memory  memory = stream->memory;
     339  
     340  
     341  #ifdef FT_DEBUG_MEMORY
     342        ft_mem_free( memory, stream->base );
     343        stream->base = NULL;
     344  #else
     345        FT_FREE( stream->base );
     346  #endif
     347      }
     348  
     349      stream->cursor = NULL;
     350      stream->limit  = NULL;
     351    }
     352  
     353  
     354    FT_BASE_DEF( FT_Byte )
     355    FT_Stream_GetByte( FT_Stream  stream )
     356    {
     357      FT_Byte  result;
     358  
     359  
     360      FT_ASSERT( stream && stream->cursor );
     361  
     362      result = 0;
     363      if ( stream->cursor < stream->limit )
     364        result = *stream->cursor++;
     365  
     366      return result;
     367    }
     368  
     369  
     370    FT_BASE_DEF( FT_UInt16 )
     371    FT_Stream_GetUShort( FT_Stream  stream )
     372    {
     373      FT_Byte*   p;
     374      FT_UInt16  result;
     375  
     376  
     377      FT_ASSERT( stream && stream->cursor );
     378  
     379      result         = 0;
     380      p              = stream->cursor;
     381      if ( p + 1 < stream->limit )
     382        result       = FT_NEXT_USHORT( p );
     383      stream->cursor = p;
     384  
     385      return result;
     386    }
     387  
     388  
     389    FT_BASE_DEF( FT_UInt16 )
     390    FT_Stream_GetUShortLE( FT_Stream  stream )
     391    {
     392      FT_Byte*   p;
     393      FT_UInt16  result;
     394  
     395  
     396      FT_ASSERT( stream && stream->cursor );
     397  
     398      result         = 0;
     399      p              = stream->cursor;
     400      if ( p + 1 < stream->limit )
     401        result       = FT_NEXT_USHORT_LE( p );
     402      stream->cursor = p;
     403  
     404      return result;
     405    }
     406  
     407  
     408    FT_BASE_DEF( FT_UInt32 )
     409    FT_Stream_GetUOffset( FT_Stream  stream )
     410    {
     411      FT_Byte*  p;
     412      FT_UInt32 result;
     413  
     414  
     415      FT_ASSERT( stream && stream->cursor );
     416  
     417      result         = 0;
     418      p              = stream->cursor;
     419      if ( p + 2 < stream->limit )
     420        result       = FT_NEXT_UOFF3( p );
     421      stream->cursor = p;
     422      return result;
     423    }
     424  
     425  
     426    FT_BASE_DEF( FT_UInt32 )
     427    FT_Stream_GetULong( FT_Stream  stream )
     428    {
     429      FT_Byte*  p;
     430      FT_UInt32 result;
     431  
     432  
     433      FT_ASSERT( stream && stream->cursor );
     434  
     435      result         = 0;
     436      p              = stream->cursor;
     437      if ( p + 3 < stream->limit )
     438        result       = FT_NEXT_ULONG( p );
     439      stream->cursor = p;
     440      return result;
     441    }
     442  
     443  
     444    FT_BASE_DEF( FT_UInt32 )
     445    FT_Stream_GetULongLE( FT_Stream  stream )
     446    {
     447      FT_Byte*  p;
     448      FT_UInt32 result;
     449  
     450  
     451      FT_ASSERT( stream && stream->cursor );
     452  
     453      result         = 0;
     454      p              = stream->cursor;
     455      if ( p + 3 < stream->limit )
     456        result       = FT_NEXT_ULONG_LE( p );
     457      stream->cursor = p;
     458      return result;
     459    }
     460  
     461  
     462    FT_BASE_DEF( FT_Byte )
     463    FT_Stream_ReadByte( FT_Stream  stream,
     464                        FT_Error*  error )
     465    {
     466      FT_Byte  result = 0;
     467  
     468  
     469      FT_ASSERT( stream );
     470  
     471      if ( stream->pos < stream->size )
     472      {
     473        if ( stream->read )
     474        {
     475          if ( stream->read( stream, stream->pos, &result, 1L ) != 1L )
     476            goto Fail;
     477        }
     478        else
     479          result = stream->base[stream->pos];
     480      }
     481      else
     482        goto Fail;
     483  
     484      stream->pos++;
     485  
     486      *error = FT_Err_Ok;
     487  
     488      return result;
     489  
     490    Fail:
     491      *error = FT_THROW( Invalid_Stream_Operation );
     492      FT_ERROR(( "FT_Stream_ReadByte:"
     493                 " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     494                 stream->pos, stream->size ));
     495  
     496      return result;
     497    }
     498  
     499  
     500    FT_BASE_DEF( FT_UInt16 )
     501    FT_Stream_ReadUShort( FT_Stream  stream,
     502                          FT_Error*  error )
     503    {
     504      FT_Byte    reads[2];
     505      FT_Byte*   p;
     506      FT_UInt16  result = 0;
     507  
     508  
     509      FT_ASSERT( stream );
     510  
     511      if ( stream->pos + 1 < stream->size )
     512      {
     513        if ( stream->read )
     514        {
     515          if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
     516            goto Fail;
     517  
     518          p = reads;
     519        }
     520        else
     521          p = stream->base + stream->pos;
     522  
     523        if ( p )
     524          result = FT_NEXT_USHORT( p );
     525      }
     526      else
     527        goto Fail;
     528  
     529      stream->pos += 2;
     530  
     531      *error = FT_Err_Ok;
     532  
     533      return result;
     534  
     535    Fail:
     536      *error = FT_THROW( Invalid_Stream_Operation );
     537      FT_ERROR(( "FT_Stream_ReadUShort:"
     538                 " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     539                 stream->pos, stream->size ));
     540  
     541      return result;
     542    }
     543  
     544  
     545    FT_BASE_DEF( FT_UInt16 )
     546    FT_Stream_ReadUShortLE( FT_Stream  stream,
     547                            FT_Error*  error )
     548    {
     549      FT_Byte    reads[2];
     550      FT_Byte*   p;
     551      FT_UInt16  result = 0;
     552  
     553  
     554      FT_ASSERT( stream );
     555  
     556      if ( stream->pos + 1 < stream->size )
     557      {
     558        if ( stream->read )
     559        {
     560          if ( stream->read( stream, stream->pos, reads, 2L ) != 2L )
     561            goto Fail;
     562  
     563          p = reads;
     564        }
     565        else
     566          p = stream->base + stream->pos;
     567  
     568        if ( p )
     569          result = FT_NEXT_USHORT_LE( p );
     570      }
     571      else
     572        goto Fail;
     573  
     574      stream->pos += 2;
     575  
     576      *error = FT_Err_Ok;
     577  
     578      return result;
     579  
     580    Fail:
     581      *error = FT_THROW( Invalid_Stream_Operation );
     582      FT_ERROR(( "FT_Stream_ReadUShortLE:"
     583                 " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     584                 stream->pos, stream->size ));
     585  
     586      return result;
     587    }
     588  
     589  
     590    FT_BASE_DEF( FT_ULong )
     591    FT_Stream_ReadUOffset( FT_Stream  stream,
     592                           FT_Error*  error )
     593    {
     594      FT_Byte   reads[3];
     595      FT_Byte*  p;
     596      FT_ULong  result = 0;
     597  
     598  
     599      FT_ASSERT( stream );
     600  
     601      if ( stream->pos + 2 < stream->size )
     602      {
     603        if ( stream->read )
     604        {
     605          if (stream->read( stream, stream->pos, reads, 3L ) != 3L )
     606            goto Fail;
     607  
     608          p = reads;
     609        }
     610        else
     611          p = stream->base + stream->pos;
     612  
     613        if ( p )
     614          result = FT_NEXT_UOFF3( p );
     615      }
     616      else
     617        goto Fail;
     618  
     619      stream->pos += 3;
     620  
     621      *error = FT_Err_Ok;
     622  
     623      return result;
     624  
     625    Fail:
     626      *error = FT_THROW( Invalid_Stream_Operation );
     627      FT_ERROR(( "FT_Stream_ReadUOffset:"
     628                 " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     629                 stream->pos, stream->size ));
     630  
     631      return result;
     632    }
     633  
     634  
     635    FT_BASE_DEF( FT_UInt32 )
     636    FT_Stream_ReadULong( FT_Stream  stream,
     637                         FT_Error*  error )
     638    {
     639      FT_Byte   reads[4];
     640      FT_Byte*  p;
     641      FT_UInt32 result = 0;
     642  
     643  
     644      FT_ASSERT( stream );
     645  
     646      if ( stream->pos + 3 < stream->size )
     647      {
     648        if ( stream->read )
     649        {
     650          if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
     651            goto Fail;
     652  
     653          p = reads;
     654        }
     655        else
     656          p = stream->base + stream->pos;
     657  
     658        if ( p )
     659          result = FT_NEXT_ULONG( p );
     660      }
     661      else
     662        goto Fail;
     663  
     664      stream->pos += 4;
     665  
     666      *error = FT_Err_Ok;
     667  
     668      return result;
     669  
     670    Fail:
     671      *error = FT_THROW( Invalid_Stream_Operation );
     672      FT_ERROR(( "FT_Stream_ReadULong:"
     673                 " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     674                 stream->pos, stream->size ));
     675  
     676      return result;
     677    }
     678  
     679  
     680    FT_BASE_DEF( FT_UInt32 )
     681    FT_Stream_ReadULongLE( FT_Stream  stream,
     682                           FT_Error*  error )
     683    {
     684      FT_Byte   reads[4];
     685      FT_Byte*  p;
     686      FT_UInt32 result = 0;
     687  
     688  
     689      FT_ASSERT( stream );
     690  
     691      if ( stream->pos + 3 < stream->size )
     692      {
     693        if ( stream->read )
     694        {
     695          if ( stream->read( stream, stream->pos, reads, 4L ) != 4L )
     696            goto Fail;
     697  
     698          p = reads;
     699        }
     700        else
     701          p = stream->base + stream->pos;
     702  
     703        if ( p )
     704          result = FT_NEXT_ULONG_LE( p );
     705      }
     706      else
     707        goto Fail;
     708  
     709      stream->pos += 4;
     710  
     711      *error = FT_Err_Ok;
     712  
     713      return result;
     714  
     715    Fail:
     716      *error = FT_THROW( Invalid_Stream_Operation );
     717      FT_ERROR(( "FT_Stream_ReadULongLE:"
     718                 " invalid i/o; pos = 0x%lx, size = 0x%lx\n",
     719                 stream->pos, stream->size ));
     720  
     721      return result;
     722    }
     723  
     724  
     725    FT_BASE_DEF( FT_Error )
     726    FT_Stream_ReadFields( FT_Stream              stream,
     727                          const FT_Frame_Field*  fields,
     728                          void*                  structure )
     729    {
     730      FT_Error  error;
     731      FT_Bool   frame_accessed = 0;
     732      FT_Byte*  cursor;
     733  
     734  
     735      if ( !fields )
     736        return FT_THROW( Invalid_Argument );
     737  
     738      if ( !stream )
     739        return FT_THROW( Invalid_Stream_Handle );
     740  
     741      cursor = stream->cursor;
     742  
     743      error = FT_Err_Ok;
     744      do
     745      {
     746        FT_ULong  value;
     747        FT_Int    sign_shift;
     748        FT_Byte*  p;
     749  
     750  
     751        switch ( fields->value )
     752        {
     753        case ft_frame_start:  /* access a new frame */
     754          error = FT_Stream_EnterFrame( stream, fields->offset );
     755          if ( error )
     756            goto Exit;
     757  
     758          frame_accessed = 1;
     759          cursor         = stream->cursor;
     760          fields++;
     761          continue;  /* loop! */
     762  
     763        case ft_frame_bytes:  /* read a byte sequence */
     764        case ft_frame_skip:   /* skip some bytes      */
     765          {
     766            FT_UInt  len = fields->size;
     767  
     768  
     769            if ( cursor + len > stream->limit )
     770            {
     771              error = FT_THROW( Invalid_Stream_Operation );
     772              goto Exit;
     773            }
     774  
     775            if ( fields->value == ft_frame_bytes )
     776            {
     777              p = (FT_Byte*)structure + fields->offset;
     778              FT_MEM_COPY( p, cursor, len );
     779            }
     780            cursor += len;
     781            fields++;
     782            continue;
     783          }
     784  
     785        case ft_frame_byte:
     786        case ft_frame_schar:  /* read a single byte */
     787          value = FT_NEXT_BYTE( cursor );
     788          sign_shift = 24;
     789          break;
     790  
     791        case ft_frame_short_be:
     792        case ft_frame_ushort_be:  /* read a 2-byte big-endian short */
     793          value = FT_NEXT_USHORT( cursor );
     794          sign_shift = 16;
     795          break;
     796  
     797        case ft_frame_short_le:
     798        case ft_frame_ushort_le:  /* read a 2-byte little-endian short */
     799          value = FT_NEXT_USHORT_LE( cursor );
     800          sign_shift = 16;
     801          break;
     802  
     803        case ft_frame_long_be:
     804        case ft_frame_ulong_be:  /* read a 4-byte big-endian long */
     805          value = FT_NEXT_ULONG( cursor );
     806          sign_shift = 0;
     807          break;
     808  
     809        case ft_frame_long_le:
     810        case ft_frame_ulong_le:  /* read a 4-byte little-endian long */
     811          value = FT_NEXT_ULONG_LE( cursor );
     812          sign_shift = 0;
     813          break;
     814  
     815        case ft_frame_off3_be:
     816        case ft_frame_uoff3_be:  /* read a 3-byte big-endian long */
     817          value = FT_NEXT_UOFF3( cursor );
     818          sign_shift = 8;
     819          break;
     820  
     821        case ft_frame_off3_le:
     822        case ft_frame_uoff3_le:  /* read a 3-byte little-endian long */
     823          value = FT_NEXT_UOFF3_LE( cursor );
     824          sign_shift = 8;
     825          break;
     826  
     827        default:
     828          /* otherwise, exit the loop */
     829          stream->cursor = cursor;
     830          goto Exit;
     831        }
     832  
     833        /* now, compute the signed value is necessary */
     834        if ( fields->value & FT_FRAME_OP_SIGNED )
     835          value = (FT_ULong)( (FT_Int32)( value << sign_shift ) >> sign_shift );
     836  
     837        /* finally, store the value in the object */
     838  
     839        p = (FT_Byte*)structure + fields->offset;
     840        switch ( fields->size )
     841        {
     842        case ( 8 / FT_CHAR_BIT ):
     843          *(FT_Byte*)p = (FT_Byte)value;
     844          break;
     845  
     846        case ( 16 / FT_CHAR_BIT ):
     847          *(FT_UShort*)p = (FT_UShort)value;
     848          break;
     849  
     850        case ( 32 / FT_CHAR_BIT ):
     851          *(FT_UInt32*)p = (FT_UInt32)value;
     852          break;
     853  
     854        default:  /* for 64-bit systems */
     855          *(FT_ULong*)p = (FT_ULong)value;
     856        }
     857  
     858        /* go to next field */
     859        fields++;
     860      }
     861      while ( 1 );
     862  
     863    Exit:
     864      /* close the frame if it was opened by this read */
     865      if ( frame_accessed )
     866        FT_Stream_ExitFrame( stream );
     867  
     868      return error;
     869    }
     870  
     871  
     872  /* END */