-module(ems_amf3). -export([decode/1, decode/3]). -export([parse_integer/1, parse_string/2, parse_array/3, parse_object/3]). -export([get/2]). -include("../include/ems.hrl"). decode(Data) -> <<Code, Rest/binary>> = iolist_to_binary(Data), {Value, Rest_, State} = decode(Code, Rest, #amf3{}), {{amf3, Value, State}, Rest_}. decode(?AMF3_UNDEFINED, Data, S) -> {undefined, Data, S}; decode(?AMF3_NULL, Data, S) -> {null, Data, S}; decode(?AMF3_FALSE, Data, S) -> {false, Data, S}; decode(?AMF3_TRUE, Data, S) -> {true, Data, S}; decode(?AMF3_INTEGER, Data, S) -> {Num, Rest} = parse_integer(Data), {Num, Rest, S}; decode(?AMF3_NUMBER, Data, S) -> <<Float:64/float, Rest/binary>> = Data, {Float, Rest, S}; decode(?AMF3_STRING, Data, S) -> parse_string(Data, S); decode(?AMF3_XML, Data, S) -> {String, Rest, S1} = parse_string(Data, S), {{xml, String}, Rest, S1}; decode(?AMF3_DATE, Data, S) -> {Type, Rest1} = parse_integer(Data), case Type band 1 of 1 -> <<Float:64/float, Rest2/binary>> = Rest1, {Ref, S1} = add_object({date, ems_util:float_to_datetime(Float/1000)}, S), {Ref, Rest2, S1}; 0 -> {{ref, Type bsr 1}, Rest1, S} end; decode(?AMF3_ARRAY, Data, S) -> {Type, Rest1} = parse_integer(Data), Length = Type bsr 1, case Type band 1 of 1 -> parse_array(Length, Rest1, S); 0 -> {{ref, Length}, Rest1, S} end; decode(?AMF3_OBJECT, Data, S) -> {Type, Rest1} = parse_integer(Data), TypeInfo = Type bsr 1, case Type band 1 of 0 -> {{ref, TypeInfo}, Rest1, S}; %% o-ref 1 -> parse_object(Type, Rest1, S) end; decode(?AMF3_XML_STRING, Data, S) -> {Type, Rest1} = parse_integer(Data), 0 = Type band 1, Length = Type bsr 1, <<String:Length/binary, Rest2/binary>> = Rest1, {{xml, String}, Rest2, S}. parse_integer(Data) -> parse_integer(Data, 0, 0). parse_integer(<<1:1, Num:7, Data/binary>>, Result, N) when N < 3 -> parse_integer(Data, (Result bsl 7) bor Num, N + 1); parse_integer(<<0:1, Num:7, Data/binary>>, Result, N) when N < 3 -> {(Result bsl 7) bor Num, Data}; parse_integer(<<Byte, Data/binary>>, Result, _N) -> Result1 = (Result bsl 8) bor Byte, Result3 = case Result1 band 16#10000000 of 16#10000000 -> Extended = Result1 bor 16#e0000000, <<Result2:32/signed>> = <<Extended:32>>, Result2; 0 -> Result1 end, {Result3, Data}. parse_string(Data, S) -> {Type, Rest} = parse_integer(Data), Length = Type bsr 1, case Type band 1 of 0 -> {get_string(Length, S), Rest, S}; 1 -> <<StringB:Length/binary, Rest1/binary>> = Rest, {String,S1} = add_string(binary_to_list(StringB), S), {String, Rest1, S1} end. parse_array(Length, <<1, Data/binary>>, S) -> {RefNum, S1} = new_object(S), {Array, Rest, S2} = parse_array(Length, Data, S1, [], Length), {Ref, S3} = finish_object(RefNum, Array, S2), {Ref, Rest, S3}. parse_array(0, Data, S, Acc, OrigLength) -> {{array, OrigLength, lists:reverse(Acc)}, Data, S}; parse_array(Length, Data, S, Acc, OrigLength) -> <<Code, Data1/binary>> = Data, {Item, Rest, S1} = decode(Code, Data1, S), parse_array(Length - 1, Rest, S1, [Item | Acc], OrigLength). parse_object(Type, Data, S) -> {RefNum, S1} = new_object(S), {Object, Rest1, S2} = parse_object_info(Type, Data, S1), {Ref, S3} = finish_object(RefNum, Object, S2), {Ref, Rest1, S3}. %%traits-ref parse_object_info(Type, Data, S) when (Type band 3) == 1 -> {Type, Data, S}; %%traits-ext parse_object_info(Type, Data, S) when (Type band 7) == 7 -> {Type, Data, S}; parse_object_info(Type, Data, S) -> Externalizable = ((Type band 4) == 4), Dynamic = ((Type band 8) == 8), {ClassName, Rest, S1} = parse_string(Data, S), Count = Type bsr 4, {Rest1,S2,NameList} = readPropertyName(Count,Rest,S1,Count,[]), {Rest2,S3,ValueList} = readPropertyValue(Count,Rest1,S2,Count,[]), KeyValues = keyValue(NameList,ValueList,[]), {Rest3,S4,KeyValues1} = readDynamicProperty(Dynamic,Rest2,S3,KeyValues,true), Object = #as_object{type=ClassName,keyValue=KeyValues1,externalizable=Externalizable,dynamic=Dynamic}, {Object, Rest3, S4}. readPropertyName(0,Data,S,Length,List) -> {Data,S,lists:reverse(List)}; readPropertyName(Index,Data,S,Length,List) -> {String, Rest1, S1} = parse_string(Data,S), readPropertyName(Index-1,Rest1,S1,Length,[String|List]). readPropertyValue(0,Data,S,Length,List) -> {Data,S,lists:reverse(List)}; readPropertyValue(Index,Data,S,Length,List) -> <<Code, Rest/binary>> = iolist_to_binary(Data), {Ref, Rest1, S1} = decode(Code, Rest, S), readPropertyValue(Index-1,Rest1,S1,Length,[get(Ref,S1)|List]). readDynamicProperty(_,Data,S,KeyValues,false) -> {<<>>,S,KeyValues}; readDynamicProperty(false,Data,S,KeyValues,_) -> {Data,S,KeyValues}; readDynamicProperty(true,Data,S,KeyValues,_) -> {String, Rest1, S1} = parse_string(Data,S), <<Code, Rest2/binary>> = iolist_to_binary(Rest1), {Ref, Rest3, S2} = decode(Code, Rest2, S1), {String1, _, _} = parse_string(Rest3,S2), Loop = (String1 =/= ""), readDynamicProperty(true,Rest3,S2,[{String,get(Ref,S2)}|KeyValues],Loop). keyValue([],[],KeyValues) -> KeyValues; keyValue([Name|Names],[Value|Values],KeyValues) -> keyValue(Names,Values,[{list_to_atom(Name),Value}|KeyValues]). add_object(Object, S) -> OldTree = case S#amf3.objects of nil -> gb_trees:from_orddict([]); _Tree -> _Tree end, RefNum = S#amf3.objectcount, Tree = gb_trees:insert(RefNum, Object, OldTree), {{ref, RefNum}, S#amf3{objects=Tree, objectcount=1 + RefNum}}. new_object(S) -> RefNum = S#amf3.objectcount, {RefNum, S#amf3{objectcount=1 + RefNum}}. finish_object(RefNum, Object, S) -> OldTree = case S#amf3.objects of nil -> gb_trees:from_orddict([]); _Tree -> _Tree end, Tree = gb_trees:insert(RefNum, Object, OldTree), {{ref, RefNum}, S#amf3{objects=Tree}}. add_string("", S) -> {"",S}; add_string(String, S) -> OldTree = case S#amf3.strings of nil -> gb_trees:from_orddict([]); _Tree -> _Tree end, RefNum = S#amf3.stringcount, Tree = gb_trees:insert(RefNum, String, OldTree), {String, S#amf3{strings=Tree, stringcount=1 + RefNum}}. get_object(RefNum, S) -> gb_trees:get(RefNum, S#amf3.objects). get_string(RefNum, S) -> gb_trees:get(RefNum, S#amf3.strings). get({ref, RefNum}, S) -> get_object(RefNum, S); get(Object, _) -> Object.