1 // stream.cpp - SWF stream reading class, for Gnash
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "SWFStream.h"
24 #include "IOChannel.h"
27 #include "action_buffer.h"
31 #include <boost/static_assert.hpp>
33 //#define USE_TU_FILE_BYTESWAPPING 1
37 SWFStream::SWFStream(IOChannel
* input
)
46 SWFStream::~SWFStream()
51 SWFStream::ensureBytes(unsigned long needed
)
53 #ifndef GNASH_TRUST_SWF_INPUT
55 // Not in a tag (should we check file length?)
56 if ( _tagBoundsStack
.empty() ) return;
58 unsigned long int left
= get_tag_end_position() - tell();
62 ss
<< "premature end of tag: need to read " << needed
<<
63 " bytes, but only " << left
<< " left in this tag";
64 throw ParserException(ss
.str());
69 unsigned SWFStream::read(char *buf
, unsigned count
)
73 // If we're in a tag, make sure we're not seeking outside the tag.
74 if ( ! _tagBoundsStack
.empty() )
76 TagBoundaries
& tb
= _tagBoundsStack
.back();
77 unsigned long endPos
= tb
.second
;
78 unsigned long cur_pos
= tell();
79 assert(endPos
>= cur_pos
);
80 unsigned long left
= endPos
- cur_pos
;
81 if ( left
< count
) count
= left
;
84 if ( ! count
) return 0;
86 return m_input
->read(buf
, count
);
89 bool SWFStream::read_bit()
93 m_current_byte
= m_input
->read_byte(); // don't want to align here
95 return (m_current_byte
&0x80);
99 return ( m_current_byte
& (1<<(--m_unused_bits
)) );
103 unsigned SWFStream::read_uint(unsigned short bitcount
)
105 // htf_sweet.swf fails when this is set to 24. There seems to
106 // be no reason why this should be limited to 32 other than
107 // that it is higher than a movie is likely to need.
110 // This might overflow a uint32_t or attempt to read outside
111 // the byte cache (relies on there being only 4 bytes after
112 // possible unused bits.)
113 throw ParserException("Unexpectedly long value advertised.");
116 // Optimization for multibyte read
117 if ( bitcount
> m_unused_bits
)
119 typedef unsigned char byte
;
121 boost::uint32_t value
= 0;
123 if (m_unused_bits
) // Consume all the unused bits.
125 int unusedMask
= (1 << m_unused_bits
)-1;
126 bitcount
-= m_unused_bits
;
127 value
|= ((m_current_byte
&unusedMask
) << bitcount
);
130 int bytesToRead
= bitcount
/8;
131 int spareBits
= bitcount
%8; // additional bits to read
133 //std::cerr << "BytesToRead: " << bytesToRead << " spareBits: " << spareBits << " unusedBits: " << (int)m_unused_bits << std::endl;
135 assert (bytesToRead
<= 4);
136 byte cache
[4]; // at most 4 bytes in the cache
138 if ( spareBits
) m_input
->read(&cache
, bytesToRead
+1);
139 else m_input
->read(&cache
, bytesToRead
);
141 for (int i
=0; i
<bytesToRead
; ++i
)
144 value
|= cache
[i
] << bitcount
;
147 //assert(bitcount == spareBits);
150 m_current_byte
= cache
[bytesToRead
];
151 m_unused_bits
= 8-bitcount
;
152 value
|= m_current_byte
>> m_unused_bits
;
165 m_current_byte
= m_input
->read_byte();
169 // TODO: optimize unusedMask creation ?
170 // (it's 0xFF if ! m_unused_bits above)
171 int unusedMask
= (1 << m_unused_bits
)-1;
173 if (bitcount
== m_unused_bits
)
175 // Consume all the unused bits.
177 return (m_current_byte
&unusedMask
);
181 assert(bitcount
< m_unused_bits
);
182 // Consume some of the unused bits.
184 m_unused_bits
-= bitcount
;
185 return ((m_current_byte
&unusedMask
) >> m_unused_bits
);
192 SWFStream::read_sint(unsigned short bitcount
)
194 //assert(bitcount <= 32); // already asserted in read_uint
196 boost::int32_t value
= boost::int32_t(read_uint(bitcount
));
199 if (value
& (1 << (bitcount
- 1))) {
200 value
|= -1 << bitcount
;
203 // IF_DEBUG(log_debug("SWFStream::read_sint(%d) == %d\n", bitcount, value));
209 float SWFStream::read_fixed()
211 // align(); // read_u32 will align
212 return static_cast<float> (
213 static_cast<double>(read_s32()) / 65536.0f
218 // float is not large enough to hold a 32 bit value without doing the wrong thing with the sign.
219 // So we upgrade to double for the calculation and then resize when we know it's small enough.
220 float SWFStream::read_ufixed()
222 // align(); // read_u32 will align
223 return static_cast<float> (
224 static_cast<double>(read_u32()) / 65536.0f
228 // Read a short fixed value, unsigned.
229 float SWFStream::read_short_ufixed()
231 // align(); // read_u16 will align
232 return static_cast<float> ( read_u16() / 256.0f
);
235 // Read a short fixed value, signed.
236 float SWFStream::read_short_sfixed()
238 // align(); // read_s16 will align
239 return static_cast<float> ( read_s16() / 256.0f
);
242 /// Read a 16bit (1:sign 5:exp 10:mantissa) floating point value
243 float SWFStream::read_short_float()
245 // read_s16 will align
246 return static_cast<float> ( read_s16() );
249 // Read a little-endian 32-bit float from p
250 // and return it as a host-endian float.
252 convert_float_little(const void *p
)
254 // Hairy union for endian detection and munging
258 struct { // for endian detection
262 struct { // for byte-swapping
272 case 0x0000: // little-endian host
275 case 0x3f80: // big-endian host
277 const boost::uint8_t *cp
= (const boost::uint8_t *) p
;
285 log_error(_("Native floating point format not recognised"));
292 /// Read a 32bit (1:sign 8:exp 23:mantissa) floating point value
293 float SWFStream::read_long_float()
295 const unsigned short dataLength
= 4;
297 char data
[dataLength
];
300 if (read(data
, dataLength
) < dataLength
)
302 throw ParserException(_("Unexpected end of stream while reading"));
304 return convert_float_little(data
);
307 // Read a 64-bit double value
308 double SWFStream::read_d64()
311 // TODO: This is very dodgy and doesn't account for endianness!
312 const unsigned short dataLength
= 8;
315 BOOST_STATIC_ASSERT(sizeof(double) == dataLength
);
318 if (read(reinterpret_cast<char*>(&d
), dataLength
) < dataLength
)
320 throw ParserException(_("Unexpected end of stream while reading"));
327 boost::uint8_t SWFStream::read_u8()
330 return m_input
->read_byte();
336 // read_u8 will align
340 boost::uint16_t SWFStream::read_u16()
342 #ifdef USE_TU_FILE_BYTESWAPPING
344 return m_input
->read_le16();
346 const unsigned short dataLength
= 2;
348 unsigned char buf
[dataLength
];
351 if (read(reinterpret_cast<char*>(buf
), dataLength
) < dataLength
)
353 throw ParserException(_("Unexpected end of stream while reading"));
356 boost::uint32_t result
= buf
[0];
357 result
|= (buf
[1] << 8);
363 boost::int16_t SWFStream::read_s16()
365 // read_u16 will align
369 boost::uint32_t SWFStream::read_u32()
371 #ifdef USE_TU_FILE_BYTESWAPPING
373 return m_input
->read_le32();
375 using boost::uint32_t;
377 const unsigned short dataLength
= 4;
379 unsigned char buf
[dataLength
];
382 if (read(reinterpret_cast<char*>(buf
), dataLength
) < dataLength
)
384 throw ParserException(_("Unexpected end of stream while reading"));
387 uint32_t result
= buf
[0];
388 result
|= buf
[1] << 8;
389 result
|= buf
[2] << 16;
390 result
|= buf
[3] << 24;
396 boost::int32_t SWFStream::read_s32()
398 // read_u32 will align
403 SWFStream::read_string(std::string
& to
)
412 const char& c
= read_u8();
413 if ( c
== 0 ) break; // don't store a NULL in the string.
419 void SWFStream::read_string_with_length(std::string
& to
)
424 const unsigned int len
= read_u8();
425 read_string_with_length(len
, to
); // will check 'len'
428 void SWFStream::read_string_with_length(unsigned len
, std::string
& to
)
435 for (unsigned int i
= 0; i
< len
; ++i
)
440 // drop trailing nulls (see swf6/Bejeweled.swf)
441 std::string::size_type last
= to
.find_last_not_of('\0');
442 if ( last
== std::string::npos
) to
.clear();
446 // seems common to find null-terminated lenght-equipped strings...
457 int pos
= m_input
->tell();
458 // TODO: check return value? Could be negative.
459 return static_cast<unsigned long>(pos
);
464 SWFStream::seek(unsigned long pos
)
468 // If we're in a tag, make sure we're not seeking outside the tag.
469 if ( ! _tagBoundsStack
.empty() )
471 TagBoundaries
& tb
= _tagBoundsStack
.back();
472 unsigned long endPos
= tb
.second
;
475 log_error("Attempt to seek past the end of an opened tag");
477 // throw ParserException ?
480 unsigned long startPos
= tb
.first
;
481 if ( pos
< startPos
)
483 log_error("Attempt to seek before start of an opened tag");
485 // throw ParserException ?
491 if (!m_input
->seek(pos
))
493 // TODO: should we throw an exception ?
494 // we might be called from an exception handler
495 // so throwing here might be a double throw...
496 log_swferror(_("Unexpected end of stream"));
505 SWFStream::get_tag_end_position()
507 assert(_tagBoundsStack
.size() > 0);
509 return _tagBoundsStack
.back().second
;
514 SWFStream::open_tag()
518 unsigned long tagStart
= tell();
522 int tagHeader
= read_u16();
523 int tagType
= tagHeader
>> 6;
524 int tagLength
= tagHeader
& 0x3F;
525 assert(m_unused_bits
== 0);
527 if (tagLength
== 0x3F)
530 tagLength
= read_u32();
535 throw ParserException("Negative tag length advertised.");
538 if ( tagLength
> 1024*64 )
540 //log_debug("Tag %d has a size of %d bytes !!", tagType, tagLength);
543 unsigned long tagEnd
= tell() + tagLength
;
545 // Check end position doesn't overflow a signed int - that makes
546 // zlib adapter's inflate_seek(int pos, void* appdata) unhappy.
547 // The cast stops compiler warnings. We know it's a positive number.
548 // TODO: make IOChannel take a long instead of an int.
549 // TODO: check against stream length.
550 if (tagEnd
> static_cast<unsigned int>(std::numeric_limits
<signed int>::max()))
552 std::stringstream ss
;
553 ss
<< "Invalid tag end position " << tagEnd
<< " advertised (tag length "
554 << tagLength
<< ").";
555 throw ParserException(ss
.str());
558 if ( ! _tagBoundsStack
.empty() )
560 // check that this tag doesn't cross containing tag bounds
561 unsigned long containerTagEnd
= _tagBoundsStack
.back().second
;
562 if ( tagEnd
> containerTagEnd
)
564 unsigned long containerTagStart
= _tagBoundsStack
.back().first
;
565 log_swferror(_("Tag %d starting at offset %d is advertised to end "
566 "at offset %d, which is after end of previously opened "
567 "tag starting at offset %d and ending at offset %d. "
568 "Making it end where container tag ends."),
569 tagType
, tagStart
, tagEnd
, containerTagStart
, containerTagEnd
);
572 tagEnd
= containerTagEnd
;
573 //throw ParserException(ss.str());
577 // Remember where the end of the tag is, so we can
578 // fast-forward past it when we're done reading it.
579 _tagBoundsStack
.push_back(std::make_pair(tagStart
, tagEnd
));
582 log_parse("SWF[%lu]: tag type = %d, tag length = %d, end tag = %lu",
583 tagStart
, tagType
, tagLength
, tagEnd
);
586 return static_cast<SWF::TagType
>(tagType
);
591 SWFStream::close_tag()
594 assert(_tagBoundsStack
.size() > 0);
595 std::streampos endPos
= _tagBoundsStack
.back().second
;
596 _tagBoundsStack
.pop_back();
598 //log_debug("Close tag called at %d, stream size: %d", endPos);
600 if (!m_input
->seek(endPos
))
602 // We'll go on reading right past the end of the stream
603 // if we don't throw an exception.
604 throw ParserException(_("Could not seek to reported end of tag"));
611 SWFStream::consumeInput()
613 // IOChannel::go_to_end is documented
614 // to possibly throw an exception (!)
616 m_input
->go_to_end();
618 catch (IOException
& ex
) {
619 log_error("SWFStream::consumeInput: underlying stream couldn't "
620 "go_to_end: %s", ex
.what());
625 } // end namespace gnash
632 // indent-tabs-mode: t