About ·
Download ·
What's new ·
Usage ·
Flash virtual machine ·
Assembler syntax ·
Embedding Flasm ·
Optimization techniques ·
__bytecode__ ·
File size difference ·
Huge scripts ·
Quirks, bugs and crashes ·
History ·
Project state ·
Resources ·
Terms of use ·
Enjoy
About
Flasm disassembles your entire SWF including all the timelines and events. Looking at disassembly, you learn how the Flash compiler works, which improves your ActionScript skills. You can also do some optimizations on the disassembled code by hand or adjust the code as you wish. Flasm then applies your changes to the original SWF, replacing original actions.
It's also possible to embed Flasm actions in your ActionScript,
making optimizing of large projects more comfortable.
Flasm is not a decompiler. What you get is the human readable representation of SWF bytecodes, not ActionScript source. If you're looking for a decompiler, Flare may suit your needs. However, Flare can't alter the SWF.
Page too long? You don't have to read it all. First, make yourself familiar with usage. Then read flash virtual machine topic to understand the concept of registers and stack. Disassemble some of your SWFs, starting with simpler ones, to see the inner workings of the Flash compiler. The rest of this page tries to address questions you may have at this point.
Download
Most recent Flasm version is 1.62.
Windows binary: flasm16win.zip
Mac OS X binary: flasm16mac.tgz
Linux x86 binary: flasm16linux.tgz
There is no installation procedure. Just create a folder named flasm somewhere and unpack the archive there. To uninstall, delete the folder and you're done. Flasm doesn't touch your system files or registry.
Third-party distributions and translations
FreeBSD port is maintained by Jun Kuriyama, Redhat RPMs are built by Daichi Shinozaki. They may be some versions behind the current and are not tested by me. If something goes wrong, please contact the maintainers. Jaco has translated Flasm manual into Italian.
Want to compile from sources?
Source code, platform independent: flasm16src.zip
You will need gcc or cc compiler with flex , bison , gperf , zlib and zlib-devel packages installed. It should compile well without any changes. Tested on Windows 2000 (Cygwin), Mac OS X, and Linux. For Cygwin, please install mingw , mingw-runtime and mingw-zlib packages too. On Windows, MS Visual C++ and other not entirely POSIX compatible compilers will require plenty of changes to the source. Consider Cygwin.
What's new in Flasm 1.6 series
Flasm 1.62
- Bug fixes, thanks to Petr Ovtchenkov et al.
Flasm 1.61
importAssets2 tag fix
placeObject2 tag fix (Flash 4)
- Calculate path to the
flasm.ini in a more reliable way
Flasm 1.6
- Flash 8: support for
metadata , fileAttributes tags
- Flash 8: support for
placeObject3 , importAssets2 tags (Wang Zhen)
- "Update with Flasm and Preview" JSFL action now works in Flash 8 IDE
- Windows binary includes zlib 1.2.3
- Fixed: names of register parameters of
function2 may contain non-English characters
- Calling Flasm without a command:
flasm foo.flm has the same effect as flasm -a foo.flm
Older changes are listed in CHANGES.TXT included in distribution.
Usage
Flasm is a command line tool. To use it, you have to open DOS window first (Windows). On Mac OS X, open terminal window: Applications/Utilities/Terminal . Then go to the Flasm folder with cd c:\Flasm (Windows) or cd /flasm (Mac/Linux), assuming you saved it here. To execute Flasm, simply type in flasm (Windows) or ./flasm (Mac/Linux). Called without arguments, Flasm will show you the list of possible commands described below.
flasm command filename
command
-d Disassemble SWF file to the console
-a Assemble Flasm project
-u Update SWF file, replace Flasm macros
-b Assemble actions to __bytecode__() instruction or byte sequence
-z Compress SWF with zlib
-x Decompress SWF
-d foo.swf
Disassemble foo.swf to the console. To see action offsets in disassembly set showoffset and hexoffset options in flasm.ini .
-d foo.swf > foo.flm
Disassemble foo.swf , redirect the output to foo.flm . Calling Flasm without a command on a .swf file has the same effect.
-a foo.flm
Assemble foo.flm and update the SWF defined inside. Calling Flasm without a command on a .flm file has the same effect.
The backup of original SWF is created with .$wf extension.
-u foo.swf
Disassemble foo.swf to the temporary file.
Execute Flasm macros embedded in SWF.
Make trivial optimizations automatically: remove double nots, replace 0.0 with 0 , rebuild constant pools, clear register arguments.
Create .$wf backup, update the original SWF.
It's a good idea to update the final version of SWF with flasm -u .
Don't expect the SWF to be noticeably faster, it will just make it a bit smaller.
-b foo.txt
produce __bytecode__ instruction or byte sequence, depending on boutput setting in flasm.ini . Takes as input a simple
action list without any movie or frame declarations. Output is sent to console. Redirect it to file if you wish: flasm -b foo.txt > foo.as When boutput is set to 1 , Flasm produces binary output — probably of use for inserting raw action chunks into swf files build by other tools on server.
-x foo.swf
Decompress foo.swf , create .$wf backup.
-z foo.swf
Compress foo.swf , create .$wf backup.
Source SWF doesn't have to be Flash MX file. However, only Flash MX and later players
will be able to play the resulting compressed file.
Flasm settings are read from the configuration file flasm.ini . Available options are commented in flasm.ini and explained at appropriate places in the documentation. flasm.ini is searched for in the working directory and, if not found, in the directory where the Flasm binary resides.
All errors and warnings go to the console. If you want to log them in a file instead, uncomment logto option in flasm.ini and enter the desired log file name there. Set logmode option to 0 (default) to append new messages to the log file. If logmode is set to 1 the log file will be overwritten each time you run Flasm.
If you like Flasm and use it often, you may want to add it to Windows right-click context menu for SWF files.
The explanation is for Windows 2000, but it should work with minor changes for any Windows version.
Start Windows Explorer. Select View, Folder Options, click the File Types tab, and choose Flash player movie (or similar) type, which stands for SWF file extension. Click Edit button, then click New button. In the Action field enter Disassemble . Click the Browse button, navigate to the Flasm's folder, and double-click on flasm.exe . No parameters are needed. Click OK, Close, and Close again. Now right click on any SWF and choose Disassemble . The disassembly of somename.swf will be stored in file somename.flm in SWF's folder.
Further automating is possible, adding flasm -u for updating SWFs or flasm -a for assembling flm files.
If you don't want to do that, look at WinFlasm — simple windows GUI wrapper for Flasm. Note WinFlasm is old and does not support all Flasm commands.
Flash virtual machine
Stack
Constant pool
Global registers
Local registers
Every ActionScript statement is compiled by Flash into a couple of
simple bytecode actions. For example, a=b*b; is transformed into
constants 'a', 'b'
push 'a', 'b'
getVariable
push 'b'
getVariable
multiply
setVariable
The bytecodes are stored in SWF in binary form. They are interpreted by the virtual machine
of the Flash Player. The code above is the visual representation of the bytecodes, created by Flasm.
I'll call actions inside of a frame or event action blocks.
Flash executes action blocks one after another, so the execution flow inside of a block is never interrupted, neither by event nor by gotoAndPlay() or similar actions. Real parallel execution would be nicer? I'm sure it would dramatically affect player stability, which is great now, considering all things going on in a complex movie.
Stack
Flash virtual machine is stack based, you can not refer to the particular memory location.
The stack is a place in memory where data can be stored so that the
last entered (pushed) value will be extracted (popped) first from the stack.
Every command reads (and pops) operands from stack and pushes the result (if any) onto the stack.
The stack may contain elements of arbitrary type — integers, strings, floats and some others.
If needed, type conversion happens during execution — like in ActionScript.
Often there's no difference between the string '10' , integer 10 or double 10.0 .
Further stack explanation by Robert Penner:
If you're familiar with Array.push and Array.pop , those commands are similar to stack manipulations. The stack is like an array of values, except you can only access the value on top, push another value onto the top, or swap the top two values.
For instance, to add two numbers, you have to push both of them onto the stack, then call add . The add command will pop the top two values off the stack, add them together, and push the value onto the stack.
The pop action leads to no errors if the stack is empty. The special UNDEF value is popped then, that corresponds to the ActionScript's undefined .
These two actions give you additional functionality for stack handling: dup and swap .
dup duplicates the value on top of the stack, swap swaps the two topmost values. Currently Flash doesn't use dup and swap very often as you'll see in disassembly, but they are of great importance for optimization.
Every ActionScript statement, regardless of its complexity, leaves the stack empty after execution. In Flash IDE you don't see the bytecodes and don't have to worry about it.
Making changes to bytecodes with Flasm, however, you should always count what's on stack.
Improper stack manipulation often doesn't lead to any errors in Flash player.
You will not see the 10.000 dead stack entries your loop produced, but the execution will slow down and the SWF probably runs out of memory at some point.
The stack was global in Flash 5. If the value was pushed in frame 1, frame 5 could trace it successfully. It was accessible in movie clips too. With Flash MX the situation changed: Flash Players 6 and 7 flush stack contents after every action block.
Constant pool
At the beginning of every action block where variables, methods or strings are used more than once, Flash creates so called constant pool. In fact, if at least one variable is used twice, the pool is created for all strings in the block.
Here is an example:
constants 'bottom', 'paused', 'aliensleft', 'fire'
Constant pool can hold up to 65535 strings (in theory). These can be addressed later in your actions with 1 byte (first 256 strings in the pool)
or 2 byte (the rest of the pool) reference. Commonly no more than 256 strings are stored, so you rarely meet 2 byte references in SWF. Practically the number of strings is limited by overall size of constants action, which can't exceed 65535 bytes like any other action.
Flasm disassembler abstracts constant references away by default. They are showed as strings. To see actual references in disassembly, set literalconstants option in flasm.ini to 0 . The difference between strings and constant pool members will be obvious then.
Writing push c:1 after the above constants definition means push second constant from the pool (counting from 0). Writing push 'paused' will in turn have the same effect, because Flasm finds the constant in the pool automatically and substitutes string with reference during assembly.
If no previous constant pool declaration is found in the same action block, however, the string 'paused' will be pushed as is. The difference is in code size only, not in execution speed — naturally, the string 'paused' takes five bytes more than one-byte reference. Don't forget to add your strings to the constant pool.
In update mode (flasm -u foo.swf ) Flasm rebuilds all constants, removing empty strings and those referenced only once.
The constant pool defined at the start of the frame is valid for every function in this frame. I've never seen constants defined in functions in disassembly. Every event has its own constant pool though.
Although Flash itself never redefines constant pool in the middle of the action block, theoretically you're allowed to do this. Flasm disassembler versions < 1.52 couldn't really deal with multiple constant pools. Flasm 1.52 will show constant references in c:xx form. To always show strings (resembles Flasm < 1.52 behavior, may be inaccurate) set literalconstants to 2 .
Global registers
Flash virtual machine has 4 global registers that are addressed r:0, r:1, r:2, r:3 .
Accessing variables is much slower than accessing registers, so you should store your most
used variables there. Flash versions before MX 2004 only used r:0 , so there was enough room for optimization. Flash MX 2004's compiler, however, may substitute local variables with other registers — a very good reason to use local variables in ActionScript.
To store something in a register, you should first put this something onto the stack and then execute
setRegister command:
push 'paused'
getVariable
setRegister r:1
Now the value of variable paused is stored in r:1 .
Instead of asking for paused next time, use push r:1 .
Note: Unlike most other commands, setRegister does not pop the top value from stack! If you don't need the value stored in register to be on stack, you should manually pop it.
The value of global register, defined in a particular frame on _root ,
is available to all functions in this frame. If some function is defined or movie clip happens here, it can access or overwrite the register too. It looks like after the showFrame tag occurs in SWF, registers disappear. Generally you don't know what happens to the global register. Of course, calling function A from the middle of function B should leave registers untouched. Flash MX 2004's compiler takes care of it — at the start of the function registers are saved on stack, at the end original values are restored. You should pay some attention here, too.
Local registers
Inside of function2 (Flash Player 6.0.65 and above), and only there, up to 255 local registers exist — from r:0 to r:254 . Why not 256? In the function2 header, the number of local registers used in this function is stored in a byte. At the start of function2 the place for local registers is allocated somewhere in memory. The highest possible value for a byte is 255.
Generally, you don't have to care about the number of allocated registers — Flasm calculates this number automatically, and it's not shown in disassembly. Please take consequent registers numbers — using r:1 and r:254 only forces Flasm and Flash Player to allocate 255 registers, which may have impact on memory.
Since local registers are addressed by the same bytecodes as global registers — setRegister and push r:something , function2 has no access to the global registers. Even more confusing is the scope — imagine you have frame A , function2 B inside of A , and function C inside of B . Now function2 B nicely has its own set of registers, and is totally unaware of global registers. That's OK. But function C will share four global registers with frame A !
Besides of all that, local registers function just like global ones. There's no speed difference, too. To summarize: in SWF7 there are still four global registers everywhere outside function2 , but any function2 may allocate a set of 255 local registers.
Assembler syntax
Data types and push
Control flow
Button events
Play head control
setTarget/setTargetExpr
function2
try/catch/finally
protect/enableDebugger
scriptLimits
Unknown actions
For details on SWF file format, read Macromedia's description and Alexis' SWF Reference. Macromedia has updated the docs for Flash 7 file format in November 2003. For historical reasons Flasm has its own names for some actions, slightly different from Macromedia's names. Important differences and abstractions are described here. If in doubt, look into action.h from Flasm's source distribution.
Every Flasm project must start with movie 'moviename.swf' .
The moviename.swf is the name of your SWF origin.
Don't forget to include the file name in quotes. At assembling time Flasm
first looks here and then tries to overwrite the file. The backup of target SWF is created
with .$wf extension.
If update fails for whatever reason, however, the original file will not be destroyed and no backup will be created.
If compressed attribute is found just after movie name
(movie 'moviename.swf' compressed ), SWF will be compressed after assembling. Original SWF may be compressed or not, compressed keyword decides about compression of updated SWF.
Flasm is case insensitive (excluding string values that may be case sensitive).
If you must use a single quote in your strings, escape it like this: 'it\'s beautiful' .
Alternatively you can include string in double quotes: "it's beautiful" .
Comments look exactly like in ActionScript:
// calculating distance
or multi-line comment:
/* calculating
distance */
Flasm implements #include macro. #include 'loop.flm' will be substituted with the contents of loop.flm .
Nested and multiple includes are allowed too: foo.flm includes routine.flm , which includes loop.flm and calc.flm .
Maximum nesting depth is 10.
I introduced some extra constructs in order to match the SWF structure. These serve as containers for Flash actions: frame , defineButton , defineMovieClip , initMovieClip , movie , on , onClipEvent , placeMovieClip .
Other supported tags:
enableDebugger ,
enableDebugger2 ,
exportAssets ,
fileAttributes .
importAssets ,
importAssets2 ,
metadata ,
protect ,
scriptLimits .
Please don't alter the SWF structure!
It means don't delete, replace or add action block containers! Well, you may add or delete an extra event without causing any damage. But if you remove a frame or change the movie clip id, Flasm will be no more able to find the pendant to it and any subsequent statements at assembling time.
Data types and push
Well, push is the core action in SWF and we'll go a bit more into detail here.
Since you can push all kinds of values onto the stack, the push action has
an internal type attribute in SWF. While you don't see and can't access the push type from within Flasm,
Flasm decides what type to use based on how your data is formatted.
Push type | Number of bytes | What it means | Example |
0 | string length + 1 | string | push 'Hello' |
1 | 4 | float | push Y_PROPERTY |
2 | 0 | null | push NULL |
3 | 0 | undefined | push UNDEF |
4 | 1 | register | push r:2 |
5 | 1 | boolean | push TRUE |
6 | 8 | double | push 3.1415926 |
7 | 4 | integer | push 25 |
8 | 1 | constant (0-255) | push 'Hello' |
9 | 2 | constant (256-65534) | push 'Hello' |
Strings must be included in single or double quotes and may contain escape characters: \b, \f, \n, \r, \t and \\ .
No line break is allowed inside of a string.
If Flasm founds push 'Hello' statement, it first looks into the constant pool
for the current action block. If the string is defined there, 1- or 2-byte reference is pushed (push type 8 or 9 );
if not, the string itself (type 0 ).
Integers are recognized in decimal and hexadecimal notation (0xF0F0 ).
Doubles are decimal: -3.1415926 . The notation 9.4e-10
is supported too. In addition, constants _NAN ,
POSITIVE_INFINITY and NEGATIVE_INFINITY are defined
as double values.
0.0 is considered double; 0 is an integer.
Flash compiler itself always stores 0 as double 0.0 . In update mode Flasm will automatically replace all 0.0 occurrences with 0 , saving 4 bytes per each replace.
Push type 1 is only used by Flash to store property values. Flash 4 stored all number values as strings (push type 0 ), Flash 5+ utilizes push type 7 for integers and push type 6 for floats.
However, Flash is not the only program creating SWFs. I know now of at least one third-party program (3D-Flash Animator),
which uses type 1 for actually storing numbers. So while Flasm will disassemble type 1 to property constant if
possible, all values that couldn't be resolved to any constant will be shown as floats: -3.1415926f
or 100.0f . You can use this notation in your Flasm projects too, saving 4 bytes
per number. Any floating point value which ends with f will be treated as
single-precision float and stored with push type 1 (beware of limited precision). The constants _NANF ,
POSITIVE_INFINITYF and NEGATIVE_INFINITYF are defined too.
One push statement can handle multiple values of different types:
push 'Hello', 3.141, XSCALE_PROPERTY . It's not just a shortcut
in Flasm for 3 single push actions, but a shorter and faster way.
Control flow
Jumps inside of the action block are implemented with branch and branchIfTrue actions. Every high level ActionScript construct like if (..) then .. else .. or while (..) do .. is converted to some branch/branchIfTrue pattern. branch simply jumps to the specified label. For example, the translation of if .. then .. else construct always has a branch after its then part, which skips the else part and jumps forward to the end of if . Backward jumps are allowed too, loops always contain them. branchIfTrue takes the condition from stack.
Internally relative numerical branch offsets are stored in SWF after every branch instruction. During disassembling Flasm creates unique label for every offset with the name label1 .. labelN , which hides the branch offset from your eyes and makes the disassembly more readable. The syntax is branch label4 or branchIfTrue label6 . Somewhere in the same action block the label (identifier followed by colon) must be present. You are by no means forced to use identifiers like label5: . Choose meaningful names (LoopStart: , SearchComplete: etc.) instead.
Let's take an example: the really fast countdown loop, which can't be made with Flash (and can't be decompiled to any valid ActionScript).
push 0,1,2,3,4,5,6,7,8,9,10
loopstart:
dup
trace
branchIfTrue loopstart
First 10 values are pushed onto the stack. Note the last pushed value (10 )
will be on top of the stack. We have to duplicate the value in loop with dup ,
because we need it two times: trace pops the first value,
branchIfTrue gets the second as loop condition.
Since branchIfTrue converts condition to boolean,
loop executes until 0 is found, which evaluates to false and stops the loop.
Button events
Every single button event on contains one or multiple of the following:
idleToOverUp | overUpToIdle | overUpToOverDown |
overDownToOverUp | overDownToOutDown | outDownToOverDown |
outDownToIdle | idleToOverDown | overDownToIdle |
keyPress | | |
There's no one-to-one relation of ActionScript button events and events stored in SWF. Some ActionScript events actually set multiple SWF events. That's why the names are different.
keyPress is used in the form keyPress 'char' or keyPpress const ,
for example keyPress 'a' or keyPress _SPACE .
All constants correspond to those in Flash authoring:
_BACKSPACE ,
_DELETE ,
_DOWN ,
_END ,
_ENTER ,
_ESCAPE ,
_HOME ,
_INSERT ,
_LEFT ,
_PAGEDOWN ,
_PAGEUP ,
_RIGHT ,
_SPACE ,
_TAB ,
_UP .
You are free to change button event conditions in Flasm code.
Play head control
The SWF file format describes three actions for this task: gotoFrame (frame number as operand), gotoFrame2 (takes the frame number from stack) and gotoLabel (frame label as operand).
While Flasm's gotoFrame and gotoLabel
actions are named exactly like their SWF format pendants, gotoFrame2 action is not present.
For your convenience gotoFrame2 is showed as gotoAndPlay /gotoAndStop .
In SWF gotoFrame2 is a sole action with a byte flag for play/stop.
Additionally, if you have multiple scenes, Flash puts yet another argument here — the total number of frames in all scenes before the one you're jumping to.
These frames will be skipped by Flash player — in other words, added to the expression on stack.
This allows for using gotoAndPlay /gotoAndStop with
a frame number inside of current scene instead of absolute frame number which starts from the beginning
of SWF. Remember, scenes do not exist in SWF. In this case Flasm will show you something like
gotoAndStop skip 10 . Note you're in trouble if your expression
represents label string instead of integer frame number. Flash player doesn't care and will add
frames to skip here too — and play head jumps to the false frame. Try using _root.gotoAndStop() .
Here movie clip method will be used instead of single instruction. It does no corrections and will
work properly for labels.
Higher Flash versions tend to use gotoAndPlay/gotoAndStop methods of the movie clip object (passing them as strings) to control movie clips.
gotoLabel is rarely seen in disassembly, because Flash
replaces it with frame-based actions exporting SWF.
Only if Flash can't resolve the frame number (the label is not on the same timeline),
gotoLabel will be left as is.
Labels, however, are still present in SWF and may be accessed from javascript
or whatever hosts the SWF, even if jumps to these labels were eliminated.
setTarget and setTargetExpr
setTarget action corresponds to tellTarget in ActionScript.
If target is an expression, setTargetExpr is used, which pops the target string from stack.
Flasm shows it like
setTarget '/defender'
gotoFrame 1
play
end |
or |
setTargetExpr
gotoFrame 1
play
end |
The end statement does not exist in bytecode; Flash uses
setTarget ''
to mark the end of “targeted” statements.
setTarget '/defender'
gotoFrame 1
play
setTarget '' |
or |
setTargetExpr
gotoFrame 1
play
setTarget '' |
Since every setTarget is handled by Flash this way, I decided to make it look more readable. Nesting of setTarget blocks is not allowed.
function2
Flash MX 2004 introduced new function2 bytecode, which works in Flash Player 6.0.65 and above.
function2 is an extended version of function .
In disassembly, it looks like this:
function2 test (r:3='arg1', 'arg2', r:4='arg3') (r:1='this', r:2='_root')
In first parenthesis function arguments are shown.
These arguments may be stored in local registers.
Each function2 has its own set of local registers.
If register is absent, the corresponding argument isn't stored in register and behaves just like normal function argument. If register is present, the corresponding argument is stored there before function2 executes.
You can't access arguments stored in registers by name (with getVariable ), only by r:something (something being numerical or literal, see below).
That means their names are effectively useless in SWF. If clearregisterargs is set to 1 in flasm.ini , flasm -u will remove these names from SWF, making it a bit smaller and forcing decompilers to name arguments arg1, arg2, ..., because actual parameter names will be lost. To the best of my knowledge
it doesn't affect code execution in any way.
Second parenthesis contains “automatic” parameters. Their values are calculated and stored in local registers before function executes, like function arguments. Currently (in SWF7) there are six possible values:
'this' ,
'arguments' ,
'super' ,
'_root' ,
'_parent' and
'_global' .
Internally in SWF the corresponding bits in an unsigned integer
value are set to indicate the presence of such value. For the
sake of understandability Flasm shows them in literal form, however
you can't add your own particular value here or affect register
allocation. Registers are allocated by Flash Player in the above
order, i.e the value of 'this' goes to r:1 , the value of 'arguments'
to r:2 etc. If 'this' is absent, 'arguments' goes to r:1 .
If you accidentally tell Flasm to store automatic values in wrong registers, Flasm will report an error.
So the use of local registers in function2 is threefold: arguments, “automatic” values and local variables are stored there.
If literalregisters flag is set to 1 in flasm.ini , Flasm will disassemble function2 like this:
function2 isSpace (r:1='char')
push r:char
...
end // of function isSpace
instead of
function2 isSpace (r:1='char')
push r:1
...
end // of function isSpace
I.e. all function2 arguments and automatic values like 'this'
will be shown with their literal names after r: . Of course, you're free to write
your own code using literal registers.
r:char in the example above means exactly the same as r:1 ,
and you may use numerical and literal notations together without any problems.
However, you can't name other registers (local variables or like).
You can safely store your own values in local registers, Flasm automatically adjusts the number of registers to allocate for any function2 . This number, although stored in SWF, is invisible in disassembly. A small side-effect: for whatever reason Flash compiler often allocates more registers than needed. Flasm will allocate the minimal possible number.
An edge case: this , arguments and super automatic parameters will be suppressed by Flash player if they don't appear in second parenthesis. They will neither be stored in registers nor accessible by name inside of function2 . Normally, you don't care: if you need one of these, allocate a register for it. In the very special case where you don't want to do it, but still want to access the parameter by name, you can list it without register given. Listing '_root' , '_parent' and '_global' makes no difference. They are always available by name anyway. Nice, what?
The try/catch/finally block
There is a new try-catch-finally construct in Flash MX 2004. In SWF all catch blocks are merged into one, and exception condition checking is done with
normal control flow there. In disassembly, the variable that holds the actual exception will be shown after try keyword, not catch . Like this:
try 'e'
push 'x'
getVariable
throw
branch label1
catch
push 5
trace
label1:
end // of try
Alternatively, condition may be held in a register: try r:2 or try r:something (literal register). Data type exceptions, for example, are always transferred through register, usually r:0 . Other registers are used if error variable is declared local inside of try block (thanks to Alex Bradley for finding this out), or given as parameter to function2 that contains the try/catch/finally block.
The throw action stores condition in a variable or register given after the try keyword automatically, you don't have to do it explicitly.
The condition is then available at the start of catch block.
protect, enableDebugger and enableDebugger2 tags
protect was meant by Macromedia as a hint for authoring program, saying that the author of particular SWF doesn't wish it to be opened in Flash IDE. protect is not actually protecting anything, any program that deals with SWF can simply ignore it. In Flasm, protect will be shown, and can be added/deleted. You can place it anywhere in SWF, albeit usual location is somewhere near to the beginning. Note protect is not an action,
so it has to be outside of action blocks. Passwords are encoded by Flash compiler into a 28 characters long string, consisting of these parts (Paul Cannon):
The $1$ at the start does signify an encryption scheme; it's the traditional way to
indicate a crypt-MD5 password. Everything between the second and third $ is the salt, and everything after the last $ is the hashed password, in a sort of base-64 representation.
Flasm will show the encoded string, but not the password.
enableDebugger is another attempt to secure the content of SWF. Always protected by password (Flasm will show the encoded string), this tag gives you the ability to “remote debug” the SWF. If you don't know the password, debugger will not let you in. If you delete the password, debugger will not let you in. But if you change enableDebugger parameter to '$1$.e$7cXTDev5MooPv3voVnOMX1' , empty password will be accepted.
To say it clear one more time: above tags, including encrypted passwords, give you no protection and can be safely deleted or altered.
Flash MX allows debugging on source code level, so there is a new tag enableDebugger2 , which is used instead of enableDebugger . It makes no difference at all. However, Flasm will not show another tag (63) or contents of external file used by debugger, don't know anything about their format.
scriptLimits tag
Introduced with Flash Player 7, scriptLimits tag gives you control about the maximum recursion depth and the maximum time before the famous "Script causes the movie to run slowly.." message appears. So far I know, these settings can not be changed in Flash IDE. The following syntax is used:
scriptLimits recursion 2000 timeout 10
While increasing recursion depth (256 by default) is surely useful in some situations, you may actually want to decrease the time-out for ActionScript for testing purposes. Instead of standard 15 or 20 seconds, setting the value to 1 or 2 will immediately show you where the bottlenecks are.
Unknown actions support
Flasm 1.6 knows every Flash action, including Flash 8 actions. Only subset of possible bytecodes, however, is currently used by Flash. Part of bytecodes space is reserved for third-party applications. For example, Apple's QuickTime added tag 0xAA for QuickTime actions. Flasm is able to disassemble/assemble actions it doesn't know. The disassembly line looks like
swfAction 0x02 // Unknown action!
If the action has additional data, hexdata part is present:
swfAction 0xAA hexdata 0x43,0x12,0x18 // Unknown action!
The data is shown as comma separated list of hex bytes.
If you define your own actions for some proprietary application, there is no need to include tag length in hexdata field — the length is calculated and added automatically if hexdata keyword is found.
Don't forget, only bytecodes > 0x80 may have additional data.
Embedding Flasm code in ActionScript
If invoked with -u command (flasm -u foo.swf ), Flasm processes macros embedded in your ActionScript and updates the SWF with Flasm statements.
It's not unlike embedding assembler in C or Pascal. The syntax is a bit special to let Flash compile scripts without errors — you have to include Flasm statements in quotes. An example:
"push 'Hello world!'"
"push myTextField"
"setVariable"
The above has the same effect as myTextField = "Hello world!"; ActionScript statement. Semicolons are not required, but will do no harm.
Flasm code strings are allowed everywhere in your scripts, so don't worry about the
right placement. Any restrictions? Sure. Don't define frames or movie clips in embedded Flasm. If you embed, you are already inside of some frame or event definition. Make sure the stack is empty after your embedded code executes. It's not a restriction, but you probably don't want to cause memory leaks.
All Flasm actions behave as expected, there is only one important difference to consider — if you use constants declaration in embedded scripts, Flasm will add them to the main pool in the action block instead of redefining it. The same goes for constants inside of any included file. Using #include as part of the embedded string, please take care of slashes. Use normal slashes and not backslashes in file path. Latter will be escaped,
if not deleted by Flash.
While Flasm works just fine with compression enabled in Flash MX, update process will require two additional steps: decompressing and compressing.
If your computer is slow, you may consider disabling compression in Flash publish settings. You can always compress SWF with flasm -z as last step before distribution.
Testing embedded actions from within Flash IDE
Of course you can export the SWF, update it with Flasm, and check for errors then. Testing directly from Flash IDE would be nicer. Since Flash IDE has no post processing interface, it's rather tricky.
Flash 8 and Flash MX 2004
I've written a dll and a JSFL script which manages to preview your SWF in internal player. Here we go.
- Copy
flasmhelper.dll from helper directory of Flasm distribution to External Libraries directory inside of your Flash configuration folder.
On windows 2000 and XP Flash 8 configuration is located here: C:\Documents and Settings\username\Local Settings\Application Data\Macromedia\Flash 8\en\Configuration . The path will vary for non-English Windows and/or Flash.
- Copy
Update with Flasm and Preview.jsfl to Commands directory inside Flash configuration folder.
- Edit the first line of JSFL script and adjust path to the Flasm executable.
- Start Flash 8/MX 2004. "Update with Flasm and Preview" should be now available under
"Commands". If you wish, associate a shortcut with it.
- You are all set. Execute the command to preview embedded Flasm actions. You have to write some first :) If unsure, re-read previous section.
All Flasm messages and errors should go to the output window now. You may also use trace statements in Flasm code. Please note JSFL isn't that stable. Flash 8/MX 2004 may crash or run out of memory occasionally. This has nothing to do with my particular JSFL code or dll.
The dll is reasonably secure in the sense it will execute only a program called flasm.exe , no command.com or such. It should be enough to prevent simple abusing it from malicious JSFL scripts. The dll is tested under Windows 2000 and XP. Please tell me if it works for you on Windows 98/ME — or if it doesn't.
It shouldn't be too hard to port the library to Mac. To a pity, I don't own a Mac and can't do it myself. If you have interest in porting, drop me a line, I'll make the source available on request. To make sure you have all the prerequisites, try compiling the Mac sample from Macromedia.
Flash 5 or MX
There was no JSFL. Sven König has found a way, and I've implemented it in Flasm. We'll make Flash believe Flasm is a browser.
While proper installation requires some tweaking, it will work like a charm once you got it.
I'll describe the procedure for Windows, but something similar should work on Mac too.
- Copy
flasm.exe and flasm.ini into the Browser subdirectory of Flash.
For Flash 5, the Browser subdirectory is inside of your Flash install folder.
Flash MX, stores settings elsewhere. If Browser subdirectory does not exist, create it.
Windows 2000 or XP: C:\Documents and Settings\username\Application Data\Macromedia\Flash MX\Configuration\Browser
Windows 98 or ME: C:\Windows\Application Data\Macromedia\Flash MX\Configuration\Browser
Windows NT: [Windows directory]\profiles\[username]\Application Data\Macromedia\Flash MX\Configuration\Browser
Mac OS X: Hard Drive/Users/Library/Application Support/Macromedia/FlashMX/Configuration/Browser
Embedding on Mac is untested, please drop me a line if you get it to work.
- Rename
flasm.exe to iexplore.exe
- Create shortcut to your new
iexplore.exe in the same subdirectory. Don't worry, it doesn't affect
the real browser.
- Open
flasm.ini in a text editor.
Change flaplayer and flabrowser
values to contain your Flash player and internet browser path, respectively.
Long file names are not supported in dos, you should first discover what the corresponding short names look like:
"C:\PROGRA~1\INTERN~1\IEXPLORE.EXE" or similar.
Even if you're on Win 2000, please use short names.
Set the value of flatest to “flaplayer ”
if you want to test your files in player, and to “flabrowser ”,
if you'll test in browser. You can change flatest value later while testing without restarting machine or Flash.
or Flash IDE.
- Done. Now open your file in Flash, insert Flasm code, make sure
HTML and “Use Default Names ”
boxes are checked in Flash publish settings, and press F12 (Publish Preview).
Flash will compile the SWF, look for browser shortcut, check the name (iexplore.exe ) is ok, give the HTML file name to Flasm. After calculating the real SWF name Flasm will update the SWF and invoke browser or player to show it.
The DOS box appears for the short time, but will only stay open if there are error messages to report.
I guess (based on my experience) the most popular error would be “Could not start: c:\...\...\foo.exe ”,
because the path in flaplayer or flabrowser is wrong.
Correct it and try again.
Doesn't work? Have you tried it with Flash MX 2004 by chance? MX 2004 doesn't seem to support default browser mechanism any more, use the first dll/JSFL method.
Sometimes Flash just doesn't start Flasm. Enter something in actions window.
Or uncheck HTML in export settings, publish, check it again, publish. Or
delete iexplore shortcut from browser directory, publish, restore shortcut.
#include may fail in Flash IDE if you haven't exported
the SWF to the right location before. Check publish settings.
In Resources you'll find links to the small debugger by Pavils Jurjans and profiler by Ben Schleimer.
Optimization techniques
Measurement
ActionScript optimizations
Flasm optimizations
Double nots
Thanks
Huge bitmaps, not optimized vectors, false frame rates, animating many movie clips at the same time, loading large XML files, dealing with tons of editable text, streaming high quality sound, or simply viewing SWF on mac — in 95% of all cases, bad SWF performance has nothing to do with ActionScript.
Flasm, although being “yet another cool tool”, is no solution for above problems.
Optimizing with Flasm makes sense for games, 3D engines, path finding, actually converting large amounts of data — computing things in general. Flash MX 2004 and Flash 8 made things much better here, too — at least for newer Flash Players.
If you're unsure where is the bottle neck: slow drawing or slow calculating, there is
a simple trick: make the player or browser window very small and switch aliasing off in the Flash Player. If performance increases significantly, you probably should optimize your graphics or movie clip structure first.
Measurement
Don't try to optimize every single line of your code — you'll just make it unreadable,
probably omit some important places, and nobody will ever notice 10.000 hours of your hard work. The key to any optimization is measurement. Bottle necks are very hard to guess. I used to have plenty of tips here, well tested for Flash 5 and Flash 4, but it just can't work this way because of the current diversity of possible environments. Now we have Flash Player 8, 7, 6, 5 and (still) 4 out there, stand-alone applications, mobile devices, not to mention Windows, Mac and Linux. These all are entirely different species. It means only some general strategies still work everywhere, and you have to measure your particular application on your target environment yourself. But how?
Optimize and test for your target environment. Tests in Flash IDE are very rough estimates at best.
You should differentiate between ActionScript code in FLA and compiled code in SWF. For example, if “Omit Trace Actions ” is checked during publishing, traces simply do not make it into SWF. They do not exist there. The same goes for commented code. #include d files are in effect exactly the same as inlined code, since they are first included, then compiled — nothing to test here, too. There are no classes in SWF — just functions. Flash compiler optimizes library function calls with constant arguments too.
Calls like f = Math.sin(0.25) or f = Math.max(3,5)
are never saved in SWF, the calculated values go here. Neither will if parts with condition that always evaluates to false be stored. Some local variables are stored in registers by the compiler, which makes a huge difference. And so on. Before you test, take a look at disassembly.
Compiled bytecodes for Flash Player 7 are, err, context-dependant. Code inside of function2 benefits from local registers. The same code in a frame will not.
In a standard procedural programming language, most of the program time is spent in loops and functions or methods called from those loops. In Flash, frame loops and often or parallel called events should be investigated too.
Ben Schleimer's Flasm Profiler (or is it Flash Profiler?) and David Chang's ASProf are attempts to solve the main problem — what to optimize. I don't know how good they work, because I've not used them in a real-world project yet — they are relatively new. The profiler basically tells you execution times and number of calls for every function.
After you've found what's critical, find better algorithm first, or change your approach in general. Although you could improve the code in small, optimization should be the last resort.
Do all tests in a defined computer state — fresh booted, no virus scans or internet connection in background, all other programs closed. Don't move or resize widows during the test, don't do anything. Don't move your mouse, and let your mouse stay over flash movie. It does make a huge difference. That's not voodoo — OS manages your mouse, and Flash isn't the only running process.
Think about graphics. A script is never interrupted, so it's relatively easy to measure. If, however, you start to measure in the first frame, and end in tenth, you measure everything in between and simply don't know what you measure. The result largely depends on player's mood and takes generally much longer, making your ActionScript test irrelevant. Network requests, gotoAndPlay actions and many other things related to screen refresh are executed asynchronously. Get them out of measured code parts.
Beware of loop overhead. Short test times are not reliable, since the loop itself takes most of the time. Calculate time for an empty loop, function, etc. and subtract it from your results. Use big loops, so that remaining times are bigger then, say, 1000 ms. Computers aren't that exact in ms. Generally, try to isolate the code in question from anything else and measure that code only. Of course, try to get other factors out of consideration first — network bandwidth, graphics etc. Otherwise you results can't be compared because of hidden overhead.
Mac Flash Player is slow compared to PC. Test on Macs early.
Don't execute different tests together. If you try to compare optimization 1 with optimization 2, give them the same environment. One run — one test. If you put both tests in the same frame, you start to deal with caching.
ActionScript optimizations
Flash Player 7 and 8 are much faster with ActionScript. Because of player improvements, and because of compiler improvements in Flash MX 2004/Flash 8. The latter are only noticeable if you compile for Flash Player 6 or higher though. If that's your target audience, you mostly can do now without Flasm optimizations, because the compiler will use registers anyway. You will still be able to achieve better performance with Flasm, but your first step should be getting Flash 8 IDE for critical applications.
I used to elaborate on so called “deprecated Flash 4 actions” here, which are much faster in Flash Player 5 or 6. The worst example: myMC.gotoAndStop() was 25 times slower than
tellTarget("myMC") gotoAndStop() . In Flash Player 7 they finally don't seem to make a real difference. To insist on recommending them, I would have to re-do the whole testing, including mobile devices, so to hell with them.
Action blocks are always executed from the start to the end, no event or gotoAndPlay() will interrupt execution of other code. That's the reason why any large for loop will hang the player, no screen updates are made.
Define local variables in functions with var keyword. Local variables are
faster, generally a good practice, and may be replaced with registers automatically, if compiled with Flash MX 2004/Flash 8.
eval is something special compared to, say, this
or any other ActionScript keyword. In fact, eval is kind of macro — it doesn't have a bytecode, but simply writes its argument onto the stack — at compile time. No doubt it's faster than any method call. Starting with Flash MX, you're no longer allowed to use eval on the left side of assignment. Use set instead.
Unfortunately, identifier length still matters, even in Flash 8, so choose short names for variables. This can be extended to built-in functions too. Creating the function t = Math.tan and substituting all Math.tan occurrences with t will serve 2 purposes: no additional lookup is made for object Math , then for method tan ; and the name itself is shorter. It works only for Flash 5+ methods and functions; Flash 4 functions will slow down.
Of course, names of local variables don't matter if they are stored in registers.
The old trick with replacing b = a*4 to b = a«2 (shift) makes no speed difference in ActionScript.
Flash tries to precalculate constant parts of your expressions. The calculation order results from operator precedence. As Robert Penner noticed, rad = Math.PI/180 will actually store calculated value in SWF, while rad = c*Math.PI/180 will not. Conclusion: explicitly set the precedence to enable precalculation (rad = c*(Math.PI/180) in this case).
for and while loops show no speed difference.
It depends on how you write them. The most optimized ActionScript examples of both, looping down to 0 , produce the same bytecode:
for(var i = 10; i--;) {} and i = 10; while (i--) {}
The third part of the for loop, absent in my example, is actually in the body of loop, so you can't compare it with a normal while .
Avoid multiple parallel hitTest() functions in events — often seen in games.
If the player is killed after any touch with an enemy, and you have 100 duplicated enemy clips,
don't include any code in the enemy clip enterFrame event.
Create the new movie clip and insert the enemy clip here. Then duplicate inside of this parent clip.
Now you can check with only one hitTest() if the collision
takes place. If you need to, use some custom math then to calculate what enemy was hit. Since most of the time no collision occurs, you'll make a really big improvement in fps.
I mostly do not say “3.45 times slower”, because comparisons are very context dependant, exact values will vary. My “slower” just means “noticeably slower, no situation ever makes it faster”.
The list is by no means complete, and will never be. Technology may render some points incorrect, again. Please make your own tests.
Flasm optimizations
After you're done in ActionScript, and the code is still slow, you can start to optimize with Flasm.
Basically only two meaningful low-level features are not accessible from ActionScript and therefore subject of Flasm work: stack and registers.
Let's optimize a simple loop using stack. Our ActionScript is
for (var n=0; n<1000; n++) {
someFunction(n);
}
Flash compiles this loop to the following bytecodes:
constants 'n', 'someFunction'
push 'n', 0.0
varEquals
label1:
push 'n'
getVariable
push 1000
lessThan
not
branchIfTrue label2
push 'n'
getVariable
push 1, 'someFunction'
callFunction
pop
push 'n', 'n'
getVariable
increment
setVariable
branch label1
label2:
|
// Store all variables in constant pool
// Push the string 'n' and starting 0 onto the stack
// Initialize loop counter: n = 0
// Start of the loop
// Get the value of 'n' again
// Push loop bound
// Evaluate boolean condition: “n < 1000?”
// Invert: now “n >= 1000?”
// If “true” is on stack, go to the end of the loop
// Loop body
// Get the value of 'n' again
// Push the number of args (1) and function name
// function call is made with n as argument
// Pop the possible function result away — it's unused
// Push 'n' two times
// Evaluate 'n' again
// n+1 on stack now
// n = n+1
// jump to the loop start — unconditional
// end of the loop — addressed with branchIfTrue above
|
What we immediately see, the n variable is evaluated many times here.
getVariable action is slow compared to stack operations, and the n
is only used as local counter.
Why not discard n , keep the counter on stack and use it over and over,
thus eliminating all getVariable calls?
We also don't need the constant pool declaration, since n will disappear,
and someFunction name will be only used once.
The number of jumps can be reduces to one, too. We know we have to call someFunction(0) ,
so there is no need to check for the condition on the top of the loop.
Look at optimized version:
push 0
loopStart:
dup
push 1, 'someFunction'
callFunction
pop
increment
dup
push 1000
lessThan
branchIfTrue loopStart
pop
|
// No need for double 0.0, integer 0 will do it
// Choosing meaningful name
// dup the counter — our function will eat it up
// Push the number of args (1) and function name
// function call is made with n as argument
// Pop the possible function result away — it's unused
// Now the counter is on top of the stack again
// Increment it
// Dup the counter — condition evaluation will eat it up
// Push loop bound
// Condition evaluation: counter < 1000?
// Jump to the loop start, counter is on top
// Should remove counter from stack after the loop
|
We can go even further. If our function, say, fills an array with some calculated values, it makes no difference
to do it from 0 to 999 or from 999 “down to” 0 . We can eliminate lessThan
action in this case, because branchIfTrue is kind enough to convert
0 to false, and all other numbers to true for us.
push 1000
loopStart:
decrement
dup
push 1, 'someFunction'
callFunction
pop
dup
branchIfTrue loopStart
pop
|
We moved decrement to the top of the loop, because otherwise
branchIfTrue would immediately exit loop if
the counter value is 0 and not let us execute someFunction(0) .
As you see, we end with a pretty clear loop version, which will be much faster than
the original Flash. How much, depends on what someFunction()
does. As the next step you would go there and optimize it.
The best way to learn how to use registers is to compile the same code in Flash MX 2004 for Flash 5, Flash 6, Flash 7, and look at the disassembly. Flash 5 version will use r:0 only, Flash 6 will utilize all four global registers, and Flash 7/8 will add local function2 registers.
Now if your target is Flash 5, you'll see from Flash 6' code what can be done. For higher targets, the room for further optimization is smaller. But there are still many places where the code could be improved — basically by eliminating useless pop s, push es and branch es.
push statements may push multiple values, not just one. Try to merge single push es into one. That's way faster. You'll have to slightly re-arrange the code to do that.
Registers are faster than variables, but still slower than stack.
Why not keep all the values on stack so they go to the top just in the moment you need them?
The problem is, if you're doing this with 2 or more variables, your algorithm may want
to access them in a different order than they're stored.
If some value is only required, say, at the start and at the end of your routine — no problem,
it happily lives somewhere at the bottom, waiting for its time coming, and lets you work
with other values on top. But for often needed values it doesn't work.
While we have swap action to exchange two top values on stack,
we can't directly access the third. Even if you find some illusionistic approach to access
many variables, you'll just slow the execution with big amounts of swap
commands.
Double nots
In certain cases Flash writes double not s in your code.
Consider ActionScript code if (a<=b) { ... } else { ... } Two
inversions are created here by Flash compiler:
push 'b'
getVariable
push 'a'
getVariable
lessThan // a>b?
not // now inverted: a<=b?
not // prepare for branch to the else condition: again a>b?
branchIfTrue elseCondition
As you see, Flash is not very flexible compiling your statements and does not change the order of operands in expression or use another pattern for if statement.
It doesn't really make sense. The only purpose here could be an attempt to force type conversion to boolean. The next action you always see in the code, however,
is branchIfTrue . And this action does type conversion itself.
So Flasm will automatically remove those not s in update mode.
Thanks
My very special thanks go to the people on flashcoders list, whose ideas helped me to
the better understanding of optimization and flowed into above examples:
Rasheed Abdal-Aziz, Ralf Bokelberg, Robin Debreuil, Zeh Fernando, Gary Fixler, Branden Hall, Dave Hayden, Damien Morton, Amos Olson, Robert Penner, Casper Schuirink.
__bytecode__
__bytecode__ function, first mentioned (1. post, 2. post) by Robin Debreuil is a way to inject bytecodes directly into swf without using Flasm (compare embedding). It takes a string filled with hexadecimal numbers as parameter. Since __bytecode__ is evaluated at compile time, it is not possible to give it a variable parameter. While it's convenient to not rely on Flasm updating the swf, __bytecode__ also offers an excellent way to shoot yourself in the foot. No checks are made by compiler here. You better make sure values are correct. Well, using Flasm in -b mode to produce __bytecode__ parameter (or any other tool) at least guarantees proper instruction layout. Some caveats remain though.
__bytecode__ is a function and as such returns a value.
So single pop action is added by Flash IDE if the result is unused. You may also assign the return value to variable, then it will be something else than pop . Normally, an extra pop is harmless.
There are possible complications here with constant pools. Defining your own inside of __bytecode__ will disable the pool automatically produced by compiler. Not defining makes the swf bigger. Define your own pool when needed and let __bytecode__ reside in a place where there is no other ActionScript — don't mix.
File size difference
After assembling Flasm source or updating SWF with Flasm you'll often see your SWF having few less bytes even if you haven't changed anything in the bytecode. Besides of trivial optimizations Flasm does in update mode, there is one more reason for it. Flash may save block lengthes in the SWF as 2 or 6 byte records. 6 bytes are only needed if the block is larger than 62 bytes. Flash, however, often uses 6 bytes where 2 bytes will do. Although Flasm does this too in certain cases, most blocks are optimized during assembling. So I get 400 less bytes on the file of 90kB length without optimizing anything. I don't know of any disadvantages, enjoy this unexpected Flasm bonus. Side note: ironically, there are places where long lengthes are required because of Flash player bugs, but Flasm is aware of this and will let them untouched.
Huge scripts
While it's good practice to keep scripts smaller than 64k (compiled) per frame, it's possible to get larger. But the sole action record — constants, push, function and other is limited to 64k because of 2 bytes length field size.
Since Flash attempts to create the constant pool for all variables and methods, and never creates multiple constant pools, what does it do in such cases? Flash 5 compiler would silently write an overflowed value to the length field without errors or warnings. Later Flash versions are smarter: they would put so many strings as possible into constant pool, other strings just remain in place. Flash MX 2004 will even warn you about classes being too big. However, the compiler doesn't check other places where overflow may occur. Function length (ActionScript 1), for example, isn't verified and will be broken for very big functions.
If you try to execute this kind of SWF, Flash player crashes or actions are omitted. Disassembly will be incomplete and/or wrong. In most cases, Flasm will show an error message.
Quirks, bugs and crashes
Flasm may not be able to disassemble protected SWFs. The protection is usually achieved by inserting some kind of junk into SWF. Generally, such SWFs work in Flash Player, but break the SWF file format spec. Flasm, however, aims to support the spec, not to mimic essentially undefined behavior of some particular Flash Player version. What's my point here? Don't ask me to disassemble something you've downloaded somewhere. Neither will I tune Flasm to overcome any protections. They are not even interesting, and easy to fix with a hex editor.
Windows version of Flasm can't open unicode file names. It seems to be a Cygwin limitation.
When saving flm files in Windows Notepad/Editor, choose ANSI as encoding. In UTF-8 mode Notepad inserts so called byte order mark (BOM) at the start of the file, which irritates Flasm (and many other programs). If you work with UTF-8, please choose another editor, most of them don't add the BOM.
You can't compile something like function 0123ä() in Flash because of parser limitations, the SWF format doesn't impose any restrictions on function names. Since Flasm deals with SWF directly, it should support such names, too. When assembling an SWF, you have to manually place quotes around problematic function names. Note: some of these (unicode names, for example, or Flasm keywords) are perfectly ok with Flash IDE, but interfere with Flasm parser.
An edge case: nested tellTargets don't work if one of them is inside of a function.
Don't know of any other bugs at the moment. If you find a nontrivial bug, fell free to send me your file. Please try to produce a minimal sample where the bug still occurs. The relevant part of source code or FLA is also welcome.
To make it clear: provided the SWF is valid, it must run properly after disassembling and assembling back without changes. The update mode must work too. There are absolutely no voodoo behaviors or unsupported features in Flasm. If you encounter problems, there must be a serious bug in my implementation and your report is highly appreciated.
History
Dave Hayden released Flasm in cooperation with Damien Morton in April 2001.
The first version was able to disassemble the main timeline of the SWF and assemble to the first frame only. Flasm was quite useful already. I was very excited to discover Flasm back then, and soon started to play with source code. I've expanded Flasm's functionality and fixed some bugs. Dave then started the project on sourceforge.net. From 2002 until now I'm the only person developing and maintaining Flasm. Recently, Wang Zhen from Genable Labs has greatly contributed to Flasm 1.6.
Project state
After five years of development Flasm is stable enough to be used in real-life projects and to my best knowledge fully supports all quirks of SWF format. I'm happy that during these time Flasm's source code has helped to develop some third-party software. My special thanks go to all the people who reported problems and suggested improvements. Now I'm busy with other projects and will not be able to implement exciting new features. Please send me your bug reports though, bug fixes will continue to happen.
It's unlikely I'll ever add support for Flash 9. As you may know, Flash Player 9 contains the new virtual machine, which is nothing like the old one. I see it as a natural end of Flasm's life cycle — supporting that would in fact mean writing another Flasm from scratch.
Resources
This page can always be found at http://www.nowrap.de/flasm.html. The mirror at flasm.sourceforge.net is updated from time to time.
The source is available here or may be downloaded from SourceForge's CVS. Project page is http://sourceforge.net/projects/flasm.
Take a look at my another project: Flare, the free ActionScript decompiler.
On the original Flasm page resides the first version and the useful explanation of Flash 5 bytecodes by Dave.
Compare tree animations made by Amos Olson: standard ActionScript version and the optimized one.
Look at path finding swf made with Flasm by Casper Schuirink.
Here is the source.
MTASC, an open source ActionScript II compiler.
ActionScript problems are discussed on the highly frequented flashcoders mailing list,
maintained by Branden Hall.
Alexis' SWF Reference
Macromedia's SWF File Format description
At the prototype site you'll find some Flash functions redefined for speed or flexibility, and also many new and useful ones. Often it's better to start Flasm optimizing from one of them.
Extendflash mailing list deals mostly with JSAPI, which allows customizing Flash MX 2004 and Flash 8 IDEs. Some posts about SWF internals.
KineticFusion by Kinesis Software converts your complete SWF to XML and back. Pricey.
Ben Schleimer's Flasm Profiler shows you what to optimize in the first place.
Pavils Jurjans has written the little debugger for Flasm,
useful while embedding Flasm code in ActionScript. The debugger shows stack and register contents.
Jon Bott's has written an obfuscator, based on Flasm.
For people who don't like to work with command line, and don't like to register Flasm as SWF handler in Windows Explorer either,
there is a WinFlasm by Sharriff Aina,
simple Windows interface to Flasm.
Albert Chosky has created Flasm 1.32 syntax files for EditPlus.
The older Flasm syntax file for UltraEdit, submitted by anonymous Russian flasmer.
Terms of use
Copyright © 2001 Opaque Industries, © 2002-2007 Igor Kogan, © 2005 Wang Zhen
All rights reserved.
Flasm is completely free. It's provided “as is” and without any warranties. Please read the license included in the distribution for details.
Macromedia and Flash are registered trademarks of Adobe Systems Inc.
Adobe does not sponsor, affiliate, or endorse this product.
Enjoy
Igor Kogan
Last significant update: 15 Juni 2007 |
|
|