1 /***
2 * SWFHeader is (c) 2006 Paul Brooks Andrus and is released under the MIT License:
3 * http://www.opensource.org/licenses/mit-license.php
4 *
5 * http://www.brooksandrus.com/blog/2006/08/01/lightweight-swf-header-reader-java/
6 * com.brooksandrus.utils.swf
7 *
8 * Modified to efficiently read only the swf header (Steve Milek)
9 *
10 */
11 package org.apache.portals.gems.flash;
12
13 import java.io.*;
14 import java.util.zip.*;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18
19
20 /***
21 * @author brooks
22 *
23 */
24 public class SWFHeader
25 {
26 protected Log log = LogFactory.getLog( SWFHeader.class );
27 public static final String COMPRESSED = "compressed";
28 public static final String UNCOMPRESSED = "uncompressed";
29
30 private String signature;
31 private String compressionType;
32 private int version;
33 private long size;
34 private int nbits;
35 private int xmax;
36 private int ymax;
37 private int width;
38 private int height;
39 private int frameRate;
40 private int frameCount;
41
42 public SWFHeader()
43 {
44 super();
45 }
46
47 public boolean parseHeader( String fileName )
48 {
49 if ( fileName == null )
50 {
51 log.error( "Name for SWF file is null" );
52 return false;
53 }
54 return manageInputStreamAndParseHeader( null, new File( fileName ) );
55 }
56
57 public boolean parseHeader( File file )
58 {
59 return manageInputStreamAndParseHeader( null, file );
60 }
61
62 public boolean parseHeader( InputStream is )
63 {
64 return manageInputStreamAndParseHeader( is, null );
65 }
66
67 private boolean manageInputStreamAndParseHeader( InputStream is, File file )
68 {
69 boolean inputIsSWF = false;
70 try
71 {
72 if ( is == null && file != null )
73 {
74 is = new FileInputStream( file );
75 }
76 inputIsSWF = doParseHeader( is );
77 }
78 catch ( FileNotFoundException fnfEx )
79 {
80 log.error( "SWF file could not be found", fnfEx );
81 inputIsSWF = false;
82 }
83 catch ( Exception e )
84 {
85 log.error( "Failed to parse SWF input", e );
86 inputIsSWF = false;
87 }
88 finally
89 {
90 try
91 {
92 if ( is != null )
93 {
94 is.close();
95 }
96 }
97 catch ( Exception ex )
98 {
99 log.error( "Failed to close SWF InputStream", ex );
100 inputIsSWF = false;
101 }
102 }
103 return inputIsSWF;
104 }
105
106 private boolean doParseHeader( InputStream is ) throws Exception
107 {
108 byte[] temp = new byte[128];
109
110 byte[] swf = null;
111
112 is.read( temp );
113
114 if ( !isSWF( temp ) )
115 {
116 log.error( "Input does not match SWF format - incorrect file signature" );
117 return false;
118 }
119 else
120 {
121 signature = "" + ( char ) temp[0] + ( char ) temp[1]
122 + ( char ) temp[2];
123 }
124
125 if ( isCompressed( temp[0] ) )
126 {
127 swf = uncompressHeader( temp );
128 compressionType = SWFHeader.COMPRESSED;
129 }
130 else
131 {
132 swf = temp;
133 compressionType = SWFHeader.UNCOMPRESSED;
134 }
135
136
137
138
139 version = swf[3];
140
141
142 size = readSize( swf );
143
144
145
146 nbits = ( ( swf[8] & 0xff ) >> 3 );
147
148 PackedBitObj pbo = readPackedBits( swf, 8, 5, nbits );
149
150 PackedBitObj pbo2 = readPackedBits( swf, pbo.nextByteIndex,
151 pbo.nextBitIndex, nbits );
152
153 PackedBitObj pbo3 = readPackedBits( swf, pbo2.nextByteIndex,
154 pbo2.nextBitIndex, nbits );
155
156 PackedBitObj pbo4 = readPackedBits( swf, pbo3.nextByteIndex,
157 pbo3.nextBitIndex, nbits );
158
159 xmax = pbo2.value;
160 ymax = pbo4.value;
161
162 width = convertTwipsToPixels( xmax );
163 height = convertTwipsToPixels( ymax );
164
165 int bytePointer = pbo4.nextByteIndex + 2;
166
167 frameRate = swf[bytePointer];
168 bytePointer++;
169
170
171 int fc1 = swf[bytePointer] & 0xFF;
172 bytePointer++;
173
174 int fc2 = swf[bytePointer] & 0xFF;
175 bytePointer++;
176
177 frameCount = ( fc2 << 8 ) + fc1;
178
179 dumpHeaderToStdOut();
180
181 return true;
182 }
183
184 public void read( byte[] output, byte[] input, int offset )
185 {
186 System.arraycopy( input, offset, output, 0, output.length - offset );
187 }
188
189 public PackedBitObj readPackedBits( byte[] bytes, int byteMarker,
190 int bitMarker, int length )
191 {
192 int total = 0;
193 int shift = 7 - bitMarker;
194 int counter = 0;
195 int bitIndex = bitMarker;
196 int byteIndex = byteMarker;
197
198 while ( counter < length )
199 {
200 for ( int i = bitMarker; i < 8; i++ )
201 {
202 int bit = ( ( bytes[byteMarker] & 0xff ) >> shift ) & 1;
203 total = ( total << 1 ) + bit;
204 bitIndex = i;
205 shift--;
206 counter++;
207
208 if ( counter == length )
209 {
210 break;
211 }
212 }
213 byteIndex = byteMarker;
214 byteMarker++;
215 bitMarker = 0;
216 shift = 7;
217 }
218 return new PackedBitObj( bitIndex, byteIndex, total );
219 }
220
221 public int convertTwipsToPixels( int twips )
222 {
223 return twips / 20;
224 }
225
226 public int convertPixelsToTwips( int pixels )
227 {
228 return pixels * 20;
229 }
230
231 public boolean isSWF( byte[] signature )
232 {
233 String sig = "" + ( char ) signature[0] + ( char ) signature[1]
234 + ( char ) signature[2];
235
236 if ( sig.equals( "FWS" ) || sig.equals( "CWS" ) )
237 {
238 return true;
239 }
240 else
241 {
242 return false;
243 }
244 }
245
246 public boolean isCompressed( int firstByte )
247 {
248 if ( firstByte == 67 )
249 {
250 return true;
251 }
252 else
253 {
254 return false;
255 }
256 }
257
258 public boolean isCompressed()
259 {
260 boolean result = false;
261 if ( signature.equalsIgnoreCase( "CWS" ) )
262 {
263 result = true;
264 }
265 return result;
266 }
267
268
269
270
271 protected byte[] uncompressHeader( byte[] bytes ) throws DataFormatException
272 {
273 Inflater decompressor = new Inflater();
274 byte[] compressed = strip( bytes );
275 decompressor.setInput( compressed );
276 byte[] buffer = new byte[ 56 ];
277 int count = decompressor.inflate( buffer );
278 decompressor.end();
279
280
281 byte[] swf = new byte[ 8 + count ];
282
283 System.arraycopy( bytes, 0, swf, 0, 8 );
284
285 System.arraycopy( buffer, 0, swf, 8, count );
286
287 swf[0] = 70;
288
289 return swf;
290 }
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319 public int readSize( byte[] bytes )
320 {
321 int s = 0;
322 for ( int i = 0; i < 4; i++ )
323 {
324 s = ( s << 8 ) + bytes[i + 4];
325 }
326
327
328
329 s = (((s >>> 24) ) |
330 ((s >> 8) & 0xFF00) |
331 ((s << 8) & 0xFF0000) |
332 ((s << 24))) - 1;
333
334 return s;
335 }
336
337 public byte[] strip( byte[] bytes )
338 {
339 byte[] compressable = new byte[bytes.length - 8];
340 System.arraycopy( bytes, 8, compressable, 0, bytes.length - 8 );
341 return compressable;
342 }
343
344
345 /***
346 * @param args
347 */
348 public static void main( String[] args )
349 {
350 if ( args.length != 1 )
351 {
352 System.err.println( "usage: swf_file" );
353 }
354 else
355 {
356 try
357 {
358 SWFHeader swfH = new SWFHeader();
359 if ( swfH.parseHeader( args[0] ) )
360 {
361 swfH.dumpHeaderToStdOut();
362 }
363 }
364 catch ( Exception e )
365 {
366 System.err.println( e.getMessage() );
367 }
368 }
369
370 }
371
372 public void dumpHeaderToStdOut()
373 {
374 System.out.println( "signature: " + getSignature() );
375 System.out.println( "version: " + getVersion() );
376 System.out.println( "compression: " + getCompressionType() );
377 System.out.println( "size: " + getSize() );
378 System.out.println( "nbits: " + getNbits() );
379 System.out.println( "xmax: " + getXmax() );
380 System.out.println( "ymax: " + getYmax() );
381 System.out.println( "width: " + getWidth() );
382 System.out.println( "height: " + getHeight() );
383 System.out.println( "frameRate: " + getFrameRate() );
384 System.out.println( "frameCount: " + getFrameCount() );
385 }
386
387 /***
388 * @return the frameCount
389 */
390 public int getFrameCount()
391 {
392 return frameCount;
393 }
394
395 /***
396 * @return the frameRate
397 */
398 public int getFrameRate()
399 {
400 return frameRate;
401 }
402
403 /***
404 * @return the nbits
405 */
406 public int getNbits()
407 {
408 return nbits;
409 }
410
411 /***
412 * @return the signature
413 */
414 public String getSignature()
415 {
416 return signature;
417 }
418
419 /***
420 * @return the size
421 */
422 public long getSize()
423 {
424 return size;
425 }
426
427 /***
428 * @return the version
429 */
430 public int getVersion()
431 {
432 return version;
433 }
434
435 /***
436 * @return the xmax
437 */
438 public int getXmax()
439 {
440 return xmax;
441 }
442
443 /***
444 * @return the ymax
445 */
446 public int getYmax()
447 {
448 return ymax;
449 }
450
451 /***
452 * @return the compressionType
453 */
454 public String getCompressionType()
455 {
456 return compressionType;
457 }
458
459 /***
460 * @return the height
461 */
462 public int getHeight()
463 {
464 return height;
465 }
466
467 /***
468 * @return the width
469 */
470 public int getWidth()
471 {
472 return width;
473 }
474
475 }