1 /***************************************************************************/
2 /*                                                                         */
3 /*  ftstream.c                                                             */
4 /*                                                                         */
5 /*    I/O stream support (body).                                           */
6 /*                                                                         */
7 /*  Copyright 2000-2015 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 = NULL;
43     stream->read   = NULL;
44     stream->close  = NULL;
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     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 )
FT_Stream_Skip(FT_Stream stream,FT_Long distance)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 )
FT_Stream_Pos(FT_Stream stream)103   FT_Stream_Pos( FT_Stream  stream )
104   {
105     return stream->pos;
106   }
107 
108 
109   FT_BASE_DEF( FT_Error )
FT_Stream_Read(FT_Stream stream,FT_Byte * buffer,FT_ULong count)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 )
FT_Stream_ReadAt(FT_Stream stream,FT_ULong pos,FT_Byte * buffer,FT_ULong count)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 )
FT_Stream_TryRead(FT_Stream stream,FT_Byte * buffer,FT_ULong count)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 )
FT_Stream_ExtractFrame(FT_Stream stream,FT_ULong count,FT_Byte ** pbytes)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 )
FT_Stream_ReleaseFrame(FT_Stream stream,FT_Byte ** pbytes)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 )
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     /* 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 )
FT_Stream_ExitFrame(FT_Stream stream)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 )
FT_Stream_GetChar(FT_Stream stream)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 )
FT_Stream_GetUShort(FT_Stream stream)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 )
FT_Stream_GetUShortLE(FT_Stream stream)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 )
FT_Stream_GetUOffset(FT_Stream stream)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 )
FT_Stream_GetULong(FT_Stream stream)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 )
FT_Stream_GetULongLE(FT_Stream stream)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 )
FT_Stream_ReadChar(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadUShort(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadUShortLE(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadUOffset(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadULong(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadULongLE(FT_Stream stream,FT_Error * error)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 )
FT_Stream_ReadFields(FT_Stream stream,const FT_Frame_Field * fields,void * structure)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 */
861