Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/anotherbigidea/flash/readers/ActionParser.java


1   /****************************************************************
2    * Copyright (c) 2001, David N. Main, All rights reserved.
3    * 
4    * Redistribution and use in source and binary forms, with or
5    * without modification, are permitted provided that the 
6    * following conditions are met:
7    *
8    * 1. Redistributions of source code must retain the above 
9    * copyright notice, this list of conditions and the following 
10   * disclaimer. 
11   * 
12   * 2. Redistributions in binary form must reproduce the above 
13   * copyright notice, this list of conditions and the following 
14   * disclaimer in the documentation and/or other materials 
15   * provided with the distribution.
16   * 
17   * 3. The name of the author may not be used to endorse or 
18   * promote products derived from this software without specific 
19   * prior written permission. 
20   * 
21   * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY 
22   * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
23   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
24   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
25   * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
26   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 
27   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
28   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
29   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
30   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
31   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
32   * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33   ****************************************************************/
34  package com.anotherbigidea.flash.readers;
35  
36  import java.io.IOException;
37  import java.util.Enumeration;
38  import java.util.Hashtable;
39  import java.util.Stack;
40  import java.util.Vector;
41  
42  import com.anotherbigidea.flash.SWFActionCodes;
43  import com.anotherbigidea.flash.SWFConstants;
44  import com.anotherbigidea.flash.interfaces.SWFActions;
45  import com.anotherbigidea.io.InStream;
46  
47  /**
48   * Parse action bytes and drive a SWFActions interface
49   */
50  public class ActionParser implements SWFActionCodes 
51  {
52      protected SWFActions actions;
53      protected int blockDepth = 0;
54      protected String mStringEncoding = SWFConstants.STRING_ENCODING_MX;
55      
56      public ActionParser( SWFActions actions, int flashVersion )
57      {
58          this.actions = actions;
59          if( flashVersion < SWFConstants.FLASH_MX_VERSION ) {
60            mStringEncoding = SWFConstants.STRING_ENCODING_PRE_MX;
61          }
62      }
63      
64      public synchronized void parse( byte[] bytes ) throws IOException
65      {
66          Vector records = createRecords( bytes );
67          processRecords( records );
68      }
69      
70      public synchronized void parse( InStream in ) throws IOException
71      {
72          Vector records = createRecords( in );
73          processRecords( records );
74      }
75          
76      protected void processRecords( Vector records ) throws IOException
77      {
78          //--process action records
79          for( Enumeration enum = records.elements(); enum.hasMoreElements(); )
80          {
81              ActionRecord rec = (ActionRecord)enum.nextElement();
82              
83              //actions.comment( "depth=" + rec.blockDepth );
84              
85              //detect end of block
86              if( rec.blockDepth < blockDepth )
87              {
88                  blockDepth--;
89                  actions.endBlock();
90              }
91              
92              if( rec.label != null ) actions.jumpLabel( rec.label );
93              
94              int code = rec.code;
95              byte[] data = rec.data;
96              
97              InStream in = (data!=null && data.length > 0) ? new InStream(data) : null;
98  
99              switch( code )
100             {
101                 case 0: actions.end(); break;
102                 
103                 //--Flash 3
104                 case GOTO_FRAME    : actions.gotoFrame( in.readUI16() ); break;
105                 case GET_URL       : actions.getURL( in.readString(mStringEncoding), in.readString(mStringEncoding) ); break;
106                 case NEXT_FRAME    : actions.nextFrame(); break;
107                 case PREVIOUS_FRAME: actions.prevFrame(); break;
108                 case PLAY          : actions.play(); break;
109                 case STOP          : actions.stop(); break;
110                 case TOGGLE_QUALITY: actions.toggleQuality(); break;
111                 case STOP_SOUNDS   : actions.stopSounds(); break;
112                 case WAIT_FOR_FRAME: actions.waitForFrame( in.readUI16(), rec.jumpLabel ); break;
113                 case SET_TARGET    : actions.setTarget( in.readString(mStringEncoding) ); break;
114                 case GOTO_LABEL    : actions.gotoFrame( in.readString(mStringEncoding) ); break;
115                 
116                 //--Flash 4
117                 case IF              : actions.ifJump( rec.jumpLabel ); break;
118                 case JUMP            : actions.jump( rec.jumpLabel ); break;
119                 case WAIT_FOR_FRAME_2: actions.waitForFrame( rec.jumpLabel ); break;
120                 case POP             : actions.pop(); break;
121                 case PUSH            : parsePush( data.length, in ); break;
122                 case ADD             : actions.add(); break;
123                 case SUBTRACT        : actions.substract(); break;
124                 case MULTIPLY        : actions.multiply(); break;
125                 case DIVIDE          : actions.divide(); break;
126                     
127                 case EQUALS          : actions.equals(); break;
128                 case LESS            : actions.lessThan(); break;
129                 case AND             : actions.and(); break;
130                 case OR              : actions.or(); break;
131                 case NOT             : actions.not(); break;
132                                        
133                 case STRING_EQUALS     : actions.stringEquals(); break;
134                 case STRING_LENGTH     : actions.stringLength(); break;
135                 case STRING_ADD        : actions.concat(); break;
136                 case STRING_EXTRACT    : actions.substring(); break;
137                 case STRING_LESS       : actions.stringLessThan(); break;                                                        
138                 case MB_STRING_EXTRACT : actions.substringMB(); break;
139                 case MB_STRING_LENGTH  : actions.stringLengthMB(); break;
140 
141                 case TO_INTEGER       : actions.toInteger(); break;
142                 case CHAR_TO_ASCII    : actions.charToAscii(); break;
143                 case ASCII_TO_CHAR    : actions.asciiToChar(); break;
144                 case MB_CHAR_TO_ASCII : actions.charMBToAscii(); break;
145                 case MB_ASCII_TO_CHAR : actions.asciiToCharMB(); break;
146     
147                 case CALL             : actions.call(); break;
148                 case GET_VARIABLE     : actions.getVariable(); break;
149                 case SET_VARIABLE     : actions.setVariable(); break;
150                  
151                 case GET_URL_2        : parseGetURL2( in.readUI8() ); break;
152                                         
153                 case GOTO_FRAME_2  : actions.gotoFrame( in.readUI8() != 0 ); break;
154                 case SET_TARGET_2  : actions.setTarget(); break;
155                 case GET_PROPERTY  : actions.getProperty(); break;
156                 case SET_PROPERTY  : actions.setProperty(); break;
157                 case CLONE_SPRITE  : actions.cloneSprite(); break;
158                 case REMOVE_SPRITE : actions.removeSprite(); break;
159                 case START_DRAG    : actions.startDrag(); break;
160                 case END_DRAG      : actions.endDrag(); break;
161                 case TRACE         : actions.trace(); break;
162                 case GET_TIME      : actions.getTime(); break;
163                 case RANDOM_NUMBER : actions.randomNumber(); break;
164 
165                 //--Flash 5                         
166                 case INIT_ARRAY         : actions.initArray(); break;
167                 case LOOKUP_TABLE       : parseLookupTable( in ); break;
168                 case CALL_FUNCTION      : actions.callFunction(); break;
169                 case CALL_METHOD        : actions.callMethod(); break;
170                 case DEFINE_FUNCTION    : parseDefineFunction(in); break;
171                 case DEFINE_LOCAL_VAL   : actions.defineLocalValue(); break;
172                 case DEFINE_LOCAL       : actions.defineLocal(); break;
173                 case DEL_VAR            : actions.deleteProperty(); break;
174                 case DEL_THREAD_VARS    : actions.deleteThreadVars(); break;
175                 case ENUMERATE          : actions.enumerate(); break;
176                 case TYPED_EQUALS       : actions.typedEquals(); break;
177                 case GET_MEMBER         : actions.getMember(); break;
178                 case INIT_OBJECT        : actions.initObject(); break;
179                 case CALL_NEW_METHOD    : actions.newMethod(); break;
180                 case NEW_OBJECT         : actions.newObject(); break;
181                 case SET_MEMBER         : actions.setMember(); break;
182                 case GET_TARGET_PATH    : actions.getTargetPath(); break;
183                 case WITH               : parseWith( in ); break;
184                 case DUPLICATE          : actions.duplicate(); break;
185                 case RETURN             : actions.returnValue(); break;
186                 case SWAP               : actions.swap(); break;
187                 case REGISTER           : actions.storeInRegister( in.readUI8() ); break;
188                 case MODULO             : actions.modulo(); break;
189                 case TYPEOF             : actions.typeOf(); break;
190                 case TYPED_ADD          : actions.typedAdd(); break;
191                 case TYPED_LESS_THAN    : actions.typedLessThan(); break;
192                 case CONVERT_TO_NUMBER  : actions.convertToNumber(); break;
193                 case CONVERT_TO_STRING  : actions.convertToString(); break;
194                 case INCREMENT          : actions.increment(); break;
195                 case DECREMENT          : actions.decrement(); break;
196                 case BIT_AND            : actions.bitAnd(); break;
197                 case BIT_OR             : actions.bitOr(); break;
198                 case BIT_XOR            : actions.bitXor(); break;
199                 case SHIFT_LEFT         : actions.shiftLeft(); break;
200                 case SHIFT_RIGHT        : actions.shiftRight(); break;
201                 case SHIFT_UNSIGNED     : actions.shiftRightUnsigned(); break;
202 
203         //--Flash 6
204         case INSTANCE_OF        : actions.instanceOf(); break;
205         case ENUMERATE_OBJECT   : actions.enumerateObject(); break;
206         case GREATER            : actions.greaterThan(); break;
207         case STRICT_EQUALS      : actions.strictEquals(); break;
208         case STRING_GREATER     : actions.stringGreaterThan(); break;
209 
210                 default: actions.unknown( code, data ); break;
211             }            
212         }
213     }
214 
215     protected void parseDefineFunction( InStream in ) throws IOException 
216     {
217         String name = in.readString(mStringEncoding);
218         int paramCount = in.readUI16();
219         
220         String[] params = new String[ paramCount ];
221         for( int i = 0; i < params.length; i++ )
222         {
223             params[i] = in.readString(mStringEncoding);
224         }
225         
226         int codesize = in.readUI16();
227 
228         //System.out.println( "codesize=" + codesize ); System.out.flush();
229 
230         actions.startFunction( name, params );
231         
232         //empty functions have no code
233         if( codesize == 0 ) {
234           actions.endBlock();
235           return;
236         }
237         
238         blockDepth++;
239     }
240 
241     protected void parseWith( InStream in ) throws IOException 
242     {
243         int codesize = in.readUI16();
244 
245         actions.startWith( );
246 
247     //empty blocks have no code
248     if( codesize == 0 ) {
249       actions.endBlock();
250       return;
251     }
252     
253         blockDepth++;
254     }    
255     
256     protected void parseLookupTable( InStream in ) throws IOException 
257     {
258         String[] strings = new String[ in.readUI16() ];
259         
260         for( int i = 0; i < strings.length; i++ )
261         {
262             strings[i] = in.readString(mStringEncoding);
263         }
264         
265         actions.lookupTable( strings );
266     }
267     
268     protected void parseGetURL2( int flags ) throws IOException
269     {
270         int sendVars = flags & 0x03;
271         int mode = 0;
272         
273         switch( flags & 0xF0 )
274         {
275             case 0x40: mode = SWFActions.GET_URL_MODE_LOAD_MOVIE_INTO_SPRITE; break;
276             case 0x80: mode = SWFActions.GET_URL_MODE_LOAD_VARS_INTO_LEVEL;   break;
277             case 0xC0: mode = SWFActions.GET_URL_MODE_LOAD_VARS_INTO_SPRITE;  break;
278             default:   mode = SWFActions.GET_URL_MODE_LOAD_MOVIE_INTO_LEVEL;  break;
279         }
280         
281         actions.getURL( sendVars, mode );
282     }
283     
284     protected void parsePush( int length, InStream in ) throws IOException 
285     {
286         while( in.getBytesRead() < length )
287         {
288             int pushType = in.readUI8();
289             
290             switch( pushType )
291             {
292                 case PUSHTYPE_STRING  : actions.push( in.readString(mStringEncoding) ); break;
293                 case PUSHTYPE_FLOAT   : actions.push( in.readFloat() ); break;
294                 case PUSHTYPE_NULL    : actions.pushNull(); break;
295                 case PUSHTYPE_03      : break;
296                 case PUSHTYPE_REGISTER: actions.pushRegister( in.readUI8() ); break;
297                 case PUSHTYPE_BOOLEAN : actions.push( (in.readUI8() != 0) ? true : false ); break;
298                 case PUSHTYPE_DOUBLE  : actions.push( in.readDouble() ); break;
299                 case PUSHTYPE_INTEGER : actions.push( in.readSI32() ); break;
300                 case PUSHTYPE_LOOKUP  : actions.lookup( in.readUI8() ); break;
301                 default:
302             }
303         }
304     }
305     
306     protected static class ActionRecord
307     {
308         public int offset;   //byte offset from start of the action array
309         public int code;
310         public String label;
311         public String jumpLabel;
312         public byte[] data;
313         public int blockDepth = 0;
314         
315         protected ActionRecord( int offset, int code, byte[] data )
316         {
317             this.offset = offset;
318             this.code   = code;
319             this.data   = data;
320         }
321     }
322 
323     /**
324      * First Pass to determine action offsets and jumps
325      */
326     protected Vector createRecords( byte[] bytes ) throws IOException 
327     {            
328         return createRecords( new InStream( bytes ));
329     }
330         
331     /**
332      * First Pass to determine action offsets and jumps
333      */
334     protected Vector createRecords( InStream in ) throws IOException 
335     {        
336         Vector records  = new Vector();
337         Vector jumpers  = new Vector();
338         Vector skippers = new Vector();
339         Hashtable offsetTable = new Hashtable();
340    
341         Stack blockSizes = new Stack();
342         
343         int labelIndex = 0;
344         
345         while( true )
346         {   
347             int offset = (int)in.getBytesRead();
348             
349             //System.out.println( "read=" + offset ); System.out.flush();
350             
351             int code = in.readUI8();                
352             int dataLength = (code >= 0x80) ? in.readUI16() : 0;   
353             byte[] data = ( dataLength > 0 ) ? in.read( dataLength ) : null;
354 
355             //System.out.println( "size=" + dataLength ); System.out.flush();          
356             
357             ActionRecord rec = new ActionRecord( offset, code, data );
358             records.addElement( rec );   
359             offsetTable.put( new Integer(offset), rec );
360 
361             if( ! blockSizes.isEmpty() )
362             {      
363                 int depth = blockSizes.size();
364                 rec.blockDepth = depth;
365                 int blockDecrement = ( dataLength > 0 ) ? ( dataLength + 3 ) : 1;
366 
367                 //--subtract the size of this action from all the block sizes
368                 //  in the block stack
369                 for( int i = depth-1; i >= 0; i-- )
370                 {                
371                     int[] blockSize = (int[])blockSizes.elementAt(i);
372                     int size = blockSize[0];
373                 
374                     size -= blockDecrement;
375                     
376                     //--reached end of block ?
377                     if( size <= 0 ) blockSizes.pop();
378                     else blockSize[0] = size;
379                 }
380             }            
381             
382             if( code == 0 ) break; //end of actions
383             
384             else if( code == DEFINE_FUNCTION )
385             {
386                 InStream in2 = new InStream( rec.data );
387                 in2.readString(mStringEncoding);
388                 int params = in2.readUI16();
389                 for( int i = 0; i < params; i++ ) in2.readString(mStringEncoding);        
390                 int blockSize = in2.readUI16();
391                 blockSizes.push( new int[]{ blockSize } );
392             }
393             else if( code == WITH )
394             {
395                 InStream in2 = new InStream( rec.data );
396                 int blockSize = in2.readUI16();
397                 blockSizes.push( new int[]{ blockSize } );                
398             }            
399             else if( code == WAIT_FOR_FRAME || code == WAIT_FOR_FRAME_2 ) 
400             {
401                 skippers.addElement( new Integer(records.size()-1));
402             }
403             else if( code == IF || code == JUMP ) jumpers.addElement( rec );
404         }        
405         
406         //--Tie up the jumpers with the offsets
407         for( Enumeration enum = jumpers.elements(); enum.hasMoreElements(); )
408         {
409             ActionRecord rec = (ActionRecord)enum.nextElement();
410             
411             InStream in2 = new InStream( rec.data );
412             int jumpOffset = in2.readSI16();
413             int offset = rec.offset + 5;
414             int absoluteOffset = offset + jumpOffset;
415             
416             ActionRecord target = 
417                 (ActionRecord)offsetTable.get( new Integer(absoluteOffset) );
418             
419             if( target != null )
420             {
421                 if( target.label == null ) target.label = rec.jumpLabel = "label" + (labelIndex++);
422                 else rec.jumpLabel = target.label;
423             }
424         }
425         
426         //--Tie up the skippers with labels
427         for( Enumeration enum = skippers.elements(); enum.hasMoreElements(); )
428         {
429             int idx = ((Integer)enum.nextElement()).intValue();
430             
431             ActionRecord rec = (ActionRecord)records.elementAt(idx);
432             
433             InStream in2 = new InStream( rec.data );
434             
435             if( rec.code == WAIT_FOR_FRAME ) in2.readUI16();  //skip frame number
436             int skip = in2.readUI8();
437             int skipIndex = idx + skip + 1;
438             
439             if( skipIndex < records.size() )
440             {
441                 ActionRecord target = (ActionRecord)records.elementAt(skipIndex);
442                 
443                 if( target.label == null ) target.label = rec.jumpLabel = "label" + (labelIndex++);
444                 else rec.jumpLabel = target.label;                
445             }
446         }
447         
448         return records;
449     }
450     
451 
452 }