#7 Flex Hack – Reading the compilation date of SWF with the magic of ByteArray

Filed under Flex, Flex Beyound basic, Flex Hacks

One of interesting things about ByteArray is like a big space, you don’t know what you can do with it. By far is the largest mysterious class I ever seen so far. Adobe should explore and implode a lot of examples about their uses, googling a little bit you will find good material about; but not at latest all the power hidden.

Anyway, this hack uses the ability to read bytes instructions inside a SWF file. To achieve the goal of this hack we needed the specification format for SWF10, released by Adobe some time ago. And the help of Paul Sivtsov an experienced developer from Belarus a country that was part of Soviet union. Thanks a lot Paul and your patience with me to explain some details. Paul was researching to do some similar thing in his solution who inspired him to start from here. A sum of SWF instructions for C++ library.

Level: Expert

Requirements: Flex 3.2 SDK

Tip: The Flex compiler (mxmlc) introduces a lot of information inside a SWF when is compiled, a part from Flash CS4 that gives a limited version of SWF, seems that mxmlc should be adopted in the Flash CS4 to a standard compilation process.

The things happen in this Class:

package org.igorcosta.hacks
{
    import flash.display.LoaderInfo;
    import flash.utils.ByteArray;
    import flash.utils.Endian;

   /**
  * Direct reading of SWF file
  * Distributed under the new BSD License
  * @author Paul Sivtsov - ad@ad.by
  */

	public class SWF
	{
		public function SWF()
		{
		}

    ///////////////////////////////////////////////////////////////////////////
    // Returns compilation date of current module
    public static function readCompilationDate(serialNumber: ByteArray = null): Date
    {
      const compilationDate: Date = new Date;
      const DATETIME_OFFSET: uint = 18;

      if (serialNumber == null)
        serialNumber = readSerialNumber();

      /* example of filled SWF_SERIALNUMBER structure
      struct SWF_SERIALNUMBER
      {
        UI32 Id;         // "3"
        UI32 Edition;    // "6"
                         // "flex_sdk_4.0.0.3342"
        UI8 Major;       // "4."
        UI8 Minor;       // "0."
        UI32 BuildL;     // "0."
        UI32 BuildH;     // "3342"
        UI32 TimestampL;
        UI32 TimestampH;
      };
      */

      // the SWF_SERIALNUMBER structure exists in FLEX swfs only, not FLASH
      if (serialNumber == null)
        return null;

      // date stored as uint64
      serialNumber.position = DATETIME_OFFSET;
      serialNumber.endian = Endian.LITTLE_ENDIAN;
      compilationDate.time = serialNumber.readUnsignedInt() + serialNumber.readUnsignedInt() * (uint.MAX_VALUE + 1);

      return compilationDate;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Returns contents of Adobe SerialNumber SWF tag
    public static function readSerialNumber(): ByteArray
    {
      const TAG_SERIAL_NUMBER: uint = 0x29;
      return findAndReadTagBody(TAG_SERIAL_NUMBER);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Returns the tag body if it is possible
    public static function findAndReadTagBody(theTagCode: uint): ByteArray
    {
      // getting direst access to unpacked SWF file
      const src: ByteArray = LoaderInfo.getLoaderInfoByDefinition(SWF).bytes;

      /*
      SWF File Header
      Field      Type  Offset   Comment
      -----      ----  ------   -------
      Signature  UI8   0        Signature byte: “F” indicates uncompressed, “C” indicates compressed (SWF 6 and later only)
      Signature  UI8   1        Signature byte always “W”
      Signature  UI8   2        Signature byte always “S”
      Version    UI8   3        Single byte file version (for example, 0x06 for SWF 6)
      FileLength UI32  4        Length of entire file in bytes
      FrameSize  RECT  8        Frame size in twips
      FrameRate  UI16  8+RECT   Frame delay in 8.8 fixed number of frames per second
      FrameCount UI16  10+RECT  Total number of frames in file
      */

      // skip AVM2 SWF header
      // skip Signature, Version & FileLength
      src.position = 8;
      // skip FrameSize
      const RECT_UB_LENGTH: uint = 5;
      const RECT_SB_LENGTH: uint = src.readUnsignedByte() >> (8 - RECT_UB_LENGTH);
      const RECT_LENGTH: uint = Math.ceil((RECT_UB_LENGTH + RECT_SB_LENGTH * 4) / 8);
      src.position += (RECT_LENGTH - 1);
      // skip FrameRate & FrameCount
      src.position += 4;

      while (src.bytesAvailable > 0)
        with (readTag(src, theTagCode))
      {
        if (tagCode == theTagCode)
          return tagBody;
      }

      return null;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Returns tag from current read position
    private static function readTag(src: ByteArray, theTagCode: uint): Object
    {
      src.endian = Endian.LITTLE_ENDIAN;

      const tagCodeAndLength: uint = src.readUnsignedShort();
      const tagCode: uint = tagCodeAndLength >> 6;
      const tagLength: uint = function(): uint {
        const MAX_SHORT_TAG_LENGTH: uint = 0x3F;
        const shortLength: uint = tagCodeAndLength & MAX_SHORT_TAG_LENGTH;
        return (shortLength == MAX_SHORT_TAG_LENGTH) ? src.readUnsignedInt() : shortLength;
      }();

      const tagBody: ByteArray = new ByteArray;
      if (tagLength > 0)
        src.readBytes(tagBody, 0, tagLength);

      return {
        tagCode: tagCode,
        tagBody: tagBody
      };
    }
  }
}

How to use:





	
		
	
	

15 Comments

  1. Posted January 13, 2009 at 12:47 pm | Permalink

    Igor,

    The code looks really good — but I’m wondering how one would get around the Sandbox security that AIR/FP will impose on the LoaderInfo class trying to look at its own SWF? Or would you suggest creating a dummy SWF to load within the project just to get the date?

  2. Posted January 13, 2009 at 7:44 pm | Permalink

    I tried it in 3.1 and always got a Security Sandbox violation.

    I finally made it work by changing line 69 for const src: ByteArray = Application(Application.application).loaderInfo.bytes

    Thanks!

  3. Posted January 19, 2009 at 1:43 am | Permalink

    @GMalarte : That worked like a champ! wow, this is pretty slick :)

  4. Ciro Alinei
    Posted January 31, 2009 at 1:47 pm | Permalink

    CS3-CS4

  5. Posted February 22, 2009 at 10:26 pm | Permalink

    I was getting the security error on line 69:
    SecurityError: Error #2119: Security sandbox violation: caller http://localhost:8888/index_24.swf cannot access LoaderInfo.applicationDomain owned by http://localhost:8888/index_24.swf.

    If I change it like GMalarte mentioned I get a TypeError instead on the same line:
    TypeError: Error #1009: Cannot access a property or method of a null object reference.

    Can you post an example online when you get a chance? Thanks so much.

  6. Posted February 23, 2009 at 1:53 am | Permalink

    I got it figured out. I needed to wait longer before running this line:
    const src: ByteArray = Application(Application.application).loaderInfo.bytes;

    The Application Complete event seems to be enough time for all the bytes to be loaded.

  7. Posted March 25, 2009 at 6:19 pm | Permalink

    Guys, check this out. You know sometimes FB doesn’t create a new SWF when i think it should. So when i am looking at the swf in the browser and it doesn’t apply my changes i’m left scratching my head. so with this class I get the compile date and compare it to what I’ve stored in a cookie (local shared object). If the compile date is the same as what i’ve stored in the cookie then I know the swf is the same version and its being cached by the browser or that FB hasn’t compiled a new version. This class is great.

    The other use for this is when you upload your swf to a live client site. You can pull in the compile date and put it into the content, “Site created on Jan 1st, 2009″. Then you can know if the client is looking at the same swf as you. http://www.judahfrangipane.com/blog/?p=252

  8. Posted May 21, 2009 at 6:08 am | Permalink

    It is worth mentioning, that you probably will get the:
    SecurityError: Error #2119: Security sandbox violation (…)

    ONLY if you use release build, you will not get runtime securityError while using debug. It is very strange, i will investigate it further, by me it is an internal error of sdk. It is also worth mentioning that the error exists on both flex sdk 3.3 and flex sdk 4.0

  9. flexflip
    Posted November 10, 2009 at 6:00 am | Permalink

    I tried this in a pure AS3 project (FDT), but kept on getting security errors (see also comments above). Has anyone been able to implement this class and if so, what were the steps you took.

  10. kim
    Posted June 15, 2011 at 6:48 am | Permalink

    Golden! Worked straight off (FlashBuilder4 SDK4.0)

  11. Keiran
    Posted August 15, 2011 at 11:47 am | Permalink

    Cool! Finally I found a solution! Thank you so much.
    This one even works with FDT.

  12. Posted October 20, 2011 at 6:43 am | Permalink

    This is really useful. Thanks for sharing !
    By the way, is there other data that you would be able to retrieve such as version of the Flash Builder that compiled the SWF, …

    Thanks again !

  13. Posted October 20, 2011 at 6:54 am | Permalink

    By the way, the spec seems to have changed location:
    http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/swf/pdf/swf_file_format_spec_v10.pdf

  14. Marco
    Posted January 26, 2012 at 11:36 am | Permalink

    To fix the security sandbox I’ve changed the code a bit:
    * removed all the static keyword on methods
    * added the following constructor

    public var loaderInfo:LoaderInfo;
    public function SWFByteCodeReader(li:LoaderInfo)
    {
    loaderInfo=li;
    }
    * changed this (around line 76)

    const src: ByteArray = LoaderInfo.getLoaderInfoByDefinition(SWFByteCodeReader).bytes;

    with this:
    const src: ByteArray = loaderInfo.bytes;

    And no more Security Violation
    Obviously you have to pass this.loaderInfo to the constructor on the applicationComplete event.

  15. Krull
    Posted August 8, 2013 at 11:40 am | Permalink

    To me worked with :
    const src: ByteArray = Application.application.loaderInfo.bytes;

2 Trackbacks

  1. [...] Igor and Paul’s work a bit further I created a class you can drop into your project with one line of code, that will [...]

  2. By Compile time einer SWF festhalten - Flashforum on August 15, 2011 at 11:27 am

    [...] [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*