Archive for April, 2009
Batch compile and update publish profile using JSFL
Some time ago, for my work at Epyc, I ran into a “little” problem: I had to use old Flash animations and load these into a Flex application. When you load an old Flash animation into Flash 9, it becomes an instance of the AVM1Movie class.
The problem with this is that AVM1Movie objects can use methods and properties inherited from the DisplayObject class, but no interoperability (such as calling methods or using parameters) between the AVM1Movie object and AVM2 objects is allowed. I needed to be able to control these animations trough actionscript, and that simply wasn’t possible.
Fortunately, there was no actionscript in the old animation files, so opening the original .fla, and changing the publish profile to Flash 9, solved this problem. But… then I discovered that there were 2500 fla’s that needed to be updated this way. Horror! No way I was going to open all these flash files manually, update the settings and save them.
Flash comes with a javascript API, which allows you to script commands. So you are able to write your own “plugins”. I’ve never used JSFL before, so I did some research first. I found these blogposts on the web, that both covered a part of my problem:
- http://www.stevensacks.net/2008/03/26/using-jsfl-to-change-publish-profile-settings-v2/
- http://miian.com/en/node/399
Throwing them together, and adding some of my own magic… I ended up with this script:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | var tempDoc=undefined; if(fl.documents.length==0){ tempDoc=fl.createDocument(); } xui = fl.getDocumentDOM().xmlPanel(fl.configURI + "Commands/AutoConvertToFlash9.xml"); if(tempDoc){ fl.closeDocument(tempDoc); } var exportlog=""; if(xui.dismiss=="accept"){ var searchSubDir=xui.searchSubDir; var folder=xui.path; if(folder.substr(0,8)!="file:///"){ folder="file:///"+folder.split(":").join("|").split("\\").join("/"); } if(folder.substr(folder.length-1,1)!="/"){ folder=folder+"/"; } } exportlist=new Array(); checkFolder(folder,exportlist,searchSubDir); var totaltime=0; if(exportlist.length==0){ alert("No file need to publish."); }else{ if(confirm(exportlist.length+" files will be updated and published")){ for(var i=0;i<exportlist.length;i++){ setPublishProfileSettings(exportlist[i]); //FLfile.write("file:///c:/exportlog.txt", exportlog);//uncomment if you want to log all files } } } function checkFolder(folder,list,checkSub,pre){ if(pre==undefined){ pre=""; } var flas=FLfile.listFolder(folder+"*.fla","files"); for(var i=0;i<flas.length;i++){ list.push(folder+flas[i]); } if(checkSub=="true"){ var flds=FLfile.listFolder(folder,"directories"); for(var i=0;i<flds.length;i++){ checkFolder(folder+flds[i]+"/",list,checkSub,pre+" "); } } } function setPublishProfileSettings(fileURI) { if (fl.fileExists(fileURI)) { var xml, from, to, delta; var doc=fl.openDocument(fileURI); var fileName = fileURI.split("/").pop(); var folderPath = fileURI.split(fileName)[0]; fileName = fileName.split(".")[0]; var pPath = folderPath + "/_Profile_.xml"; fl.getDocumentDOM().exportPublishProfile(pPath); xml = FLfile.read(pPath); var swfpath=fileName+".swf"; from = xml.indexOf("<flashFileName>"); to = xml.indexOf("</flashFileName>"); delta = xml.substring(from, to); xml = xml.split(delta).join("<flashFileName>"+swfpath); from = xml.indexOf("<Version>"); to = xml.indexOf("</Version>"); delta = xml.substring(from, to); xml = xml.split(delta).join("<Version>9"); from = xml.indexOf("<ActionScriptVersion>"); to = xml.indexOf("</ActionScriptVersion>"); delta = xml.substring(from, to); xml = xml.split(delta).join("<ActionScriptVersion>3"); from = xml.indexOf("<AS3PackagePaths>"); to = xml.indexOf("</AS3PackagePaths>"); delta = xml.substring(from, to); var classPath = "./"; if (fileName.indexOf("/") > -1) { classPath = ""; var splitPath = fileName.split("/"); splitPath.length--; var i = splitPath.length; while (i--) { classPath += "../"; } } xml = xml.split(delta).join("<AS3PackagePaths>" + classPath + "classes"); FLfile.write(pPath, xml); fl.getDocumentDOM().importPublishProfile(pPath); fl.saveDocument( doc); fl.getDocumentDOM().publish(); FLfile.remove(pPath); fl.closeDocument(doc); exportlog+="updated and exported " + fileURI+"\r\n"; } } |
One small problem though. Flash warns you when you try to save an old .fla as a newer version. So, when I ran the script the first time, Flash gave a warning for each of the .fla files. This ment I had to press “ok” 2500 times! Luckily, this is just a setting. This can be turned off under “preferences -> Alerts”.
So, how do you use this?:
- Download the files here
- Extract the script, and place the script and the GUI under “Documents and Settings/[Your login id]/Local Settings/Application Data/Macromedia/[Your flash version]/[language]/Configuration/Commands/”
- Run flash, you will see a new command named “AutoConvertToFlash9″ in the command menu.
- Run the script, everything else should be obvious
I only tested this in CS3, but I think it will work fine under CS4 too. If you are a CS4 user, and this doesn’t work, please let me know and I’ll look into it.
We all know Flash can easiy be decompiled, and we all don’t want people stealing our codes. There are a couple of good commercial products out there that let you obfuscate your actionscript code. None of them are bullet proof, but they surely don’t make it easy for the occasional hacker to get your code.
So, lately I was thinking of ways to make it even harder. And I came up with this:
With actionscript 3 it is possible to load binary files into a ByteArray. It is also possible to load an SWF using a ByteArray as source (using the loadBytes() function of the Loader class). So, my idea was to encrypt an SWF file, load that SWF into a byteArray, decrypt it, and then show it. The decrypted SWF only exists in the memory, which makes it harder to decompile using the many Flash decompilers out there. So, I wrote a little AIR tool that lets you encrypt SWF files using a simple XOR algorithm (it can also be used to decrypt, because if you XOR an already XOR-red file with the same key, it will give you the original back). I then wrote a class that loads in an encrypted SWF file and adds it to the displaylist.
Why on earth did I use XOR for encryption when it’s so “easy” to crack? Because one can still decompile the loader movie, and see how the swf is decrypted. So putting your effort in making a better encryption algorithm is a waste of time. Still, this will prevent 99% of the people who know how to download your swf from decompiling it. So I decided to just use XOR. It leaves the file size intact, which is a big advantage over other encryption methods. It’s also fast, and simple.
This is not bullet-proof. It’s just another barrier. I recommend obfuscating the loader movie with an obfuscation tool like SWF Encrypt. Or even better, also obfuscate the main movie before encrypting it. Anyway, I’m throwing this into the public domain, so we can all figure out our own ways to obfuscate our code. If everyone uses the same techniques, we only make it easier for those who are chasing our codes. So use this as a starting point… and use your imagination.
Here’s the code for the loader (stub) movie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | //copyright 2009 Ward De Langhe package { import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; import flash.net.URLLoader; import flash.net.URLLoaderDataFormat; import flash.net.URLRequest; import flash.utils.ByteArray; public class EncryptedSWFLoader extends Sprite { private var binaryLoader:URLLoader; private static const ENCRYPTED_SWF_URL:String="./encryptedfile.swf";//put the url of the encrypted swf here private static const KEY:String="testkey";//put your encryption key here public function EncryptedSWFLoader() { super(); binaryLoader = new URLLoader(); binaryLoader.addEventListener(Event.COMPLETE, onLoadInit); binaryLoader.dataFormat = URLLoaderDataFormat.BINARY; binaryLoader.load(new URLRequest(ENCRYPTED_SWF_URL)); } private function onLoadInit(e:Event):void { binaryLoader.removeEventListener(Event.COMPLETE, onLoadInit); var animationLoader:Loader = new Loader(); var binaryData:ByteArray = new ByteArray(); binaryData = binaryLoader.data; if(binaryData.length != 0) { XOR(binaryData,KEY); animationLoader.loadBytes(binaryData); addChild(animationLoader); } } private static function XOR(binaryData:ByteArray, key:String):void{ var keyIndex:Number=0; for(var i:Number=0;i binaryData[i]=binaryData[i]^key.charCodeAt(keyIndex); keyIndex++; if(keyIndex>=key.length) keyIndex=0; } } } } |
To create the encrypted SWF files you can use this AIR tool:
XORTool.air
I also uploaded a zip file with the source of the loader (or stub):
encryptedSWFLoader.zip
Here’s how you use these files:
- Use the AIR tool to encrypt your Flash animation.
- Open the .FLA in the zip file above and change the document width and height to size of your original movie.
- Open EncryptedSWFLoader.as and change the value of ENCRYPTED_SWF_URL (at the top of the file) so it points to your encrypted SWF
- Also change the KEY variable to the key you used to encrypt your file
- Compile and you should see your original Flash animation appear
Fun with particles
When I started this blog it wasn’t my intention to write about generative art only. But… here I go again, I just love doing this.
Here’s a new experiment. I had this idea of taking a photo, and let particles wander around on it. I set up some basic rules for these particles. They normally try to steer towards bright areas, but sometimes they can have a mind of their own, and do whatever they want. They also more or less adopt the brightness of the pixel on the source image. On top of that, I added some filters, and this is the result:
Yes, this is the head of president Obama. I didn’t use his portrait as a political statement, its just a face everyone recognizes these days, and there are lots of good portaits of him available on the web.
I think this experiment turned out pretty cool. When I find some more free time I’ll try to optimize and improve it. Maybe even render the process to a movie.



