"Fossies" - the Fresh Open Source Software Archive

Member "gnash-0.8.10/libcore/parser/action_buffer.cpp" (19 Jan 2012, 19383 Bytes) of package /linux/www/old/gnash-0.8.10.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // action_buffer.cpp:  holds actions for later execution, for Gnash.
    2 // 
    3 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
    4 //   Free Software 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
   19 //
   20 
   21 #include "action_buffer.h"
   22 
   23 #include <string>
   24 #include <cstring> // for memcpy
   25 #include <boost/static_assert.hpp>
   26 
   27 #include "log.h"
   28 #include "SWFStream.h"
   29 #include "SWF.h"
   30 #include "ASHandlers.h"
   31 #include "movie_definition.h"
   32 
   33 namespace gnash {
   34 
   35 // Forward declarations
   36 namespace {
   37     float convert_float_little(const void *p);
   38     double convert_double_wacky(const void *p);
   39 }
   40 
   41 action_buffer::action_buffer(const movie_definition& md)
   42     :
   43     _pools(),
   44     _src(md)
   45 {
   46 }
   47 
   48 void
   49 action_buffer::read(SWFStream& in, unsigned long endPos)
   50 {
   51     unsigned long startPos = in.tell();
   52     assert(endPos <= in.get_tag_end_position());
   53     unsigned size = endPos-startPos;
   54 
   55     if (!size) {
   56         IF_VERBOSE_MALFORMED_SWF(
   57             log_swferror(_("Empty action buffer starting at offset %lu"),
   58                 startPos);
   59         );
   60         return;
   61     }
   62 
   63     // Allocate the buffer
   64     // 
   65     // NOTE: a .reserve would be fine here, except GLIBCPP_DEBUG will complain...
   66     //
   67     m_buffer.resize(size);
   68     unsigned char* buf = &m_buffer.front();
   69 
   70     // Read all the bytes in the buffer
   71     //
   72     // NOTE:
   73     // we might be reading more data then we'll actually
   74     // use here if the SWF contains Action blocks padded
   75     // with data after the terminating END.
   76     // This has a cost in memory use, but for the normal
   77     // case (non-malformed SWF) not looking for an END
   78     // tag should give significant speedup in parsing
   79     // large action-based movies.
   80     //
   81     in.read(reinterpret_cast<char*>(buf), size);
   82 
   83     // Consistency checks here
   84     //
   85     // NOTE: it is common to find such movies, swfmill is known to write
   86     //       DoAction w/out the terminating END tag
   87     //
   88     if (m_buffer.back() != SWF::ACTION_END) {
   89         // Add a null terminator so read_string won't read off
   90         // the end of the buffer.
   91         m_buffer.push_back(0x00);
   92 
   93         IF_VERBOSE_MALFORMED_SWF(
   94             log_swferror(_("Action buffer starting at offset %lu doesn't "
   95                     "end with an END tag"), startPos);
   96         );
   97     }
   98     
   99 }
  100 
  101 const ConstantPool&
  102 action_buffer::readConstantPool(size_t start_pc, size_t stop_pc) const
  103 {
  104     assert(stop_pc <= m_buffer.size()); // TODO: drop, be safe instead
  105 
  106     // Return a previously parsed pool at the same position, if any
  107     PoolsMap::iterator pi = _pools.find(start_pc);
  108     if ( pi != _pools.end() ) return pi->second;
  109 
  110     // Actual processing.
  111 
  112     ConstantPool& pool = _pools[start_pc];
  113 
  114     size_t i = start_pc;
  115     const boost::uint16_t length = read_uint16(i + 1);
  116     const boost::uint16_t count = read_uint16(i + 3); 
  117     i += 2;
  118     
  119     assert(start_pc + 3 + length == stop_pc);
  120     
  121     pool.resize(count);
  122     
  123     // Index the strings.
  124     for (int ct = 0; ct < count; ct++) {
  125         // Point into the current action buffer.
  126         pool[ct] = reinterpret_cast<const char*>(&m_buffer[3 + i]);
  127 
  128         // TODO: rework this "safety" thing here (doesn't look all that safe)
  129         while (m_buffer[3 + i]) {
  130             // safety check.
  131             if (i >= stop_pc) {
  132                 log_error(_("action buffer dict length exceeded"));
  133                 // Jam something into the remaining (invalid) entries.
  134                 while (ct < count) {
  135                     pool[ct] = "<invalid>";
  136                     ct++;
  137                 }
  138                 return pool;
  139             }
  140             i++;
  141         }
  142         i++;
  143     }
  144 
  145     return pool;
  146 }
  147 
  148 
  149 // Disassemble one instruction to the log. The maxBufferLength
  150 // argument is the number of bytes remaining in the action_buffer
  151 // and prevents malformed instructions causing a read past the
  152 // end of the buffer.
  153 static std::string
  154 disasm_instruction(const unsigned char* instruction_data,
  155         size_t maxBufferLength)
  156 {
  157 
  158     using namespace SWF;
  159 
  160     const SWF::SWFHandlers& ash = SWF::SWFHandlers::instance();
  161 
  162     assert (maxBufferLength > 0);
  163 
  164     ArgumentType fmt = ARG_HEX;
  165     ActionType action_id = static_cast<ActionType>(instruction_data[0]);
  166 
  167     std::stringstream ss;
  168 
  169     // Show instruction.
  170     if (action_id > ash.lastType()) {
  171         ss << "<unknown>[0x]" <<  action_id << "\n";
  172     }
  173     else {
  174         ss << ash[action_id].getType();
  175     }
  176     
  177     // Show instruction argument(s).
  178     if (action_id & 0x80) {
  179 
  180         assert(maxBufferLength >= 3);
  181         ss << " (";
  182         fmt = ash[action_id].getArgFormat();
  183         
  184         size_t length = (instruction_data[1] | (instruction_data[2] << 8));
  185         
  186         // Assert that length without the three initial bytes
  187         // is always within the buffer.
  188         assert(length <= maxBufferLength - 3);
  189 
  190         switch (fmt) {
  191 
  192             case ARG_NONE:
  193                 break;
  194 
  195             case ARG_HEX:
  196                 ss << hexify(&instruction_data[3], length, false) << " ";
  197                 break;
  198 
  199             case ARG_STR:
  200             {
  201                 const std::string str =
  202                     hexify(&instruction_data[3], length, true);
  203                 ss << "\"" << str.c_str() << "\"";
  204                 break;
  205             }
  206 
  207             case ARG_U8:
  208             {
  209                 const int val = instruction_data[3];
  210                 ss << " " << val;
  211                 break;
  212             }
  213 
  214             case ARG_U16:
  215             {
  216                 const int val =
  217                     instruction_data[3] | (instruction_data[4] << 8);
  218                 ss << " " << val;
  219                 break;
  220             }
  221 
  222             case ARG_S16:
  223             {
  224                 int val = instruction_data[3] | (instruction_data[4] << 8);
  225                 if (val & 0x8000) val |= ~0x7FFF;    // sign-extend
  226                 ss << " " << val;
  227                 break;
  228             }
  229 
  230             case ARG_PUSH_DATA:
  231             {
  232                 size_t i = 0;
  233                 while (i < length) {
  234                     int type = instruction_data[3 + i];
  235                     
  236                     // This should be safe, as the buffer is always
  237                     // 0-terminated.
  238                     if (i++) ss << ", ";
  239 
  240                     switch (type)
  241                     {
  242                         case 0:
  243                         {
  244                             // string
  245                             std::string str;
  246                             while (instruction_data[3 + i] && i < length)
  247                             {
  248                                 str += hexify(&instruction_data[3 + i], 1, true);
  249                                 i++;
  250                             }
  251                             i++;
  252                             ss << "\"" << str.c_str() << "\"";
  253                             break;
  254                         }
  255 
  256                         case 1:
  257                         {
  258                             // float (little-endian)
  259                             if (i + 4 > length) break;
  260                             float f = convert_float_little(instruction_data + 3 + i);
  261                             i += 4;
  262                             ss << "(float) " << f;
  263                             break;
  264                         }
  265 
  266                         case 2:
  267                             ss << "NULL";
  268                             break;
  269 
  270                         case 3:
  271                             ss << "undef";
  272                             break;
  273 
  274                         case 4:
  275                         {
  276                             // contents of register
  277                             int reg = instruction_data[3 + i];
  278                             i++;
  279                             ss << "reg[" << reg << "]";
  280                             break;
  281                         }
  282 
  283                         case 5:
  284                         {
  285                             
  286                             int bool_val = instruction_data[3 + i];
  287                             i++;
  288                             ss << "bool(" << bool_val << ")";
  289                             break;
  290                         }
  291 
  292                         case 6:
  293                         {
  294                             // double in wacky format: 45670123
  295                             if (i + 8 > length) break;
  296                             double d = convert_double_wacky(instruction_data + 3 + i);
  297                             i += 8;
  298                             ss << "(double) " << d;
  299                             break;
  300                         }
  301 
  302                         case 7:
  303                         {
  304                             // boost::int32_t
  305                             boost::int32_t val = instruction_data[3 + i]
  306                             | (instruction_data[3 + i + 1] << 8)
  307                             | (instruction_data[3 + i + 2] << 16)
  308                             | (instruction_data[3 + i + 3] << 24);
  309                             i += 4;
  310                             ss << "(int) " << val;
  311                             break;
  312                         }
  313 
  314                         case 8:
  315                         {
  316                             int id = instruction_data[3 + i];
  317                             i++;
  318                             ss << "dict_lookup[" << id << "]";
  319                             break;
  320                         }
  321 
  322                         case 9:
  323                         {
  324                             int id = instruction_data[3 + i] | (instruction_data[3 + i + 1] << 8);
  325                             i += 2;
  326                             ss << "dict_lookup_lg[" << id << "]";
  327                             break;
  328                         }
  329                     }
  330                 }
  331                 break;
  332             }
  333 
  334             case ARG_DECL_DICT:
  335             {
  336                 size_t i = 0;
  337                 size_t count = instruction_data[3 + i] | (instruction_data[3 + i + 1] << 8);
  338                 i += 2;
  339                 
  340                 ss << " [" << count << "] ";
  341                 
  342                 // Print strings.
  343                 for (size_t ct = 0; ct < count; ct++)
  344                 {
  345                     if ( ct ) ss << ", ";
  346 
  347                     ss << ct << ":"; 
  348                     
  349                     std::string str;
  350                     while (instruction_data[3 + i] && i < length)
  351                     {
  352                         str += instruction_data[3 + i];
  353                         i++;
  354                     }
  355                     ss << "\"" << str.c_str() << "\"";
  356                     i++;
  357                 }
  358                 break;
  359             }
  360 
  361             case ARG_FUNCTION2:
  362             {
  363                 size_t i = 0;
  364                 std::string functionName;
  365                 // Signature info for a function2 opcode.
  366                 while (instruction_data[3 + i] && i <= length)
  367                 {
  368                     functionName.push_back(instruction_data[3 + i]);
  369                     ++i;
  370                 }
  371  
  372                 // Don't read outside the instruction.
  373                 if (i + 6 > length) break;
  374                 ++i;
  375                        
  376                 boost::uint16_t argCount = instruction_data[3 + i] | (instruction_data[3 + i + 1] << 8);
  377                 i += 2;
  378                 
  379                 boost::uint8_t registerCount = instruction_data[3 + i];
  380                 i++;
  381 
  382                 ss << "\tname = '" << functionName << "'"
  383                        << " arg count = " << argCount
  384                        << " register count = " << static_cast<int>(registerCount);
  385                 
  386                 const boost::uint16_t flags =
  387                     (instruction_data[3 + i]) |
  388                     (instruction_data[3 + i + 1] << 8);
  389 
  390                 i += 2;
  391                 
  392                 const bool preload_global = (flags & 0x100);
  393                 const bool preload_parent = (flags & 0x80);
  394                 const bool preload_root   = (flags & 0x40);
  395                 const bool suppress_super = (flags & 0x20);
  396                 const bool preload_super  = (flags & 0x10);
  397                 const bool suppress_args  = (flags & 0x08);
  398                 const bool preload_args   = (flags & 0x04);
  399                 const bool suppress_this  = (flags & 0x02);
  400                 const bool preload_this   = (flags & 0x01);
  401                 
  402                 ss << " pg=" << preload_global
  403                 << " pp=" << preload_parent
  404                 << " pr=" << preload_root
  405                 << " ss=" << suppress_super
  406                 << " ps=" << preload_super
  407                 << " sa=" << suppress_args
  408                 << " pa=" << preload_args
  409                 << " st=" << suppress_this
  410                 << " pt=" << preload_this;
  411 
  412                 for (size_t argi = 0; argi < argCount; ++argi) {
  413 
  414                     // Make sure not to read past the end of the
  415                     // instruction.
  416                     if (i >= length) break;
  417                     
  418                     int arg_register = instruction_data[3 + i];
  419                     i++;
  420 
  421                     std::string argName;
  422                     // Signature info for a function2 opcode.
  423                     while (instruction_data[3 + i] && i <= length) {
  424                         argName.push_back(instruction_data[3 + i]);
  425                         i++;
  426                     }
  427                     
  428                     ss << "\targ[" << argi << "]"
  429                        << " - reg[" << arg_register << "]"
  430                        << " - '" << argName << "'";
  431 
  432                     if (i == length) break;
  433                     
  434                     // Advance past the terminating 0
  435                     i++;
  436 
  437                 }
  438                 
  439                 if (i + 2 > length) break;
  440                 int function_length = instruction_data[3 + i] | (instruction_data[3 + i + 1] << 8);
  441                 i += 2;
  442                 
  443                 ss << "\t\tfunction length = " << function_length;
  444                 break;
  445             }            
  446         } // Switch
  447         
  448         ss << ")";    
  449     } // If action & 0x80
  450     
  451     return ss.str();
  452 }
  453 
  454 std::string
  455 action_buffer::disasm(size_t pc) const
  456 {
  457     const size_t maxBufferLength = m_buffer.size() - pc;
  458     return disasm_instruction(&m_buffer[pc], maxBufferLength);
  459 }
  460 
  461 float
  462 action_buffer::read_float_little(size_t pc) const
  463 {
  464     return convert_float_little(&m_buffer[pc]);
  465 }
  466 
  467 double
  468 action_buffer::read_double_wacky(size_t pc) const
  469 {
  470     return convert_double_wacky(&m_buffer[pc]);
  471 }
  472 
  473 const std::string&
  474 action_buffer::getDefinitionURL() const
  475 {
  476     return _src.get_url();
  477 }
  478 
  479 int
  480 action_buffer::getDefinitionVersion() const
  481 {
  482     return _src.get_version();
  483 }
  484 
  485 namespace {
  486 
  487 // Endian conversion routines.
  488 //
  489 // Flash format stores integers as little-endian,
  490 // floats as little-endian IEEE754,
  491 // and doubles as little-endian IEEE754 with the two 32-bit words swapped over.
  492 //
  493 // We detect endianness at runtime.
  494 // It looks hairy but the cost is small (one assignment, one switch),
  495 // and it is less of a maintenance/portability nightmare.
  496 // It also allows us to detect three existing variants instead of two and
  497 // to reject incompatible (non-IEEE754) floating point formats (VAX etc).
  498 // For these we would need to interpret the IEEE bitvalues explicitly.
  499 
  500 // Read a little-endian 32-bit float from m_buffer[pc]
  501 // and return it as a host-endian float.
  502 float
  503 convert_float_little(const void *p)
  504 {
  505     // Hairy union for endian detection and munging
  506     union {
  507         float f;
  508         boost::uint32_t i;
  509         struct {    // for endian detection
  510             boost::uint16_t s0;
  511             boost::uint16_t s1;
  512         } s;
  513         struct {    // for byte-swapping
  514             boost::uint8_t c0;
  515             boost::uint8_t c1;
  516             boost::uint8_t c2;
  517             boost::uint8_t c3;
  518         } c;
  519     } u;
  520 
  521     u.f = 1.0;
  522     switch (u.s.s0) {
  523 
  524         case 0x0000:    // little-endian host
  525             std::memcpy(&u.i, p, 4); // TODO: use std::copy instead ..
  526             break;
  527         case 0x3f80:    // big-endian host
  528         {
  529             const boost::uint8_t *cp = static_cast<const boost::uint8_t*>(p);
  530             u.c.c0 = cp[3];
  531             u.c.c1 = cp[2];
  532             u.c.c2 = cp[1];
  533             u.c.c3 = cp[0];
  534             break;
  535         }
  536         default:
  537             log_error(_("Native floating point format not recognised"));
  538             std::abort();
  539     }
  540     
  541     return u.f;
  542 }
  543 
  544 
  545 // Read a 64-bit double from memory, stored in word-swapped little-endian
  546 // format and return it as a host-endian double.
  547 // "Wacky format" is 45670123.
  548 double
  549 convert_double_wacky(const void *p)
  550 {
  551     const boost::uint8_t *cp = static_cast<const boost::uint8_t*>(p);
  552     union {
  553         double d;
  554         boost::uint64_t i;
  555         struct {
  556             boost::uint32_t l0;
  557             boost::uint32_t l1;
  558         } l;
  559         struct {
  560             boost::uint16_t s0;
  561             boost::uint16_t s1;
  562             boost::uint16_t s2;
  563             boost::uint16_t s3;
  564         } s;
  565         struct {
  566             boost::uint8_t c0;
  567             boost::uint8_t c1;
  568             boost::uint8_t c2;
  569             boost::uint8_t c3;
  570             boost::uint8_t c4;
  571             boost::uint8_t c5;
  572             boost::uint8_t c6;
  573             boost::uint8_t c7;
  574         } c;
  575     } u;
  576 
  577     BOOST_STATIC_ASSERT(sizeof(u) == sizeof(u.i));
  578 
  579     // Detect endianness of doubles by storing a value that is
  580     // exactly representable and that has different values in the
  581     // four 16-bit words.
  582     // 0x11223344 is represented as 0x41b1 2233 4400 0000 (bigendian)
  583     u.d = static_cast<double>(0x11223344);
  584     switch (u.s.s0) {
  585     case 0x0000:    // pure little-endian host: swap words only.
  586         std::memcpy(&u.l.l1, cp, 4);
  587         std::memcpy(&u.l.l0, cp + 4, 4);
  588         break;
  589     case 0x41b1:    // pure big-endian host: swap contents of 32-bit words
  590         u.c.c0 = cp[3];
  591         u.c.c1 = cp[2];
  592         u.c.c2 = cp[1];
  593         u.c.c3 = cp[0];
  594         u.c.c4 = cp[7];
  595         u.c.c5 = cp[6];
  596         u.c.c6 = cp[5];
  597         u.c.c7 = cp[4];
  598         break;
  599     case 0x2233:    // word-swapped little-endian host (PDP / ARM FPA)
  600             // is the same as wacky format.
  601         std::memcpy(&u.i, cp, 8);
  602         break;
  603     case 0x4400:    // word-swapped big-endian host: does this exist?
  604         u.c.c0 = cp[7];
  605         u.c.c1 = cp[6];
  606         u.c.c2 = cp[5];
  607         u.c.c3 = cp[4];
  608         u.c.c4 = cp[3];
  609         u.c.c5 = cp[2];
  610         u.c.c6 = cp[1];
  611         u.c.c7 = cp[0];
  612         break;
  613     default:
  614         log_error(_("Native double floating point format not recognised"));
  615         abort();
  616     }
  617 
  618     return u.d;
  619 }
  620 
  621 } // unnamed namespace
  622 } // namespace gnash
  623 
  624 // Local Variables:
  625 // mode: C++
  626 // indent-tabs-mode: nil
  627 // End: