package org.cl0zzzy.rtmp; import java.util.ArrayList; import java.util.Arrays; public class AMF { private ArrayList AMFMessage = new ArrayList(); private byte[] amf; public boolean next = false; public byte[] nextArr; public AMF( byte[] amf ) { this.amf = amf; this.decodeAMF(); } private AMFResult decodeNumber( int i ) { byte[] number = new byte[8]; number[0] = amf[i+1]; number[1] = amf[i+2]; number[2] = amf[i+3]; number[3] = amf[i+4]; number[4] = amf[i+5]; number[5] = amf[i+6]; number[6] = amf[i+7]; number[7] = amf[i+8]; AMFResult result = new AMFResult(); result.value = this.numberToDouble( Util.bytesToHex( number ) ); result.index = i+8; return result; } private AMFResult decodeString( int i ) { byte[] stringLength = new byte[2]; stringLength[0] = amf[i+1]; stringLength[1] = amf[i+2]; int length = Integer.parseInt( Util.bytesToHex(stringLength), 16 ); ArrayList str = new ArrayList(); for( int u = (i+3); u < (i+3+length); u++ ) { str.add( amf[u] ); } AMFResult result = new AMFResult(); byte[] _a = new byte[str.size()]; int j = 0; for(Byte b : str) { _a[j++] = b.byteValue(); } try { result.value = new String( _a, "UTF-8" ); } catch( Exception e ) { e.printStackTrace(); } result.index = i+2+length; return result; } private AMFResult decodeECMA( int i ) { AMFEcmaArray ecma = new AMFEcmaArray(); if( amf[i] == AMFObject.AMF_ECMA_ARRAY_START && amf[i+1] == 0x00 && amf[i+2] == 0x00 && amf[i+3] == 0x00 && amf[i+4] == 0x00 && amf[i+5] == 0x00 && amf[i+6] == 0x00 && amf[i+7] == AMFObject.AMF_OBJECT_END ) { i = i + 7; AMFResult result = new AMFResult(); result.value = ecma; result.index = i; return result; } i+=5; int index = 0; while( true ) { if( amf[i] == 0x00 && amf[i+1] == 0x00 && amf[i+2] == 0x09 ) { i = i + 2; break; } i+=3; int __index = index; __index = __index - ( __index % 10 ); if( __index > 0 ) { int len = String.valueOf(__index).substring(1).length(); i += len; } Result res = this.decodeOne(i); AMFObject value = new AMFObject( amf[i], res.val ); ecma.add(value); i = res.i; i++; index++; } AMFResult result = new AMFResult(); result.value = ecma; result.index = i; return result; } private AMFResult decodeObj( int i ) { i++; AMFObjectArray array = new AMFObjectArray(); while( true ) { if( amf[i] == 0x00 && amf[i+1] == 0x00 && amf[i+2] == 0x09 ) { i = i + 2; break; } byte[] length = new byte[2]; length[0] = amf[i]; length[1] = amf[i+1]; int decimal = Integer.parseInt( Util.bytesToHex(length), 16 ); String key = ""; for( int u = i+2; u < (i+2+decimal); u++ ) { key = key + new String( new byte[] {amf[u]} ); } Result res = this.decodeOne(i+2+decimal); AMFObject obj = new AMFObject( amf[i+2+decimal], res.val ); array.add(key, obj); i = res.i + 1; } AMFResult result = new AMFResult(); result.value = array; result.index = i; return result; } private Result decodeOne( int i ) { Object val = null; if( amf[i] == AMFObject.AMF_NUMBER ) { AMFResult result = this.decodeNumber(i); val = result.value; i = result.index; } else if( amf[i] == AMFObject.AMF_STRING ) { AMFResult result = this.decodeString(i); val = result.value; i = result.index; } else if( amf[i] == AMFObject.AMF_BOOLEAN ) { if( amf[i+1] == 0x00 ) { val = false; } else { val = true; } i++; } else if( amf[i] == AMFObject.AMF_OBJECT_START ) { AMFResult result = this.decodeObj(i); val = result.value; i = result.index; } else if( amf[i] == AMFObject.AMF_NULL ) { val = null; } else if( amf[i] == AMFObject.AMF_ECMA_ARRAY_START ) { AMFResult result = this.decodeECMA(i); val = result.value; i = result.index; } else if( amf[i] == AMFObject.AMF_DATE ) { val = "Date"; i = i + 10; } else if( amf[i] == (byte) 0x43 || amf[i] == (byte) 0x83 ){ val = "end"; next = true; nextArr = Arrays.copyOfRange(amf, i, amf.length); } Result res = new Result(); res.i = i; res.val = val; return res; } private void decodeAMF() { for( int i = 0; i < amf.length; i++ ) { Object val = null; int _i = i; Result res = this.decodeOne( i ); i = res.i; val = res.val; if( val != null && val.equals("end") ) { break; } if( amf[_i] != AMFObject.AMF_BOOLEAN && amf[_i] != AMFObject.AMF_ECMA_ARRAY_START && amf[_i] != AMFObject.AMF_NUMBER && amf[_i] != AMFObject.AMF_OBJECT_START && amf[_i] != AMFObject.AMF_STRING ) { amf[_i] = AMFObject.AMF_NULL; } AMFObject value = new AMFObject( amf[_i], val ); AMFMessage.add(value); } } public int getLength() { return this.AMFMessage.size(); } public AMFObject get( int index ) { return this.AMFMessage.get(index); } private double numberToDouble( String hexString ) { long longHex = parseUnsignedHex(hexString); return Double.longBitsToDouble(longHex); } private long parseUnsignedHex(String text) { if (text.length() == 16) { return (parseUnsignedHex(text.substring(0, 1)) << 60) | parseUnsignedHex(text.substring(1)); } return Long.parseLong(text, 16); } }