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