Categories Displayed in Flash

Archive for the 'ByteArray' Category

Why AMF Rocks and beats XML

Ted made a great summary of why AMF is Flash's file format of choice.

http://www.onflex.org/ted/2007/11/abcs-of-amf.php

While I've touted the benefits about persistence and serialization of native classes in previous posts (see persistance or serialization categories). Often XML is preferred for data interchange with servers or text editor compatible configuration. Here Ted had a gem in the comments, comparing why using XML inside of AMF over XML in a text file, rocks!

E4X is super handy for querying the data and returning collections after parsing to xml. That said XML is a native object and can be included in AMF3. The data is stored as a string zlib compressed and when deserialized becomes an XML object again.

bytes.writeBytes( new XML('') );

This gives you zlib compression and benefits of e4x in one go. zlib is very effective on xml and can compress documents as much as 20X, I have seen 300Kb XML files become 7K given tag redundency in the ASCII text.

The XML human readable is important as often for games and such that I build, but frequently it's doesn't make sense to build a configurator given the many excellent XML editors out there. In addition it's very easy for most servers to generate, where they can't generate AMF as easily. But there are two compelling benefits to going that route:
1) As the XML grows, compressing things by up to 20x can improve the responsiveness of the application (like startup of a website), and reduce network costs.

2) XML stored this way is native data type to Flash not a string...meaning it's already an XML accessible to E4X instead of a random string that has to be parsed into XMLNodes. Reconstituting the data out of the AMF stream still takes overhead, but it should be much faster than parsing it from interpreting tags and attributes from the plain text, as most the marshalling code is in C inside the player instead of actionscript. Read better experience for end users.

What occurs to me now is that either a very simple AIR app act in a similar fashion to Winzip for compressing/decompressing text for consumption by flash, or act as a simple text editor, just using a text field.
I'm sure that Adobe will open up the AMF API so that servers can generate raw AMF without using remoting or RMTP. This will open up the possibilities for CMS driven websites to prerender sites daily out of database requests.

Using ByteArray to Serialize AS3 Classes for AIR, Web and Everywhere!

When using AIR's FileStream to write AS3 classes to ByteArray, I've found that the same file format works for loading in via URLLoader, as well as what's uploaded using FTP. This isn't surprising, but wanted to test anyway.

What this means is that ByteArray is a groovy ubiquitous format for storing full classes and dependencies and references, via ByteArray.writeObject and ByteArray.readObject (provided registerClass is used), without nececessarily having to parse them manually though that is still suggested for versioning and class evolution. Meaning you can create a file using AIR , send that file to a friend in email, and have them open it up in a flash based webpage, and they operating in a webpage could Http POST the change back to you. The ByteArray can have rich complexity, classes, self references, collections, etc. This could be used to save whole game states (even massive multiplayer, NPC brains, etc). Or in my target case debugging outside other non-AIR runtimes to speed development.

How to save to the same directory the swf AIR is running from

This took me a bit to figure out. Primarily as I'm using AIR to write the files in Flash CS3 and it's not immediately apparent in the docs, how to target the 'self' directory of the swf. This isn't recommended in the docs (which I agree) but in the case I'm using it for it makes sense to do so. The API for AIR is here

  1.  
  2.                 // this is in url path
  3.                 var file:File = File.applicationDirectory.resolvePath( "example.jpg" );
  4.                 trace("file " + file.url + " " +file.nativePath ); //outputs app:/example.jpg, and "C:\SomeDirectory\Somepath\example.jpg"
  5.  
  6.                 var wr:File = new File(file.nativePath);
  7.                 // Create a stream and open the file for asynchronous reading
  8.                 var stream:FileStream = new FileStream();
  9.                 stream.open( wr, FileMode.WRITE );
  10.                 stream.write.....
  11.                 stream.close();
  12.  

This would be swf._url in AS2, or in AS3 DisplayObject.loaderInfo.url , as when using relative links in URLLoader to load whatever you're saving this will resolve properly in AIR. Turns out flash.filesystem.File.applicationDirectory is the droid you are looking for. It's not fully qualified (e.g. file:C:\somedirectory...) when traced it looks like:

"app-resource:/yourSwfNameGoesHere.swf"

unless you use the file.nativePath which is required in order to actually write to the directory. So you can use still this in old school string name parsing the swf name out, so you don't have to use File.applicationResourceDirectory if your looking to make a single swf for both AIR and Web use, and on the web, the AIR libraries won't be accessible.

Here's two handy scripts to help inspect ByteArray's, the first looks at the 1's an 0's, the other looks at the objects in the stream, using the getQualifiedClassName, and if it finds an Array or sub ByteArray attempts to inspect it.
/* converts byteArray to binary like

TraceBits--------------------
0 = 110
1 = 1011
2 = 1001000
3 = 1100101
*/

  1.  
  2. function traceBits(bA:ByteArray) {
  3.         trace("TraceBits--------------------");
  4.         for (var i:int = 0; i < bA.length; i++) {
  5.                 bA.position = i;
  6.  
  7.                 var inte:uint = bA.readByte();
  8.                 trace(i+ " = " + inte.toString(2));
  9.         }
  10. }
  11.  
  12. function traceByteArray(bA:ByteArray) {
  13.         var o;
  14.         var cnt:int = 0;
  15.         var cnm:String;
  16.  
  17.         var o2;
  18.         var cnt2:int = 0;
  19.         var cnm2:String;
  20.         try {
  21.                 while (true) {
  22.                         o = bA.readObject();
  23.                         cnm = getQualifiedClassName(o);
  24.                         trace( cnt++ + " " + o + " : " + cnm);
  25.                         if (cnm == "flash.utils::ByteArray") {
  26.                                 try {
  27.                                         cnt2 = 0;
  28.                                         cnm2 = "";
  29.                                         while (true) {
  30.                                                 o2 = o.readObject();
  31.                                                 trace(" " +  cnt2++ + " " + o2 + " : " + getQualifiedClassName(o2));
  32.  
  33.                                         }
  34.                                 } catch (err:Error) {
  35.                                 }
  36.                         } else if (cnm == "Array") {
  37.                                 trace("array contents...");
  38.                                 try {
  39.                                         var ar:Array = o as Array;
  40.                                         trace("ar " + ar);
  41.                                         trace(ar.join("\r"));
  42.                                 } catch (err:Error) {
  43.                                         trace(err.toString());
  44.                                 }
  45.                         }
  46.                 }
  47.         } catch (err:Error) {
  48.         }
  49. }

DRM, MetaData and swfs.

DRM in flash can be tricky, how is it you identify and pass id's to a swf duplicated a thousand times to know where it goes?
1) A temp approach is using server generated FlashVars in AS3, good for session ID's
http://blogs.adobe.com/pdehaan/2006/07/using_flashvars_with_actionscr.html
2) First not visible to actionscript is the metadata tag, preferrably writing it on th fly:

http://tutorials.lastashero.com/2005/10/using_swf_metadata_in_flash_8.html (via the IDE)

The metadata can easily be retrieved by some search engine swf plugins, it's actually what it was designed for.
There is a $30 dollar utility to batch do this to your swfs:

http://www.polarswf.com/

3) Accessible to actionscript is  rewriting of variables in the swf This is useful when root vars aren't an option, and or license keys (e.g. encrypted tokens) need to be embedded into the swf.
http://neurofuzzy.net/index.php?p=25

Both 2 and 3 can be accomplished via AS3 and ByteArray. Though I haven't tried it yet.

AS3 Serialization and Deserialization: ByteArray BitmapData registerClassAlias

Grr. Flash's registerClassAlias does not work with BitmapData (I wonder ?), so while you can save it to the ByteArray and wherever that can go... you can't get it back! As BitmapData complains about the constructor getting no args, presumeably that memory allocation is fixed upon the constructor.
This means you'll have to roll your own, which isn't too hard but it when having the BitmapData as members of other classes it might be a pain as each will have to serialize/deserialize the BitmapData using the IExternalizable.
Another option might have been overridding, but while BitmapData can be extended there isn't a no arg constructor, so the constructor can't really be overridden efficetively. A Proxy could be created for this.
Andrew has a good workaround, iterating over the pixels, though I've opted to write the width and height into the stream as well. You can use writeObject for the height and widht without problems (versus writing writeInt)
For additional details and others experiencing this problem: search terms "registerClassAlias BitmapData" "deserialize Bitmapdata"

  • http://www.google.com/search?q=deserialize+BitmapData&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:official&client=firefox-a
  • http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?catid=641&threadid=1255888#4547075
  • http://osflash.org/pipermail/red5_osflash.org/2007-April/011091.html

Solutions

  • [1] http://www.cynergysystems.com/blogs/blogs/andrew.trice/BitmapData/SharedDesktop/srcview/
  • [2] http://www.barncar.com/writejpg.mxml (sample code using corelib to save as Jpg)
  • http://www.bytearray.org/?p=90 (a more flexible snapshot of video/swf to jpg using corelib, which is a completely different solution path)
  • corelib is here http://code.google.com/p/as3corelib/

NOTE:

if you do need to use registerClassAlias you can do this trick:

private static const REG:* = registerClassAlias("reflection.IntrospectableObj",IntrospectableObj);