disassembled shaders from 4k fly-through

by yonatan forked from 4k fly-through (diff: 615)
using fujimaru's disassembler from http://slatmp.blog102.fc2.com/#46
♥2 | Line 396 | Modified 2011-12-01 08:18:25 | MIT License
play

ActionScript3 source code

/**
 * Copyright yonatan ( http://wonderfl.net/user/yonatan )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/jA3Y
 */

// forked from keim_at_Si's 4k fly-through

package {
    import com.bit101.components.*;
    import flash.utils.*;

    public class PlainText extends TextArea {
        public function PlainText() {
            width=height=465;

            var vs:String = "78da5d8c3b0ec0200c431dc8d04e59b958ef57d69e8b7b401ca88afa064b963fb700a82820965c1a264df062993ee18fa97cfd20871eac76ca093cfc995b55f7d7fa8d9c7dd97e3cefcc071a530a56";
            var fs:String = "78da6d8ed10d80200c44af05a37fe8160ea4fb49e2177339886d0141e325d0f25aae3d0840a415aac0726d1e5903e6453909a70f77c6bdf2c485d3735edca2fa70f6b137990fe0f24f6b2dfd3bff70ea386364c9cf3ab7c549d354f76c0a4e0a1777e406d8f508e6"

            text = "vertex shader:\n\n" + AGALDisassembler.disassemble(q(vs)) + "\n\nfragment shader:\n\n" + AGALDisassembler.disassemble(q(fs));
        }

        public function q(h:*):*{
            var b:*=new ByteArray;
            var a:*;
            b.endian="littleEndian";
            for(var i:*=0;a=h.substr(i,2);i+=2)b.writeByte(parseInt(a,16));
            b.uncompress();
            return b
        }
    }
}

import flash.utils.*;

// fujimaru's disassembler from http://slatmp.blog102.fc2.com/#46
class AGALDisassembler 
{
	//////////////////////////////////////////////////////////////////////
	// properties
	
	// error
	public static var error:String = "";
	
	// shader type
	private static const SHADERTYPE_FRAGMENT:String = "fragment";
	private static const SHADERTYPE_VERTEX:String = "vertex";
	private static const SHADERTYPE_UNKNOWN:String = "unknown";
	public static var shaderType:String = "";
	
	// opcode
	private static const OPCODE_LISTS:Vector.<String> = Vector.<String>([
			"mov"  // 0x00
			, "add"  // 0x01
			, "sub"  // 0x02
			, "mul"  // 0x03
			, "div"  // 0x04
			, "rcp"  // 0x05
			, "min"  // 0x06
			, "max"  // 0x07
			, "frc"  // 0x08
			, "sqt"  // 0x09
			, "rsq"  // 0x0a
			, "pow"  // 0x0b
			, "log"  // 0x0c
			, "exp"  // 0x0d
			, "nrm"  // 0x0e
			, "sin"  // 0x0f
			, "cos"  // 0x10
			, "crs"  // 0x11
			, "dp3"  // 0x12
			, "dp4"  // 0x13
			, "abs"  // 0x14
			, "neg"  // 0x15
			, "sat"  // 0x16
			, "m33"  // 0x17
			, "m44"  // 0x18
			, "m34"  // 0x19
			, "ifz"  // 0x1a
			, "inz"  // 0x1b
			, "ife"  // 0x1c
			, "ine"  // 0x1d
			, "ifg"  // 0x1e
			, "ifl"  // 0x1f
			, "ieg"  // 0x20
			, "iel"  // 0x21
			, "els"  // 0x22
			, "eif"  // 0x23
			, "rep"  // 0x24
			, "erp"  // 0x25
			, "brk"  // 0x26
			, "kil"  // 0x27
			, "tex"  // 0x28
			, "sge"  // 0x29
			, "slt"  // 0x2a
			, "sgn"  // 0x2b
			, "seq"  // 0x2c
			, "sne"  // 0x2d
		]);
	
	// vertex register type
	private static const REGISTERTYPE_VERTEX_LISTS:Vector.<String> = Vector.<String>([
			"va" // 0x00
			, "vc" // 0x01
			, "vt" // 0x02
			, "op" // 0x03
			, "v"  // 0x04
		]);
	// fragment register type
	private static const REGISTERTYPE_FRAGMENT_LISTS:Vector.<String> = Vector.<String>([
			"[error?]" // 0x00
			, "fc" // 0x01
			, "ft" // 0x02
			, "oc" // 0x03
			, "v"  // 0x04
			, "fs" // 0x05
		]);
	
	// source component pattern
	private static const SOURCE_COMPONENTTYPE:Vector.<String> = Vector.<String>([
			"x"
			, "y"
			, "z"
			, "w"			
		]);

	// sampler dimention
	private static const SAMPLER_DIMENSION:Vector.<String> = Vector.<String>([
			"2d"
			, "cube"
			, "3d"
		]);
	// sampler mipmap
	private static const SAMPLER_MIPMAP:Vector.<String> = Vector.<String>([
            //			  "nomip"		// = "mipnone"
			"mipnone"
			, "mipnearest"
			, "mipliner"
		]);
	// sampler texture filter
	private static const SAMPLER_TEXTUREFILTER:Vector.<String> = Vector.<String>([
			"nearest"
			, "linear"
		]);
	// sampler texture repeat
	private static const SAMPLER_TEXTUREREPEAT:Vector.<String> = Vector.<String>([
			"clamp"
			, "repeat"	
            //			, "wrap"	// = "repeat"
		]);
	

	//////////////////////////////////////////////////////////////////////
	// constructor
	public function AGALDisassembler() {}
	
	//////////////////////////////////////////////////////////////////////
	// disassemble
	public static function disassemble( bytecode:ByteArray ):String {
		error = "";
		var output:String = "";
		var oldPos:uint = bytecode.position;
		bytecode.position = 0;
		
		////////////////////////////////////////////////////////////////////////////////////
		// header
		// A0000001A100		vertex program
		// A0000001A101		fragment program
		// other			unkown program
		if ( bytecode.readUnsignedByte() != 0xA0 ) { error = "magic"; return output; }
		if ( bytecode.readUnsignedInt() != 0x01 ) { error = "version"; return output; }
		if ( bytecode.readUnsignedByte() != 0xa1 ) { error = "shaderTypeID"; return output; }
		var shaderTypeByte:uint = bytecode.readUnsignedByte();
		if ( shaderTypeByte == 0x00 ) {
			shaderType = SHADERTYPE_VERTEX;
		} else if ( shaderTypeByte == 0x01 ) {
			shaderType = SHADERTYPE_FRAGMENT;				
		} else {
			shaderType = SHADERTYPE_UNKNOWN;				
			error = "shaderTypeUnkown";
			return output;
		}
		
		////////////////////////////////////////////////////////////////////////////////////
		// token
		// [opcode][destination][source1][source2 or sampler]	always 192 bit ( 24 byte )
		
		while ( bytecode.bytesAvailable >= 24 ) {
			
			////////////////////////////////////////////////////////////////////////////////////
			// [opcode]	32 bit
			var opcode:uint = bytecode.readUnsignedInt();
			if ( opcode >= OPCODE_LISTS.length ) {
				error = "[opcode : " + opcode + " ] unkown opcode." ;
				return output;				
			}
			output += OPCODE_LISTS[ opcode ] + " ";
			
			////////////////////////////////////////////////////////////////////////////////////
			// create registers
			switch( opcode ) {
				//////////////////////////////
				// dst src1 none
				case 0x00: // mov
				case 0x05: // rcp
				case 0x08: // frc
				case 0x09: // sqt
				case 0x0a: // rsq
				case 0x0c: // log
				case 0x0d: // exp
				case 0x0e: // nrm
				case 0x0f: // sin
				case 0x10: // cos
				case 0x14: // abs
				case 0x15: // neg
				case 0x16: // sat
				case 0x2b: // sgn
				output += readDestination( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				readSourceNone( bytecode );
				break;
				
				//////////////////////////////
				// dst src1 src2
				case 0x01: // add
				case 0x02: // sub
				case 0x03: // mul
				case 0x04: // div
				case 0x06: // min
				case 0x07: // max
				case 0x0b: // pow
				case 0x11: // crs
				case 0x12: // dp3
				case 0x13: // dp4
				case 0x29: // sge
				case 0x2a: // slt
				case 0x2c: // seq
				case 0x2d: // sne
				output += readDestination( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				break;
				
				//////////////////////////////
				// dst src1 src2
				case 0x17: // m33
				case 0x18: // m44
				case 0x19: // m34
				output += readDestination( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				break;
				
				//////////////////////////////
				// none src1 none
				case 0x1a: // ifz
				case 0x1b: // inz
				case 0x24: // rep
				case 0x27: // kil
				readDestinationNone( bytecode );
				output += readSource( bytecode, shaderType );
				readSourceNone( bytecode );
				break;
				
				//////////////////////////////
				// none src1 src2
				case 0x1c: // ife
				case 0x1d: // ine
				case 0x1e: // ifg
				case 0x1f: // ifl
				case 0x20: // ieg
				case 0x21: // iel
				readDestinationNone( bytecode );
				output += readSource( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				break;
				
				//////////////////////////////
				// none none none
				case 0x22: // els
				case 0x23: // eif
				case 0x25: // erp
				case 0x26: // brk
				readDestinationNone( bytecode );
				readSourceNone( bytecode );
				readSourceNone( bytecode );
				break;
				
				//////////////////////////////
				// dst src1 sampler
				case 0x28: // tex
				output += readDestination( bytecode, shaderType );
				output += ", " + readSource( bytecode, shaderType );
				output += ", " + readSampler( bytecode, shaderType );
				break;
				
				default:
				
				break;
			}
			
			output += "\n";
		}
		
		output += "\n";
		
		bytecode.position = oldPos;
		return output;
	}
	
	////////////////////////////////////////////////////////////////////////////////////
	// [destination]
	// ----TTTT ----MMMM NNNNNNNN NNNNNNNN
	private static function readDestination( bytecode:ByteArray, shaderType:String):String {
		
		var registerNumber:uint  = bytecode.readShort();			// NNNNNNNNNNNNNNNN
		var mask:uint 		     = bytecode.readUnsignedByte();		// MMMM
		var registerType:uint    = bytecode.readUnsignedByte();		// TTTT

		var ret:String = "";
		// registerType
		ret += getRegisterName( shaderType, registerType );
		// registerNumber
		if ( registerType != 0x03 ) { // 0x03 is output register.
			ret += registerNumber;
		}
		// mask
		if( mask != 0x0f ){
			ret += ".";
			if ( mask & 0x01 ) ret += "x";
			if ( mask & 0x02 ) ret += "y";
			if ( mask & 0x04 ) ret += "z";
			if ( mask & 0x08 ) ret += "w";
		}
		
		return ret;
	}
	
	private static function readDestinationNone( bytecode:ByteArray):void {
		bytecode.position += 4;			
	}
	
	////////////////////////////////////////////////////////////////////////////////////
	// [source1] 64 bit
	// D------- ------QQ ----IIII ----TTTT SSSSSSSS OOOOOOOO NNNNNNNN NNNNNNNN
	private static function readSource( bytecode:ByteArray, shaderType:String ):String {

		var registerNumber:uint       = bytecode.readShort(); 			// NNNNNNNNNNNNNNNN
		var indirectOffset:uint       = bytecode.readUnsignedByte();	// OOOOOOOO
		var swizzle:uint     		  = bytecode.readUnsignedByte();	// SSSSSSSS
		var registerType:uint         = bytecode.readUnsignedByte();	// TTTT
		var idxRegisterType:uint      = bytecode.readUnsignedByte();	// IIII
		var idxRegisterComponent:uint = bytecode.readUnsignedByte();	// QQ
		var isDirect:uint     		  = bytecode.readUnsignedByte();	// D
		
		var ret:String = "";
		// registerType
		ret += getRegisterName( shaderType, registerType );
		// direct or indirect.
		if ( isDirect == 0x00 ) {
			// direct
			// registerNumber
			if ( registerType != 0x03 ) { // 0x03 is output register.
				ret += registerNumber;
			}
		} else /* if( isDirect == 0x80 ) */{
			// indirect
			ret += "[";
			ret += getRegisterName( shaderType, idxRegisterType );
			ret += registerNumber;
			ret += "." + SOURCE_COMPONENTTYPE[ idxRegisterComponent ];
			ret += "+" + indirectOffset;
			ret += "]";
		}
		
		// swizzle
		if ( swizzle != 0xe4 ) {	// 0xe4 is ".xyzw"
			ret += ".";
			var bit01:uint, bit23:uint, bit45:uint, bit67:uint;
			var tmp0:String, tmp1:String, tmp2:String, tmp3:String;
			tmp0 = tmp1 = tmp2 = tmp3 = "";
			bit01 = ( swizzle & 0x03 );
			bit23 = ( swizzle & 0x0c ) >> 2;
			bit45 = ( swizzle & 0x30 ) >> 4;
			bit67 = ( swizzle & 0xc0 ) >> 6;
			tmp3 = SOURCE_COMPONENTTYPE[ bit67 ];
			if( bit67 != bit45 ){
				tmp2 = SOURCE_COMPONENTTYPE[ bit45 ];
			}
			if (   ( bit67 != bit45 )
				|| ( ( bit67 == bit45 ) && ( bit45 != bit23 ) ) 
			){
				tmp1 = SOURCE_COMPONENTTYPE[ bit23 ];
			}
			if (   ( bit67 != bit45 )
				|| ( ( bit67 == bit45 ) && ( bit45 != bit23 ) ) 
				|| ( ( bit67 == bit45 ) && ( bit45 == bit23 ) && ( bit23 != bit01 ) ) 
			){
				tmp0 = SOURCE_COMPONENTTYPE[ bit01 ];
			}
			
			ret += tmp0 + tmp1 + tmp2 + tmp3;
		}
		
		return ret;
	}
	
	private static function readSourceNone( bytecode:ByteArray ):void {
		bytecode.position += 8;
	}
	
	////////////////////////////////////////////////////////////////////////////////////
	// [sampler] 64 bit
	// FFFFMMMM WWWWSSSS DDDD---- ----TTTT -------- BBBBBBBB NNNNNNNN NNNNNNNN
	private static function readSampler( bytecode:ByteArray, shaderType:String ):String {
		var tmp:uint;
		var registerNumber:uint   = bytecode.readShort();						 // NNNNNNNNNNNNNNNN
		var bias:int     	  	  = bytecode.readUnsignedByte();				 	 // BBBBBBBB
		tmp = bytecode.readUnsignedByte();
		var registerType:uint     = bytecode.readUnsignedByte();			 	 // TTTT
		var dimension:uint     	  = ( bytecode.readUnsignedByte() & 0xF0 ) >> 4; // DDDD
		tmp = bytecode.readUnsignedByte();
		var wrapping:uint     	  = ( tmp & 0xF0 ) >> 4;						 // WWWW
		var specialFlagBits:uint  = ( tmp & 0x0F )     ;						 // SSSS
		tmp = bytecode.readUnsignedByte();
		var filter:uint     	  = ( tmp & 0xF0 ) >> 4;						 // FFFF
		var mipmap:uint     	  = ( tmp & 0x0F )     ;						 // MMMM
		
		var ret:String = "";
		// registerType
		ret += getRegisterName( shaderType, registerType );
		ret += registerNumber;
		ret += " <" + SAMPLER_DIMENSION[ dimension ];
		ret += "," + SAMPLER_TEXTUREFILTER[ filter ];
		ret += "," + SAMPLER_TEXTUREREPEAT[ wrapping ];
		ret += "," + SAMPLER_MIPMAP[ mipmap ];
		if( bias != 0 ){
			ret += "," + ( Number(bias) / 8.0 );
		}
		ret += ">";
		
		return ret;
	}
	

	////////////////////////////////////////////////////////////////////////////////////
	// getRegisterName
	private static function getRegisterName( shaderType:String, registerType:uint ):String {
		if ( shaderType == SHADERTYPE_VERTEX ) {
			if ( registerType >= REGISTERTYPE_VERTEX_LISTS.length ) {
				error = "[registerType : " + registerType + " ] unkown registerType." ;
				return "ERROR";
			}
			return REGISTERTYPE_VERTEX_LISTS[ registerType ];
		} else /*if ( shaderType == SHADERTYPE_FRAGMENT ) */{
			if ( registerType >= REGISTERTYPE_FRAGMENT_LISTS.length ) {
				error = "[registerType : " + registerType + " ] unkown registerType." ;
				return "ERROR";
			}
			return REGISTERTYPE_FRAGMENT_LISTS[ registerType ];
		}
	}
	
	////////////////////////////////////////////////////////////////////////////////////
	// debug
	public static function debug_bytecodeFormattingString( source:String, bytecode:ByteArray ):String {
		var i:int;
		var output:String = "";
		var oldPos:uint = bytecode.position;
		bytecode.position = 0;
		
		var lines:Array;
		var lineIdx:uint = 0;
		if( source ){
			lines = source.split( "\n" );
		}
		
		// [header]
		output += "[";
		for( i = 0; i < 7; i++ ){
			output += ( "0" + bytecode.readUnsignedByte().toString(16) ).substr( -2 ) + " ";		
		}
		output += "]\n";
		
		// [token]
		try {
			while ( bytecode.bytesAvailable > 12 ) {
				// soure
				if( source ){
					output += lines[ lineIdx++ ] + "\n";
				}
				
				// bytecode
				output += "[";
				// [opcode]
				output += "[";
				for( i = 0; i < 4; i++ ){
					output += ( "0" + bytecode.readUnsignedByte().toString(16) ).substr( -2 ) + " ";		
				}
				output += "]";
				// [destination]
				output += "[";
				for( i = 0; i < 4; i++ ){
					output += ( "0" + bytecode.readUnsignedByte().toString(16) ).substr( -2 ) + " ";		
				}
				output += "]";			
				// [source1]
				output += "[";
				for( i = 0; i < 3; i++ ){
					output += ( "0" + bytecode.readUnsignedByte().toString(16) ).substr( -2 ) + " ";		
				}
				output += ( "0000000" + bytecode.readUnsignedByte().toString(2) ).substr( -8 ) + " ";	
				for( i = 0; i < 4; i++ ){
					output += ( "0" + bytecode.readUnsignedByte().toString(16) ).substr( -2 ) + " ";		
				}
				output += "]";							
				// [source2 or sampler]
				output += "[";
				for( i = 0; i < 8; i++ ){
					output += ( "0" + bytecode.readUnsignedByte().toString(16) ).substr( -2 ) + " ";		
				}
				output += "]";							
				output += "]\n";
			}
		} catch ( error:Error ) {
			trace( error.message );
		}
		
		bytecode.position = oldPos;
		return output;
		
	}
}