Source code: com/anotherbigidea/flash/readers/TagParser.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.*;
37 import java.util.*;
38 import java.util.zip.*;
39 import com.anotherbigidea.flash.interfaces.*;
40 import com.anotherbigidea.flash.*;
41 import com.anotherbigidea.flash.structs.*;
42 import com.anotherbigidea.io.*;
43 import com.anotherbigidea.flash.writers.*;
44
45 /**
46 * Parse Tags and drive a SWFTagTypes interface.
47 */
48 public class TagParser implements SWFTags, SWFConstants, SWFFileSignature
49 {
50 protected String mStringEncoding = SWFConstants.STRING_ENCODING_MX;
51 protected int mFlashVersion = SWFConstants.FLASH_MX_VERSION;
52
53 protected SWFTagTypes mTagtypes;
54 //protected byte[] contents; //for debugging
55
56 public TagParser( SWFTagTypes tagtypes )
57 {
58 mTagtypes = tagtypes;
59 }
60
61 /**
62 * @see SWFFileSignature#signature(String)
63 */
64 public void signature( String sig ) {
65 if( mTagtypes instanceof SWFFileSignature ) {
66 ((SWFFileSignature) mTagtypes).signature( sig );
67 }
68 }
69
70 /**
71 * Interface SWFTags
72 */
73 public void header( int version, long length,
74 int twipsWidth, int twipsHeight,
75 int frameRate, int frameCount ) throws IOException
76 {
77 mTagtypes.header( version, length,
78 twipsWidth, twipsHeight,
79 frameRate, frameCount );
80
81 //revert to old encoding for pre-MX
82 if( version < SWFConstants.FLASH_MX_VERSION ) {
83 mStringEncoding = SWFConstants.STRING_ENCODING_PRE_MX;
84 }
85
86 mFlashVersion = version;
87 }
88
89 /**
90 * Interface SWFTags
91
92 public void tag( int tagType, boolean longTag, byte[] contents )
93 throws IOException
94 {
95 //this.contents = contents; //for debugging
96
97 int length = ( contents != null ) ? contents.length : 0;
98
99 InStream in = ( length > 0 ) ? new InStream( contents ) : null;
100
101 //System.out.println( "IN Tag " + tagType + " " + longTag + " " + length );
102
103 switch( tagType )
104 {
105 case TAG_END: tagtypes.tagEnd(); break;
106 case TAG_SHOWFRAME: tagtypes.tagShowFrame(); break;
107
108 //case TAG_DEFINESHAPE :
109 //case TAG_DEFINESHAPE2:
110 //case TAG_DEFINESHAPE3: parseDefineShape( tagType, in ); break;
111
112 case TAG_DOACTION : parseDoAction( in ); break;
113 //case TAG_FREECHARACTER : tagtypes.tagFreeCharacter( in.readUI16() ); break;
114 //case TAG_PLACEOBJECT : parsePlaceObject( in, length ); break;
115 case TAG_PLACEOBJECT2 : parsePlaceObject2( in ); break;
116 //case TAG_REMOVEOBJECT : tagtypes.tagRemoveObject( in.readUI16(), in.readUI16() ); break;
117 //case TAG_REMOVEOBJECT2 : tagtypes.tagRemoveObject2( in.readUI16() ); break;
118 //case TAG_SETBACKGROUNDCOLOR : tagtypes.tagSetBackgroundColor( new Color(in) ); break;
119 //case TAG_FRAMELABEL : tagtypes.tagFrameLabel( in.readString() ); break;
120 case TAG_DEFINESPRITE : parseDefineSprite( in ); break;
121 //case TAG_PROTECT : tagtypes.tagProtect( (length>0) ? in.read(length) : null ); break;
122 //case TAG_ENABLEDEBUG : tagtypes.tagEnableDebug( (length>0) ? in.read(length) : null ); break;
123 //case TAG_DEFINEFONT : parseDefineFont( in ); break;
124 //case TAG_DEFINEFONTINFO: parseFontInfo( in, length ); break;
125 //case TAG_DEFINEFONT2 : parseDefineFont2( in ); break;
126 //case TAG_DEFINETEXTFIELD: parseDefineTextField( in ); break;
127
128 //case TAG_DEFINETEXT :
129 //case TAG_DEFINETEXT2 : parseDefineText( tagType, in ); break;
130
131 case TAG_DEFINEBUTTON : parseDefineButton( in ); break;
132 case TAG_DEFINEBUTTON2 : parseDefineButton2( in ); break;
133 //case TAG_DEFINEBUTTONCXFORM : parseButtonCXForm( in ); break;
134
135 //case TAG_EXPORT : parseExport( in ); break;
136 //case TAG_IMPORT : parseImport( in ); break;
137
138 //case TAG_DEFINEQUICKTIMEMOVIE : tagtypes.tagDefineQuickTimeMovie( in.readUI16(), in.readString() ); break;
139
140 //case TAG_DEFINEBITSJPEG2 : parseDefineJPEG2( in, length ); break;
141 //case TAG_DEFINEBITSLOSSLESS : parseDefineBitsLossless( in, length, false ); break;
142 //case TAG_DEFINEBITSLOSSLESS2 : parseDefineBitsLossless( in, length, true ); break;
143
144 //case TAG_DEFINEMORPHSHAPE : parseMorphShape( in ); break;
145
146 //case TAG_NAMECHARACTER: tagtypes.tagNameCharacter ( contents ); break;
147 //case TAG_GENERATOR_TEXT: tagtypes.tagGeneratorText ( contents ); break;
148 //case TAG_TEMPLATECOMMAND: tagtypes.tagGeneratorCommand( contents ); break;
149 //case TAG_FLASHGENERATOR: tagtypes.tagGenerator ( contents ); break;
150 //case TAG_GEN_EXTERNAL_FONT: tagtypes.tagGeneratorFont ( contents ); break;
151 //case TAG_SERIALNUMBER: tagtypes.tagSerialNumber( in.readString() ); break;
152
153 //case TAG_DEFINESOUND: parseDefineSound( in ); break;
154 //case TAG_STARTSOUND: parseStartSound( in ); break;
155 //case TAG_DEFINEBUTTONSOUND: parseDefineButtonSound( in ); break;
156 //case TAG_SOUNDSTREAMHEAD: parseSoundStreamHead( true, in ); break;
157 //case TAG_SOUNDSTREAMHEAD2: parseSoundStreamHead( false, in ); break;
158 //case TAG_SOUNDSTREAMBLOCK: parseSoundStreamBlock( in ); break;
159
160 //case TAG_DEFINEBITS: parseDefineBits( in ); break;
161 //case TAG_JPEGTABLES: parseDefineJPEGTables( in ); break;
162 //case TAG_DEFINEBITSJPEG3: parseDefineBitsJPEG3( in ); break;
163
164 default: //--Unknown Tag Type
165 tagtypes.tag( tagType, longTag, contents );
166 break;
167 }
168 } */
169
170 /**
171 * Interface SWFTags
172 */
173 public void tag( int tagType, boolean longTag, byte[] contents )
174 throws IOException
175 {
176 //this.contents = contents; //for debugging
177
178 int length = ( contents != null ) ? contents.length : 0;
179
180 InStream in = ( length > 0 ) ? new InStream( contents ) : null;
181
182 //System.out.println( "IN Tag " + tagType + " " + longTag + " " + length );
183
184 switch( tagType )
185 {
186 case TAG_END: mTagtypes.tagEnd(); break;
187 case TAG_SHOWFRAME: mTagtypes.tagShowFrame(); break;
188
189 case TAG_DEFINESHAPE :
190 case TAG_DEFINESHAPE2:
191 case TAG_DEFINESHAPE3: parseDefineShape( tagType, in ); break;
192
193 case TAG_DOACTION : parseDoAction( in ); break;
194 case TAG_DOINITACTION : parseDoInitAction( in ); break;
195 case TAG_FREECHARACTER : mTagtypes.tagFreeCharacter( in.readUI16() ); break;
196 case TAG_PLACEOBJECT : parsePlaceObject( in, length ); break;
197 case TAG_PLACEOBJECT2 : parsePlaceObject2( in ); break;
198 case TAG_REMOVEOBJECT : mTagtypes.tagRemoveObject( in.readUI16(), in.readUI16() ); break;
199 case TAG_REMOVEOBJECT2 : mTagtypes.tagRemoveObject2( in.readUI16() ); break;
200 case TAG_SETBACKGROUNDCOLOR : mTagtypes.tagSetBackgroundColor( new Color(in) ); break;
201 case TAG_FRAMELABEL : parseFrameLabel( in, contents ); break;
202 case TAG_DEFINESPRITE : parseDefineSprite( in ); break;
203 case TAG_PROTECT : mTagtypes.tagProtect( (length>0) ? in.read(length) : null ); break;
204 case TAG_ENABLEDEBUG : mTagtypes.tagEnableDebug( (length>0) ? in.read(length) : null ); break;
205 case TAG_DEFINEFONT : parseDefineFont( in ); break;
206 case TAG_DEFINEFONTINFO: parseFontInfo( in, length, false ); break;
207 case TAG_DEFINEFONTINFO2: parseFontInfo( in, length, true ); break;
208 case TAG_DEFINEFONT2 : parseDefineFont2( in ); break;
209 case TAG_DEFINETEXTFIELD: parseDefineTextField( in ); break;
210
211 case TAG_DEFINETEXT :
212 case TAG_DEFINETEXT2 : parseDefineText( tagType, in ); break;
213
214 case TAG_DEFINEBUTTON : parseDefineButton( in ); break;
215 case TAG_DEFINEBUTTON2 : parseDefineButton2( in ); break;
216 case TAG_DEFINEBUTTONCXFORM : parseButtonCXForm( in ); break;
217
218 case TAG_EXPORT : parseExport( in ); break;
219 case TAG_IMPORT : parseImport( in ); break;
220
221 case TAG_DEFINEQUICKTIMEMOVIE : mTagtypes.tagDefineQuickTimeMovie( in.readUI16(), in.readString( mStringEncoding ) ); break;
222
223 case TAG_DEFINEBITSJPEG2 : parseDefineJPEG2( in, length ); break;
224 case TAG_DEFINEBITSLOSSLESS : parseDefineBitsLossless( in, length, false ); break;
225 case TAG_DEFINEBITSLOSSLESS2 : parseDefineBitsLossless( in, length, true ); break;
226
227 case TAG_DEFINEMORPHSHAPE : parseMorphShape( in ); break;
228
229 case TAG_NAMECHARACTER: mTagtypes.tagNameCharacter ( contents ); break;
230 case TAG_GENERATOR_TEXT: mTagtypes.tagGeneratorText ( contents ); break;
231 case TAG_TEMPLATECOMMAND: mTagtypes.tagGeneratorCommand( contents ); break;
232 case TAG_FLASHGENERATOR: mTagtypes.tagGenerator ( contents ); break;
233 case TAG_GEN_EXTERNAL_FONT: mTagtypes.tagGeneratorFont ( contents ); break;
234 case TAG_SERIALNUMBER: mTagtypes.tagSerialNumber( in.readString( mStringEncoding ) ); break;
235
236 case TAG_DEFINESOUND: parseDefineSound( in ); break;
237 case TAG_STARTSOUND: parseStartSound( in ); break;
238 case TAG_DEFINEBUTTONSOUND: parseDefineButtonSound( in ); break;
239 case TAG_SOUNDSTREAMHEAD: parseSoundStreamHead( true, in ); break;
240 case TAG_SOUNDSTREAMHEAD2: parseSoundStreamHead( false, in ); break;
241 case TAG_SOUNDSTREAMBLOCK: parseSoundStreamBlock( in ); break;
242
243 case TAG_DEFINEBITS: parseDefineBits( in ); break;
244 case TAG_JPEGTABLES: parseDefineJPEGTables( in ); break;
245 case TAG_DEFINEBITSJPEG3: parseDefineBitsJPEG3( in ); break;
246
247 case TAG_ENABLEDEBUGGER2 : parseEnableDebugger2( in, length ); break;
248
249 default: //--Unknown Tag Type
250 mTagtypes.tag( tagType, longTag, contents );
251 break;
252 }
253 }//*/
254
255 protected void parseEnableDebugger2( InStream in, int length ) throws IOException {
256 int reserved = in.readUI16();
257 mTagtypes.tagEnableDebug2( (length>2) ? in.read(length-2) : null );
258 }
259
260 protected void parseFrameLabel( InStream in, byte[] contents ) throws IOException {
261
262 if( contents != null && contents.length >= 2 ) {
263 int len = contents.length;
264 if( contents[len-1] == 1 && contents[len-2] == 0 ) {
265 //is an anchor label
266 mTagtypes.tagFrameLabel( in.readString( mStringEncoding ), true );
267 return;
268 }
269 }
270
271 mTagtypes.tagFrameLabel( in.readString( mStringEncoding ) );
272 }
273
274 protected void parseDefineSound( InStream in ) throws IOException
275 {
276 int id = in.readUI16();
277 int format = (int)in.readUBits( 4 );
278 int frequency = (int)in.readUBits( 2 );
279 boolean bits16 = in.readUBits(1) != 0;
280 boolean stereo = in.readUBits(1) != 0;
281 int sampleCount = (int)in.readUI32();
282
283 byte[] soundData = in.read();
284
285 mTagtypes.tagDefineSound( id, format, frequency, bits16, stereo,
286 sampleCount, soundData );
287 }
288
289 protected void parseStartSound( InStream in ) throws IOException
290 {
291 int id = in.readUI16();
292 SoundInfo info = new SoundInfo( in );
293
294 mTagtypes.tagStartSound( id, info );
295 }
296
297 protected void parseDefineButtonSound( InStream in ) throws IOException
298 {
299 int id = in.readUI16();
300
301 int rollOverSoundId = in.readUI16();
302 SoundInfo rollOverSoundInfo = (rollOverSoundId==0) ? null : new SoundInfo( in );
303
304 int rollOutSoundId = in.readUI16();
305 SoundInfo rollOutSoundInfo = (rollOutSoundId==0) ? null : new SoundInfo( in );
306
307 int pressSoundId = in.readUI16();
308 SoundInfo pressSoundInfo = (pressSoundId==0) ? null : new SoundInfo( in );
309
310 int releaseSoundId = in.readUI16();
311 SoundInfo releaseSoundInfo = (releaseSoundId==0) ? null : new SoundInfo( in );
312
313 mTagtypes.tagDefineButtonSound( id,
314 rollOverSoundId, rollOverSoundInfo,
315 rollOutSoundId, rollOutSoundInfo,
316 pressSoundId, pressSoundInfo,
317 releaseSoundId, releaseSoundInfo );
318
319 }
320
321 protected void parseSoundStreamHead( boolean adpcmOnly, InStream in ) throws IOException
322 {
323 int reserved = (int)in.readUBits(4);
324 int playbackFreq = (int)in.readUBits(2);
325 boolean playback16bits = ( in.readUBits(1) != 0 );
326 boolean playbackStereo = ( in.readUBits(1) != 0 );
327
328 int format = (int)in.readUBits(4);
329 int streamFreq = (int)in.readUBits(2);
330 boolean stream16bits = ( in.readUBits(1) != 0 );
331 boolean streamStereo = ( in.readUBits(1) != 0 );
332 int avgSampleCount = (int)in.readUI16();
333
334 //--MP3 Streams sometimes have an extra word here...
335 //if( format == SWFConstants.SOUND_FORMAT_MP3 )
336 //{
337 // int unknown = in.readUI16();
338 //}
339 //--but other SDK's don't know about this so we can't assume it
340 // will always be present in a SWF.
341
342 if( adpcmOnly )
343 {
344 mTagtypes.tagSoundStreamHead( playbackFreq, playback16bits, playbackStereo,
345 format, streamFreq, stream16bits, streamStereo,
346 avgSampleCount );
347 }
348 else
349 {
350 mTagtypes.tagSoundStreamHead2( playbackFreq, playback16bits, playbackStereo,
351 format, streamFreq, stream16bits, streamStereo,
352 avgSampleCount );
353 }
354 }
355
356 protected void parseSoundStreamBlock( InStream in ) throws IOException
357 {
358 mTagtypes.tagSoundStreamBlock( in.read() );
359 }
360
361 protected void parseDefineBits( InStream in ) throws IOException
362 {
363 mTagtypes.tagDefineBits( in.readUI16(), in.read() );
364 }
365
366 protected void parseDefineJPEGTables( InStream in ) throws IOException
367 {
368 mTagtypes.tagJPEGTables( in.read() );
369 }
370
371 protected void parseDefineBitsJPEG3( InStream in ) throws IOException
372 {
373 int id = in.readUI16();
374 int size = (int)in.readUI32();
375
376 byte[] imageData = in.read( size );
377 byte[] alphaData = in.read();
378
379 mTagtypes.tagDefineBitsJPEG3( id, imageData, alphaData );
380 }
381
382 protected void parseMorphShape( InStream in ) throws IOException
383 {
384 int id = in.readUI16();
385
386 Rect startBounds = new Rect( in );
387 Rect endBounds = new Rect( in );
388
389 int edgeOffset = (int)in.readUI32();
390
391 SWFShape shape = mTagtypes.tagDefineMorphShape( id, startBounds, endBounds );
392
393 if( shape == null ) return;
394
395 //--Read the Fill Styles
396 int fillCount = in.readUI8();
397 if( fillCount == 0xff ) fillCount = in.readUI16();
398
399 for( int i = 0; i < fillCount; i++ )
400 {
401 parseMorphFillStyle( in, shape );
402 }
403
404 //--Read the Line Styles
405 int lineCount = in.readUI8();
406 if( lineCount == 0xff ) lineCount = in.readUI16();
407
408 for( int i = 0; i < lineCount; i++ )
409 {
410 parseMorphLineStyle( in, shape );
411 }
412
413 //--read the start shape
414 parseShape( in, shape, false, true );
415
416 //--read the end shape
417 parseShape( in, shape, false, true );
418 }
419
420 protected void parseMorphLineStyle( InStream in, SWFShape shape ) throws IOException
421 {
422 int startWidth = in.readUI16();
423 int endWidth = in.readUI16();
424
425 AlphaColor startColor = new AlphaColor(in);
426 AlphaColor endColor = new AlphaColor(in);
427
428 shape.defineLineStyle( startWidth, startColor );
429 shape.defineLineStyle( endWidth, endColor );
430 }
431
432 protected void parseMorphFillStyle( InStream in, SWFShape shape ) throws IOException
433 {
434 int fillType = in.readUI8();
435
436 if( fillType == FILL_SOLID )
437 {
438 AlphaColor startColor = new AlphaColor(in);
439 AlphaColor endColor = new AlphaColor(in);
440
441 shape.defineFillStyle( startColor );
442 shape.defineFillStyle( endColor );
443 }
444 else if( fillType == FILL_LINEAR_GRADIENT
445 || fillType == FILL_RADIAL_GRADIENT )
446 {
447 Matrix startMatrix = new Matrix( in );
448 Matrix endMatrix = new Matrix( in );
449
450 int numRatios = in.readUI8();
451
452 int[] startRatios = new int[ numRatios ];
453 AlphaColor[] startColors = new AlphaColor[ numRatios ];
454
455 int[] endRatios = new int[ numRatios ];
456 AlphaColor[] endColors = new AlphaColor[ numRatios ];
457
458 for( int i = 0; i < numRatios; i++ )
459 {
460 startRatios[i] = in.readUI8();
461 startColors[i] = new AlphaColor(in);
462 endRatios[i] = in.readUI8();
463 endColors[i] = new AlphaColor(in);
464 }
465
466 shape.defineFillStyle( startMatrix, startRatios, startColors,
467 fillType == FILL_RADIAL_GRADIENT );
468
469 shape.defineFillStyle( endMatrix, endRatios, endColors,
470 fillType == FILL_RADIAL_GRADIENT );
471 }
472 else if( fillType == FILL_TILED_BITMAP
473 || fillType == FILL_CLIPPED_BITMAP )
474 {
475 int bitmapId = in.readUI16();
476 Matrix startMatrix = new Matrix( in );
477 Matrix endMatrix = new Matrix( in );
478
479 shape.defineFillStyle( bitmapId, startMatrix,
480 fillType == FILL_CLIPPED_BITMAP );
481
482 shape.defineFillStyle( bitmapId, endMatrix,
483 fillType == FILL_CLIPPED_BITMAP );
484 }
485 }
486
487 protected void parseDefineJPEG2( InStream in, int length ) throws IOException
488 {
489 int id = in.readUI16();
490
491 //--read the image data
492 byte[] image = in.read( length - 2 );
493
494 mTagtypes.tagDefineBitsJPEG2( id, image );
495 }
496
497 protected void parseDefineBitsLossless( InStream in, int length,
498 boolean hasAlpha )
499 throws IOException
500 {
501 int id = in.readUI16();
502 int format = in.readUI8();
503 int width = in.readUI16();
504 int height = in.readUI16();
505
506 int size = 0;
507
508 switch( format )
509 {
510 case BITMAP_FORMAT_8_BIT: size = in.readUI8() + 1; break;
511 case BITMAP_FORMAT_16_BIT: size = in.readUI16() + 1; break;
512 case BITMAP_FORMAT_32_BIT: size = 0; break;
513 default: throw new IOException( "unknown bitmap format: " + format );
514 }
515
516 byte[] data = in.read( length - (int)in.getBytesRead() );
517
518 //--unzip the data
519 ByteArrayInputStream bin = new ByteArrayInputStream(data);
520 InflaterInputStream inflater = new InflaterInputStream( bin );
521 InStream dataIn = new InStream( inflater );
522
523 Color[] colors = hasAlpha ? new AlphaColor[ size ] : new Color[ size ];
524
525 for( int i = 0; i < size; i++ )
526 {
527 colors[i] = hasAlpha ? new AlphaColor( dataIn ) : new Color( dataIn );
528 }
529
530 byte[] imageData = dataIn.read();
531
532 if( hasAlpha )
533 {
534 mTagtypes.tagDefineBitsLossless2( id, format, width, height,
535 (AlphaColor[])colors, imageData );
536 }
537 else
538 {
539 mTagtypes.tagDefineBitsLossless( id, format, width, height, colors, imageData );
540 }
541 }
542
543 protected void parseExport( InStream in ) throws IOException
544 {
545 int count = in.readUI16();
546
547 String[] exportNames = new String[ count ];
548 int[] exportIds = new int[ count ];
549
550 for( int i = 0; i < count; i++ )
551 {
552 exportIds[i] = in.readUI16();
553 exportNames[i] = in.readString( mStringEncoding );
554 }
555
556 mTagtypes.tagExport( exportNames, exportIds );
557 }
558
559 protected void parseImport( InStream in ) throws IOException
560 {
561 String movieName = in.readString( mStringEncoding );
562 int count = in.readUI16();
563
564 String[] importNames = new String[ count ];
565 int[] importIds = new int[ count ];
566
567 for( int i = 0; i < count; i++ )
568 {
569 importIds[i] = in.readUI16();
570 importNames[i] = in.readString( mStringEncoding );
571 }
572
573 mTagtypes.tagImport( movieName, importNames, importIds );
574 }
575
576 protected void parseDefineButton2( InStream in ) throws IOException
577 {
578 int id = in.readUI16();
579
580 boolean trackAsMenu = ( in.readUI8() != 0 );
581
582 int actionOffset = in.readUI16(); //skip first offset
583
584 //--Read multiple button records
585 Vector buttonRecords = ButtonRecord2.read( in );
586
587 SWFActions actions = mTagtypes.tagDefineButton2( id, trackAsMenu, buttonRecords );
588
589 if( actions == null ) return;
590
591 //--Read multiple action records
592 while( actionOffset != 0 )
593 {
594 actionOffset = in.readUI16();
595
596 //--Read the condition flags for this action array
597 int actionConditions = in.readUI16();
598
599 actions.start( actionConditions );
600
601 ActionParser parser = new ActionParser( actions, mFlashVersion );
602 parser.parse( in );
603 }
604
605 actions.done();
606 }
607
608 protected void parseButtonCXForm( InStream in ) throws IOException
609 {
610 int buttonId = in.readUI16();
611 ColorTransform transform = new ColorTransform( in );
612
613 mTagtypes.tagButtonCXForm( buttonId, transform );
614 }
615
616 protected void parseDefineButton( InStream in ) throws IOException
617 {
618 int id = in.readUI16();
619 Vector buttonRecords = ButtonRecord.read( in );
620
621 SWFActions actions = mTagtypes.tagDefineButton( id, buttonRecords );
622
623 if( actions == null ) return;
624
625 actions.start( 0 ); //no conditions
626 ActionParser parser = new ActionParser( actions, mFlashVersion );
627 parser.parse( in );
628 actions.done();
629 }
630
631 protected void parseDefineText( int type, InStream in ) throws IOException
632 {
633 int id = in.readUI16();
634 Rect bounds = new Rect( in );
635 Matrix matrix = new Matrix( in );
636
637 SWFText text = ( type == TAG_DEFINETEXT ) ?
638 mTagtypes.tagDefineText( id, bounds, matrix ) :
639 mTagtypes.tagDefineText2( id, bounds, matrix );
640
641 if( text == null ) return;
642
643 int glyphBits = in.readUI8();
644 int advanceBits = in.readUI8();
645
646 //--Read multiple text records
647 int firstByte;
648
649 while( ( firstByte = in.readUI8()) != 0 )
650 {
651 if( (firstByte & 0x80) == 0 ) //Glyph Record
652 {
653 //--Get number of glyph entries
654 int glyphCount = firstByte & 0x7f;
655
656 int[] glyphs = new int[ glyphCount ];
657 int[] advances = new int[ glyphCount ];
658
659 //--Read the glyph entries
660 for( int i = 0; i < glyphCount; i++ )
661 {
662 glyphs[i] = (int)in.readUBits( glyphBits );
663 advances[i] = in.readSBits( advanceBits );
664 }
665
666 text.text( glyphs, advances );
667 }
668 else //Style Record
669 {
670 int flags = firstByte;
671
672 int fontId = 0;
673
674 if( ( flags & TEXT_HAS_FONT ) != 0 )
675 {
676 fontId = in.readUI16();
677 }
678
679 if( ( flags & TEXT_HAS_COLOR ) != 0 )
680 {
681 text.color( (type == TAG_DEFINETEXT2) ?
682 new AlphaColor( in ) :
683 new Color( in ));
684 }
685
686 if( ( flags & TEXT_HAS_XOFFSET ) != 0 )
687 {
688 text.setX( in.readSI16() );
689 }
690
691 if( ( flags & TEXT_HAS_YOFFSET ) != 0 ) //x & y are in reverse order from flag bits
692 {
693 text.setY ( in.readSI16() );
694 }
695
696 if( ( flags & TEXT_HAS_FONT ) != 0 )
697 {
698 int textHeight = in.readUI16();
699
700 text.font( fontId, textHeight );
701 }
702 }
703 }
704
705 text.done();
706 }
707
708 protected void parseDefineTextField( InStream in ) throws IOException
709 {
710 int id = in.readUI16();
711
712 Rect boundary = new Rect( in );
713
714 int flags = in.readUI16();
715 int fontId = in.readUI16();
716 int fontSize = in.readUI16();
717 AlphaColor textColor = new AlphaColor( in );
718
719 int charLimit = ( (flags & TEXTFIELD_LIMIT_CHARS ) != 0 ) ? in.readUI16() : 0;
720
721 int alignment = in.readUI8();
722 int leftMargin = in.readUI16();
723 int rightMargin = in.readUI16();
724 int indentation = in.readUI16();
725 int lineSpacing = in.readUI16();
726
727 String fieldName = in.readString( mStringEncoding );
728 String initialText = ( (flags & TEXTFIELD_HAS_TEXT ) != 0 ) ? in.readString( mStringEncoding ) : null;
729
730 mTagtypes.tagDefineTextField( id, fieldName, initialText, boundary, flags,
731 textColor, alignment, fontId, fontSize,
732 charLimit, leftMargin, rightMargin,
733 indentation, lineSpacing );
734 }
735
736 protected void parseDefineFont2( InStream in ) throws IOException
737 {
738 int id = in.readUI16();
739 int flags = in.readUI8();
740 int reservedFlags = in.readUI8();
741
742 int nameLength = in.readUI8();
743 String name = new String( in.read( nameLength ) );
744
745 int glyphCount = in.readUI16();
746 Vector glyphs = new Vector();
747
748 int[] offsets = new int[ glyphCount + 1 ];
749 boolean is32 = ( flags & FONT2_32OFFSETS ) != 0;
750 for( int i = 0; i <= glyphCount; i++ )
751 {
752 offsets[i] = is32 ? (int)in.readUI32() : in.readUI16();
753 }
754
755 for( int i = 1; i <= glyphCount; i++ )
756 {
757 int glyphSize = offsets[i] - offsets[i-1];
758 byte[] glyphBytes = in.read( glyphSize );
759 glyphs.addElement( glyphBytes );
760 }
761
762 boolean isWide = (( flags & FONT2_WIDECHARS ) != 0 ) || ( glyphCount > 256 );
763
764 int[] codes = new int[ glyphCount ];
765 for( int i = 0; i < glyphCount; i++ )
766 {
767 codes[i] = isWide ? in.readUI16() : in.readUI8();
768 }
769
770 System.out.println( "glyphCount=" + glyphCount + " flags=" + Integer.toBinaryString( flags ) );
771
772 int ascent = 0;
773 int descent = 0;
774 int leading = 0;
775 int[] advances = new int[0];
776 Rect[] bounds = new Rect[0];
777 int[] kerningCodes1 = new int[0];
778 int[] kerningCodes2 = new int[0];
779 int[] kerningAdjustments = new int[0];
780
781 if( ( flags & FONT2_HAS_LAYOUT ) != 0 )
782 {
783 ascent = in.readSI16();
784 descent = in.readSI16();
785 leading = in.readSI16();
786
787 advances = new int[ glyphCount ];
788
789 for( int i = 0; i < glyphCount; i++ )
790 {
791 advances[i] = in.readSI16();
792 }
793
794 bounds = new Rect[ glyphCount ];
795
796 for( int i = 0; i < glyphCount; i++ )
797 {
798 bounds[i] = new Rect( in );
799 }
800
801 int kerningCount = in.readUI16();
802
803 kerningCodes1 = new int[ kerningCount ];
804 kerningCodes2 = new int[ kerningCount ];
805 kerningAdjustments = new int[ kerningCount ];
806
807 for( int i = 0; i < kerningCount; i++ )
808 {
809 kerningCodes1 [i] = isWide ? in.readUI16() : in.readUI8();
810 kerningCodes2 [i] = isWide ? in.readUI16() : in.readUI8();
811 kerningAdjustments[i] = in.readSI16();
812 }
813 }
814
815 SWFVectors vectors = mTagtypes.tagDefineFont2( id, flags, name, glyphCount,
816 ascent, descent, leading,
817 codes, advances, bounds,
818 kerningCodes1,
819 kerningCodes2,
820 kerningAdjustments );
821
822 if( vectors == null ) return;
823
824 if( glyphs.isEmpty() )
825 {
826 vectors.done();
827 }
828 else
829 {
830 for( Enumeration enum = glyphs.elements(); enum.hasMoreElements(); )
831 {
832 byte[] glyphBytes = (byte[])enum.nextElement();
833
834 InStream glyphIn = new InStream( glyphBytes );
835
836 parseShape( glyphIn, vectors, false, false );
837 }
838 }
839 }
840
841 protected void parseFontInfo( InStream in, int length, boolean isFI2 ) throws IOException
842 {
843 int fontId = in.readUI16();
844
845 //--Read the font name
846 int nameLength = in.readUI8();
847 byte[] chars = in.read( nameLength );
848 String fontName = new String( chars );
849
850 //--Read the font flags
851 int flags = in.readUI8();
852
853 //--Read language code
854 int langCode = isFI2 ? in.readUI8() : 0;
855
856 //--Adjust the body length
857 length -= 4 + nameLength;
858
859 //--Read the Glyph-to-code table
860 boolean wide = (flags & FONT_WIDECHARS) != 0;
861
862 int[] codes = new int[ wide ? ( length / 2 ) : length ];
863
864 for( int i = 0; i < codes.length; i++ )
865 {
866 codes[i] = wide ? in.readUI16() : in.readUI8();
867 }
868 if( isFI2 ) mTagtypes.tagDefineFontInfo2( fontId, fontName, flags, codes, langCode );
869 else mTagtypes.tagDefineFontInfo ( fontId, fontName, flags, codes );
870 }
871
872 protected void parseDefineFont( InStream in ) throws IOException
873 {
874 int id = in.readUI16();
875 int firstOffset = in.readUI16();
876 int numGlyphs = firstOffset / 2;
877
878 SWFVectors vectors = mTagtypes.tagDefineFont( id, numGlyphs );
879
880 if( vectors == null ) return;
881
882 //--Skip the offset table
883 for( int i = 1; i < numGlyphs; i++ )
884 {
885 in.readUI16();
886 }
887
888 for( int i = 0; i < numGlyphs; i++ )
889 {
890 parseShape( in, vectors, false, false );
891 }
892 }
893
894 protected void parseDefineSprite( InStream in ) throws IOException
895 {
896 int id = in.readUI16();
897 int frameCount = in.readUI16();
898
899 SWFTagTypes sstt = mTagtypes.tagDefineSprite( id );
900
901 if( sstt == null ) return;
902
903 TagParser parser = new TagParser( sstt );
904 SWFReader reader = new SWFReader( parser, in );
905 reader.readTags();
906 }
907
908 protected void parsePlaceObject2( InStream in ) throws IOException
909 {
910 boolean hasClipActions = in.readUBits(1) != 0;
911 boolean isClipBracket = in.readUBits(1) != 0;
912 boolean hasName = in.readUBits(1) != 0;
913 boolean hasRatio = in.readUBits(1) != 0;
914 boolean hasColorTransform = in.readUBits(1) != 0;
915 boolean hasMatrix = in.readUBits(1) != 0;
916 boolean hasCharacter = in.readUBits(1) != 0;
917 boolean isMove = in.readUBits(1) != 0;
918
919 int depth = in.readUI16();
920
921 int charId = hasCharacter ? in.readUI16() : 0;
922 Matrix matrix = hasMatrix ? new Matrix( in ) : null;
923 AlphaTransform cxform = hasColorTransform ? new AlphaTransform( in ) : null;
924 int ratio = hasRatio ? in.readUI16() : -1;
925 int clipDepth = isClipBracket ? in.readUI16() : 0;
926 String name = hasName ? in.readString( mStringEncoding ) : null;
927
928 int clipActionFlags = 0;
929
930 if( hasClipActions )
931 {
932 in.readUI16(); //unknown
933 clipActionFlags = in.readUI16(); //compound flags
934 }
935
936 SWFActions actions = mTagtypes.tagPlaceObject2(
937 isMove, clipDepth, depth, charId,
938 matrix, cxform, ratio, name,
939 clipActionFlags );
940
941 if( hasClipActions && actions != null )
942 {
943 int flags = 0;
944
945 while( (flags = in.readUI16()) != 0 )
946 {
947 int length = (int)in.readUI32();
948
949 actions.start( flags );
950 ActionParser parser = new ActionParser( actions, mFlashVersion );
951
952 parser.parse( in );
953 }
954
955 actions.done();
956 }
957 }
958
959 protected void parsePlaceObject( InStream in, int length ) throws IOException
960 {
961 mTagtypes.tagPlaceObject(
962 in.readUI16(), //char id
963 in.readUI16(), //depth
964 new Matrix( in ),
965 ( in.getBytesRead() < length ) ? new AlphaTransform( in ) : null );
966 }
967
968 protected void parseDoAction( InStream in ) throws IOException
969 {
970 SWFActions actions = mTagtypes.tagDoAction();
971
972 if( actions == null ) return;
973
974 actions.start( 0 ); //no conditions
975 ActionParser parser = new ActionParser( actions, mFlashVersion );
976 parser.parse( in );
977 actions.done();
978 }
979
980 protected void parseDoInitAction( InStream in ) throws IOException
981 {
982 int spriteId = in.readUI16();
983 SWFActions actions = mTagtypes.tagDoInitAction( spriteId );
984
985 if( actions == null ) return;
986
987 actions.start( 0 ); //no conditions
988 ActionParser parser = new ActionParser( actions, mFlashVersion );
989 parser.parse( in );
990 actions.done();
991 }
992
993 protected void parseDefineShape( int type, InStream in ) throws IOException
994 {
995 int id = in.readUI16();
996 Rect rect = new Rect( in );
997
998 SWFShape shape = null;
999
1000 switch( type )
1001 {
1002 case TAG_DEFINESHAPE : shape = mTagtypes.tagDefineShape( id, rect ); break;
1003 case TAG_DEFINESHAPE2: shape = mTagtypes.tagDefineShape2( id, rect ); break;
1004 case TAG_DEFINESHAPE3: shape = mTagtypes.tagDefineShape3( id, rect ); break;
1005 default: break;
1006 }
1007
1008 if( shape == null ) return;
1009
1010 parseShape( in, shape,
1011 true /*has style*/,
1012 type == TAG_DEFINESHAPE3 /*has alpha*/ );
1013 }
1014
1015 protected void parseShape( InStream in, SWFVectors vectors,
1016 boolean hasStyle, boolean hasAlpha )
1017 throws IOException
1018 {
1019 SWFShape shape = (vectors instanceof SWFShape) ?
1020 (SWFShape)vectors :
1021 null;
1022
1023 in.synchBits();
1024
1025 if( hasStyle ) parseStyles( in, shape, hasAlpha );
1026
1027 in.synchBits();
1028
1029 int[] numFillBits = new int[] { (int)in.readUBits(4) };
1030 int[] numLineBits = new int[] { (int)in.readUBits(4) };
1031
1032 //--Read the shape records
1033 while( true )
1034 {
1035 int type = (int)in.readUBits(1);
1036
1037 if( type == 1 ) // Edge shape-record
1038 {
1039 boolean isCurved = in.readUBits(1) == 0L;
1040
1041 if( isCurved ) //curve
1042 {
1043 int numBits = ((int)in.readUBits(4)) + 2;
1044
1045 int cx = in.readSBits( numBits );
1046 int cy = in.readSBits( numBits );
1047 int dx = in.readSBits( numBits );
1048 int dy = in.readSBits( numBits );
1049
1050 vectors.curve( cx, cy, dx, dy );
1051 }
1052 else //line
1053 {
1054 int numBits = ((int)in.readUBits(4)) + 2;
1055
1056 boolean generalLine = in.readUBits(1) == 1;
1057
1058 int dx = 0;
1059 int dy = 0;
1060
1061 if( generalLine )
1062 {
1063 dx = in.readSBits( numBits );
1064 dy = in.readSBits( numBits );
1065 }
1066 else // a non-general line
1067 {
1068 boolean vertLine = in.readUBits(1) == 1;
1069
1070 if( vertLine )
1071 {
1072 dy = in.readSBits( numBits );
1073 }
1074 else //horizontal line
1075 {
1076 dx = in.readSBits( numBits );
1077 }
1078 }
1079
1080 vectors.line( dx, dy );
1081 }
1082 }
1083 else //End of records or Change Record
1084 {
1085 int flags = (int)in.readUBits(5);
1086
1087 if( flags == 0 ) break; //end of records
1088
1089 parseChangeRecord( in, flags, vectors, shape, hasAlpha,
1090 numFillBits, numLineBits );
1091 }
1092 }
1093
1094 vectors.done();
1095 }
1096
1097 protected void parseChangeRecord( InStream in, int flags, SWFVectors vectors,
1098 SWFShape shape, boolean hasAlpha,
1099 int[] numFillBits, int[] numLineBits )
1100 throws IOException
1101 {
1102 //System.out.println( "In Change --> " + Integer.toBinaryString( flags ));
1103
1104 boolean hasNewStyles = (flags & 0x10) != 0;
1105 boolean hasLineStyle = (flags & 0x08) != 0;
1106 boolean hasFillStyle1 = (flags & 0x04) != 0; //note reverse order
1107 boolean hasFillStyle0 = (flags & 0x02) != 0; //note reverse order
1108 boolean hasMoveTo = (flags & 0x01) != 0;
1109
1110 if( hasMoveTo )
1111 {
1112 int moveBits = (int)in.readUBits(5);
1113 int moveX = in.readSBits( moveBits );
1114 int moveY = in.readSBits( moveBits );
1115
1116 //System.out.println( "X=" + moveX + ", Y=" + moveY );
1117
1118 vectors.move( moveX, moveY );
1119 }
1120
1121 if( hasFillStyle0 )
1122 {
1123 int fillStyle0 = (int)in.readUBits( numFillBits[0] );
1124
1125 //System.out.println( "fill0=" + fillStyle0 );
1126
1127 if( shape != null ) shape.setFillStyle0( fillStyle0 );
1128 }
1129
1130 if( hasFillStyle1 )
1131 {
1132 int fillStyle1 = (int)in.readUBits( numFillBits[0] );
1133
1134 //System.out.println( "fill1=" + fillStyle1 );
1135
1136 if( shape != null ) shape.setFillStyle1( fillStyle1 );
1137 }
1138
1139 if( hasLineStyle )
1140 {
1141 int lineStyle = (int)in.readUBits( numLineBits[0] );
1142
1143 //System.out.println( "line=" + lineStyle );
1144
1145 if( shape != null ) shape.setLineStyle( lineStyle );
1146 }
1147
1148 if( hasNewStyles )
1149 {
1150 parseStyles( in, shape, hasAlpha );
1151
1152 numFillBits[0] = (int)in.readUBits(4);
1153 numLineBits[0] = (int)in.readUBits(4);
1154 }
1155 }
1156
1157 protected void parseStyles( InStream in, SWFShape shape, boolean hasAlpha )
1158 throws IOException
1159 {
1160 int numFillStyles = in.readUI8();
1161 if( numFillStyles == 0xff ) //larger number format
1162 {
1163 numFillStyles = in.readUI16();
1164 }
1165
1166 for( int i = 0; i < numFillStyles; i++ )
1167 {
1168 parseFillStyle( in, shape, hasAlpha );
1169 }
1170
1171 int numLineStyles = in.readUI8();
1172 if( numLineStyles == 0xff ) //larger number format
1173 {
1174 numLineStyles = in.readUI16();
1175 }
1176
1177 for( int i = 0; i < numLineStyles; i++ )
1178 {
1179 parseLineStyle( in, shape, hasAlpha );
1180 }
1181 }
1182
1183 public void parseLineStyle( InStream in, SWFShape shape, boolean hasAlpha )
1184 throws IOException
1185 {
1186 int width = in.readUI16();
1187 Color color = hasAlpha ? new AlphaColor(in) : new Color(in);
1188
1189 if( shape != null ) shape.defineLineStyle( width, color );
1190 }
1191
1192 public void parseFillStyle( InStream in, SWFShape shape, boolean hasAlpha )
1193 throws IOException
1194 {
1195 int fillType = in.readUI8();
1196
1197 if( fillType == FILL_SOLID )
1198 {
1199 Color color = hasAlpha ? new AlphaColor(in) : new Color(in);
1200
1201 if( shape != null ) shape.defineFillStyle( color );
1202 }
1203 else if( fillType == FILL_LINEAR_GRADIENT
1204 || fillType == FILL_RADIAL_GRADIENT )
1205 {
1206 Matrix matrix = new Matrix( in );
1207
1208 int numRatios = in.readUI8();
1209
1210 int[] ratios = new int[ numRatios ];
1211 Color[] colors = new Color[ numRatios ];
1212
1213 for( int i = 0; i < numRatios; i++ )
1214 {
1215 ratios[i] = in.readUI8();
1216 colors[i] = hasAlpha ? new AlphaColor(in) : new Color(in);
1217 }
1218
1219 if( shape != null )
1220 {
1221 shape.defineFillStyle( matrix, ratios, colors,
1222 fillType == FILL_RADIAL_GRADIENT );
1223 }
1224 }
1225 else if( fillType == FILL_TILED_BITMAP
1226 || fillType == FILL_CLIPPED_BITMAP )
1227 {
1228 int bitmapId = in.readUI16();
1229 Matrix matrix = new Matrix( in );
1230
1231 if( shape != null )
1232 {
1233 shape.defineFillStyle( bitmapId, matrix,
1234 fillType == FILL_CLIPPED_BITMAP );
1235 }
1236 }
1237 }
1238
1239 public static void main( String[] args ) throws IOException
1240 {
1241 FileInputStream in = new FileInputStream ( args[0] );
1242 FileOutputStream out = new FileOutputStream( args[1] );
1243
1244 SWFWriter writer = new SWFWriter( out );
1245 TagWriter tagwtr = new TagWriter( writer );
1246
1247 TagParser parser = new TagParser( tagwtr );
1248 SWFReader reader = new SWFReader( parser, in );
1249
1250 reader.readFile();
1251 out.flush();
1252 out.close();
1253 in.close();
1254 }
1255}