no|wrap.de - Flasm
Description: Flasm is a free command line assembler/disassembler of Flash ActionScript bytecode. It lets you make changes to any SWF. Flasm fully supports SWFs produced by Macromedia Flash 8 and earlier.

Flasm is a free command line assembler/disassembler of Flash ActionScript bytecode. It lets you make changes to any SWF. Flasm fully supports SWFs produced by Macromedia Flash 8 and earlier Flash versions.
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 |