codemodify

Back to ABC Format Library

Code Modification

This example loads an SWF, parses the ABC bytecode, makes a small modification, and then writes the SWF back out. that Note this uses Reader and Writer to work with classes/methods/fields, and OpReader/OpWriter to work with actual opcodes.

package;
import haxe.io.Bytes;
import haxe.io.BytesInput;
import haxe.io.BytesOutput;
import haxe.Resource;
import format.abc.Data;
 
using Main;

class Main {
    
    static function main() {
        new Main();
    }
    
    public function new() {
        var swfInput = new BytesInput( Resource.getBytes( "in.swf" ) );
        var swfReader = new format.swf.Reader( swfInput );
        var swf = swfReader.read();
        
        for( i in 0...swf.tags.length ) {
            var tag = swf.tags[i];
            switch( tag ) {
                case TSymbolClass( classes ):
                    trace( 'Class linkage: $classes' );
                    
                case TActionScript3( abcBytes, other ):
                    swf.tags[i] = TActionScript3( transformAbc( abcBytes ) );
                    
                case _:
            }
        }
        
        var swfOutput = new BytesOutput();
        var swfWriter = new format.swf.Writer( swfOutput );
        swfWriter.write( swf );
        
        // swfOutput.getBytes() now returns the modified SWF, which you can write to disk.
    }
    
    function transformAbc( abcBytes : Bytes )
    {
        var abcInput = new BytesInput( abcBytes );
        var abcReader = new format.abc.Reader( abcInput );
        
        var abcData = abcReader.read();

        for( cls in abcData.classes ) {
            
            switch( abcData.getName( cls.name ) ) {
                case NName( name, ns ):
                    var nameString = abcData.getString( name );
                    var newNameString = '${nameString}_Modified';
                    abcData.setString( name, newNameString);
                    
                    trace( 'Class: $nameString changed to $newNameString' );
                    
                case _:
                    throw "Unexpected class name";
            }
            
        }
        
        for( f in abcData.functions ) {
            var code = format.abc.OpReader.decode( new BytesInput( f.code ) );
            trace( code );
        }

        var abcOutput = new BytesOutput();
        var abcWriter = new format.abc.Writer( abcOutput );
        abcWriter.write( abcData );
        
        return abcOutput.getBytes();
    }
    
    // Static extensions for easier dereferencing of indices
    public static function getName( abcData : format.abc.Data.ABCData, idx : Index<Name> ) : Name {
        switch( idx ) {
            case Idx(i):    return abcData.names[i-1];
        }
    }
    
    public static function getString( abcData : format.abc.Data.ABCData, idx : Index<String> ) : String {
        switch( idx ) {
            case Idx(i):    return abcData.strings[i-1];
        }
    }
    
    public static function setString( abcData : format.abc.Data.ABCData, idx : Index<String>, value : String ) {
        switch( idx ) {
            case Idx(i):    abcData.strings[i-1] = value;
        }
    }
}
version #19967, modified 2014-03-01 05:58:32 by hgupta