format.abc is the successor to hxAsm and is included in the File Formats library. This library can dynamically compile Flash9 assembly code into ABC (ActionScript Bytecode) which can be executed by the AVM2 (Flash 9+).
- Reader.hx and Writer.hx are used to deserialize/serialize ABC class/method/field information
- Raw binary found in the DoABC / TActionScript3 tag of an SWF
- OpReader.hx and OpWriter.hx are used to deserialize/serialize ABC opcodes for method bodies
- Raw binary found in abcData.functions[i].code
- Data.hx contains the classes and enums
- Context.hx is only used for code generation, and does not contain features for reading existing SWF files
Code Modification example - modify an existing SWF file
Once you parse an SWF using swf.Reader.read(), you'll want to search for the DoABC tag. This contains the raw ABC data. You'll feed this in to abc.Reader.read, which will read all of the ABC metadata, but leave the actual opcodes unparsed. So finally, if you want to get the actual code out of a function, you'll want to use abc.OpReader.decode() to decode a function in ABCData,functions that abc.read() returns.
Code Generation example - generate a new ABC code block
If you need to write out AS3 bytecode, format has some slightly better utils for generating ABC -- check out Context.hx. For example, it's not too hard to generate an empty stub class extending Sprite and link it to a graphic using TSymbolClass.
Reader and Writer don't actually use OpWriter and OpReader to read and write the bytecodes, but instead leave the actual code as binary blobs. This is just like how SWF reads the TActionScript3 tag as Bytes, and you have to parse it with OpReader if you are interested.
The bytecode is stored in abcData.functions[i].code, where each function represents a method or other executable unit of AS3 (remember that AS3 allows weird stuff like code at class level, outside of a method) When you read in the ABC, you'll also want to read the ops you are interested in by using OpReader.decode on abcData.functions.
For writing opcodes, you can either use Context (which uses OpWriter internally) or use manually OpWriter manually to write the ops back to Bytes in abcData.functions. Context creates a whole new ABCData block, so it's mainly good for creating new ABC from scratch. If you are modifying existing ABC, you'll want to do the latter.
The ABC decoding/encoding in format isn't super robust. For example, the opcodes you read in OpReader index into the various arrays in an ABCData, so even just printing things out for debugging is not an easy task (you have to dereference all these indexes). If you need to do some heavy parsing and generation of ABC, you'll probably have to write your own utility classes to make things easier.
If you just need to know which assets map to which AS3 classes, you can look for the TSymbolClass tag in the SWF.
It's not always easy to count bytes when writing a
OJump opcode. There is an API to make it more easy to works with jumps. Here's the modified
fib version that uses this API :
var m = ctx.beginMethod("fib",[tint],tint); m.maxStack = 3; ctx.ops([ OReg(1), OSmallInt(1), ]); var j = ctx.jump(JGt); // prepare a jump ctx.ops([ OInt(1), ORet, ]); j(); // patch the jump with current position ctx.ops([ ODecrIReg(1), OThis, OReg(1), OCallProperty(ctx.property("fib"),1), ODecrIReg(1), OThis, OReg(1), OCallProperty(ctx.property("fib"),1), OOp(OpIAdd), ORet, ]);
ctx.jump method writes a
OJump opcode, then returns a function. This function can be called when you reach the place of the jump target.
There's also a
ctx.backwardJump that works the following :
var j = ctx.backwardJump(); // .... j(JAlways); // jump to saved position
- In order to read an array, first push on the stack the array and the index, then use
- In order to write into an array, first push on the stack the array, the index and the value, then use
- I'm getting a "VerifyError":
#1017: This is a
scope overflowerror. Try increasing your "maxScope" count.
#1018: This is a
scope underflowerror. It means that you are using an operation (such as
OPopScope) while there are not enough scopes in the chain.
#1021: This is an
invalid jump addresserror. This shouldn't occur if you use the Jump API that is presented before.
#1023: This is a
stack overflowerror. Try increasing the
maxStackproperty of your method.
#1024: This is a
stack underflowerror. It means that you are using an operation (such as
ORet) while there are not enough values on the stack.
#1025: This is an
invalid register error. Try increasing the
nRegsproperty of your method.
#1030: This is a
stack unbalancederror. It means that two branches of a Jump result in different stack sizes when they join back. All jumps or code leading to a given position should result in the same stack size.
A good reference of the Flash9 AVM2 instructions can be found here. The names in this reference are not always the same as in
format.abc, but they should be similar. If you edit format/abc/OpWriter.hx you'll see for each opcode the hex code AVM2 is using.
A list of available opcodes, jump styles, and operators can be found in format/abc/Data.hx.
Don't hesitate to ask on the Haxe mailing list if you have any question about