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