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