fix typo, add instructions about lowercase named strings
[gnash.git] / libcore / SWFStream.cpp
blobc214bc7d1e04fb4e9e8bb64fbe7dac928a67740c
1 // stream.cpp - SWF stream reading class, for Gnash
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // Foundation, Inc
5 //
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.
10 //
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.
15 //
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"
23 #include "log.h"
24 #include "IOChannel.h"
25 #include "SWF.h"
26 #include "Property.h"
27 #include "action_buffer.h"
29 #include <cstring>
30 #include <climits>
31 #include <boost/static_assert.hpp>
33 //#define USE_TU_FILE_BYTESWAPPING 1
35 namespace gnash {
37 SWFStream::SWFStream(IOChannel* input)
39 m_input(input),
40 m_current_byte(0),
41 m_unused_bits(0)
46 SWFStream::~SWFStream()
50 void
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();
59 if ( left < needed )
61 std::stringstream ss;
62 ss << "premature end of tag: need to read " << needed <<
63 " bytes, but only " << left << " left in this tag";
64 throw ParserException(ss.str());
66 #endif
69 unsigned SWFStream::read(char *buf, unsigned count)
71 align();
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()
91 if (!m_unused_bits)
93 m_current_byte = m_input->read_byte(); // don't want to align here
94 m_unused_bits = 7;
95 return (m_current_byte&0x80);
97 else
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.
108 if (bitcount > 32)
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)
143 bitcount -= 8;
144 value |= cache[i] << bitcount;
147 //assert(bitcount == spareBits);
148 if ( bitcount )
150 m_current_byte = cache[bytesToRead];
151 m_unused_bits = 8-bitcount;
152 value |= m_current_byte >> m_unused_bits;
154 else
156 m_unused_bits = 0;
159 return value;
163 if (!m_unused_bits)
165 m_current_byte = m_input->read_byte();
166 m_unused_bits = 8;
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.
176 m_unused_bits = 0;
177 return (m_current_byte&unusedMask);
179 else
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));
198 // Sign extend...
199 if (value & (1 << (bitcount - 1))) {
200 value |= -1 << bitcount;
203 // IF_DEBUG(log_debug("SWFStream::read_sint(%d) == %d\n", bitcount, value));
205 return 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.
251 static float
252 convert_float_little(const void *p)
254 // Hairy union for endian detection and munging
255 union {
256 float f;
257 boost::uint32_t i;
258 struct { // for endian detection
259 boost::uint16_t s0;
260 boost::uint16_t s1;
261 } s;
262 struct { // for byte-swapping
263 boost::uint8_t c0;
264 boost::uint8_t c1;
265 boost::uint8_t c2;
266 boost::uint8_t c3;
267 } c;
268 } u;
270 u.f = 1.0;
271 switch (u.s.s0) {
272 case 0x0000: // little-endian host
273 memcpy(&u.i, p, 4);
274 break;
275 case 0x3f80: // big-endian host
277 const boost::uint8_t *cp = (const boost::uint8_t *) p;
278 u.c.c0 = cp[3];
279 u.c.c1 = cp[2];
280 u.c.c2 = cp[1];
281 u.c.c3 = cp[0];
283 break;
284 default:
285 log_error(_("Native floating point format not recognised"));
286 abort();
289 return u.f;
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];
299 // Should align
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;
313 double d = 0;
315 BOOST_STATIC_ASSERT(sizeof(double) == dataLength);
317 // Should align:
318 if (read(reinterpret_cast<char*>(&d), dataLength) < dataLength)
320 throw ParserException(_("Unexpected end of stream while reading"));
323 return d;
327 boost::uint8_t SWFStream::read_u8()
329 align();
330 return m_input->read_byte();
333 boost::int8_t
334 SWFStream::read_s8()
336 // read_u8 will align
337 return read_u8();
340 boost::uint16_t SWFStream::read_u16()
342 #ifdef USE_TU_FILE_BYTESWAPPING
343 align();
344 return m_input->read_le16();
345 #else
346 const unsigned short dataLength = 2;
348 unsigned char buf[dataLength];
350 // Should align:
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);
359 return result;
360 #endif
363 boost::int16_t SWFStream::read_s16()
365 // read_u16 will align
366 return read_u16();
369 boost::uint32_t SWFStream::read_u32()
371 #ifdef USE_TU_FILE_BYTESWAPPING
372 align();
373 return m_input->read_le32();
374 #else
375 using boost::uint32_t;
377 const unsigned short dataLength = 4;
379 unsigned char buf[dataLength];
381 // Should align
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;
392 return result;
393 #endif
396 boost::int32_t SWFStream::read_s32()
398 // read_u32 will align
399 return read_u32();
402 void
403 SWFStream::read_string(std::string& to)
405 align();
407 to.clear();
411 ensureBytes(1);
412 const char& c = read_u8();
413 if ( c == 0 ) break; // don't store a NULL in the string.
414 to.push_back(c);
415 } while(1);
419 void SWFStream::read_string_with_length(std::string& to)
421 align();
423 ensureBytes(1);
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)
430 align();
432 to.resize(len);
434 ensureBytes(len);
435 for (unsigned int i = 0; i < len; ++i)
437 to[i] = read_u8();
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();
443 else {
444 ++last;
445 if (last < len) {
446 // seems common to find null-terminated lenght-equipped strings...
447 to.erase(last);
454 unsigned long
455 SWFStream::tell()
457 int pos = m_input->tell();
458 // TODO: check return value? Could be negative.
459 return static_cast<unsigned long>(pos);
463 bool
464 SWFStream::seek(unsigned long pos)
466 align();
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;
473 if ( pos > endPos )
475 log_error("Attempt to seek past the end of an opened tag");
476 // abort(); // ?
477 // throw ParserException ?
478 return false;
480 unsigned long startPos = tb.first;
481 if ( pos < startPos )
483 log_error("Attempt to seek before start of an opened tag");
484 // abort(); // ?
485 // throw ParserException ?
486 return false;
490 // Do the seek.
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"));
497 return false;
500 return true;
504 unsigned long
505 SWFStream::get_tag_end_position()
507 assert(_tagBoundsStack.size() > 0);
509 return _tagBoundsStack.back().second;
513 SWF::TagType
514 SWFStream::open_tag()
516 align();
518 unsigned long tagStart = tell();
520 ensureBytes(2);
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)
529 ensureBytes(4);
530 tagLength = read_u32();
533 if (tagLength < 0)
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);
571 // what to do now ?
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));
581 IF_VERBOSE_PARSE (
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);
590 void
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"));
607 m_unused_bits = 0;
610 void
611 SWFStream::consumeInput()
613 // IOChannel::go_to_end is documented
614 // to possibly throw an exception (!)
615 try {
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());
621 // eh.. and now ?!
625 } // end namespace gnash
628 // Local Variables:
629 // mode: C++
630 // c-basic-offset: 8
631 // tab-width: 8
632 // indent-tabs-mode: t
633 // End: