Skip to content

Commit

Permalink
Support for typed array tags + update to 0.2.0
Browse files Browse the repository at this point in the history
Signed-off-by: hypothermic <admin@hypothermic.nl>
  • Loading branch information
hypothermic committed Jul 8, 2021
1 parent b7280c1 commit 60c50cb
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 23 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ An Erlang/OTP library which can manipulate NBT (Named Binary Tag) data.

This library currently is up to date with the [1.0 spec](https://web.archive.org/web/20110723210920/http://www.minecraft.net/docs/NBT.txt).

Please note that the API may change before the first major version is released.
Please note that the API may change before the first major version is released.
If you want to avoid API breakage, please wait until version 1.0.0 is released.

Quick navigation:
[Build](#building-the-library)
Expand Down Expand Up @@ -34,9 +35,9 @@ Depending on your build tool, add the following line to the "dependencies" secti

| Build Tool | Dependency line |
| --------------------- | ------- |
| rebar3 (Erlang/OTP) | `{erl_nbt, "0.1.0"}` |
| mix (Elixir) | `{:erl_nbt "~> 0.1.0"}` |
| erlang.mk (Erlang) | `dep_erl_nbt = hex 0.1.0` |
| rebar3 (Erlang/OTP) | `{erl_nbt, "0.2.0"}` |
| mix (Elixir) | `{:erl_nbt "~> 0.2.0"}` |
| erlang.mk (Erlang) | `dep_erl_nbt = hex 0.2.0` |

Documentation
-----
Expand Down
2 changes: 1 addition & 1 deletion src/erl_nbt.app.src
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{application, erl_nbt, [
{description, "An Erlang/OTP library for manipulating NBT (Named Binary Tag) data"},
{vsn, "0.1.0"},
{vsn, "0.2.0"},
{licenses, ["Apache 2.0"]},
{links, [
{"GitHub", "https://github.com/hypothermic/erl_nbt"}
Expand Down
36 changes: 35 additions & 1 deletion src/erl_nbt_decode.erl
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,24 @@ decode_tag(<<Id:8/unsigned-integer, Rest/binary>>, Count, Depth, Output) when Id

decode_tag(Rest3, Count + 1, Depth, Output#{Name => {?TAG_DOUBLE_TYPE, Double}});

decode_tag(<<Id:8/unsigned-integer, Rest/binary>>, Count, Depth, Output) when Id =:= ?TAG_BYTE_ARRAY_ID ->
{ok, Name, Rest2} = decode_string(Rest),
{ok, ByteArray, Rest3} = decode_byte_array(Rest2),

decode_tag(Rest3, Count + 1, Depth, Output#{Name => {?TAG_BYTE_ARRAY_TYPE, ByteArray}});

decode_tag(<<Id:8/unsigned-integer, Rest/binary>>, Count, Depth, Output) when Id =:= ?TAG_INT_ARRAY_ID ->
{ok, Name, Rest2} = decode_string(Rest),
{ok, IntArray, Rest3} = decode_int_array(Rest2),

decode_tag(Rest3, Count + 1, Depth, Output#{Name => {?TAG_INT_ARRAY_TYPE, IntArray}});

decode_tag(<<Id:8/unsigned-integer, Rest/binary>>, Count, Depth, Output) when Id =:= ?TAG_LONG_ARRAY_ID ->
{ok, Name, Rest2} = decode_string(Rest),
{ok, LongArray, Rest3} = decode_long_array(Rest2),

decode_tag(Rest3, Count + 1, Depth, Output#{Name => {?TAG_LONG_ARRAY_TYPE, LongArray}});

decode_tag(<<Id:8/unsigned-integer, Rest/binary>>, Count, Depth, Output) when Id =:= ?TAG_STRING_ID ->
{ok, Name, Rest2} = decode_string(Rest),
{ok, String, Rest3} = decode_string(Rest2),
Expand Down Expand Up @@ -150,5 +168,21 @@ decode_float(<<Float:32/big-signed-float, Rest/binary>>) ->
decode_double(<<Double:64/big-signed-float, Rest/binary>>) ->
{ok, Double, Rest}.

decode_byte_array(<<Length:32/big-signed-integer, Rest/binary>>) ->
read_next_raw(Rest, 8, Length, []).

decode_int_array(<<Length:32/big-signed-integer, Rest/binary>>) ->
read_next_raw(Rest, 32, Length, []).

decode_long_array(<<Length:32/big-signed-integer, Rest/binary>>) ->
read_next_raw(Rest, 64, Length, []).

decode_string(<<Length:16/unsigned-integer, String:Length/binary, Rest/binary>>) ->
{ok, binary_to_list(String), Rest}.
{ok, binary_to_list(String), Rest}.

read_next_raw(Data, UnitLength, Remaining, Out) when Remaining > 0 ->
<<Value:UnitLength/signed-integer, Rest/binary>> = Data,
read_next_raw(Rest, UnitLength, Remaining-1, [Value|Out]);

read_next_raw(Data, _UnitLength, _Remaining, Out) ->
{ok, lists:reverse(Out), Data}.
24 changes: 24 additions & 0 deletions src/erl_nbt_encode.erl
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,30 @@ encode_tag(Key, {?TAG_DOUBLE_TYPE, Value}) ->
Value:64/big-signed-float
>>;

encode_tag(Key, {?TAG_BYTE_ARRAY_TYPE, Value}) ->
<<
?TAG_BYTE_ARRAY_ID:8/unsigned-integer,
(encode_string(Key))/binary,
(length(Value)):32/big-signed-integer,
(lists:foldl(fun (E, Acc) -> <<Acc/binary, E:8/signed-integer>> end, <<>>, Value))/binary
>>;

encode_tag(Key, {?TAG_INT_ARRAY_TYPE, Value}) ->
<<
?TAG_INT_ARRAY_ID:8/unsigned-integer,
(encode_string(Key))/binary,
(length(Value)):32/big-signed-integer,
(lists:foldl(fun (E, Acc) -> <<Acc/binary, E:32/signed-integer>> end, <<>>, Value))/binary
>>;

encode_tag(Key, {?TAG_LONG_ARRAY_TYPE, Value}) ->
<<
?TAG_LONG_ARRAY_ID:8/unsigned-integer,
(encode_string(Key))/binary,
(length(Value)):32/big-signed-integer,
(lists:foldl(fun (E, Acc) -> <<Acc/binary, E:64/signed-integer>> end, <<>>, Value))/binary
>>;

encode_tag(Key, {?TAG_STRING_TYPE, Value}) ->
<<
?TAG_STRING_ID:8/unsigned-integer,
Expand Down
11 changes: 11 additions & 0 deletions test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# erl_nbt tests

A quick overview what every test is for

| Name | Description |
| --------------- | -------------------------------------------------------- |
| Basic | Tests the parser using Notch's "basic" example file |
| Integers | Tests support for Byte, Short, Int, Long tags |
| Floating Points | Tests support for Float and Double tags |
| Integer Arrays | Tests support for ByteArray, IntArray and LongArray tags |
| Compounds | Tests support for (nested) Compound tags |
43 changes: 28 additions & 15 deletions test/basic_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,39 @@
-author("Matthijs Bakker <matthijs at hypothermic .nl>").
-copyright("Copyright (C) 2021 hypothermic.nl").

-include_lib("erl_nbt.hrl").
-include_lib("eunit/include/eunit.hrl").

-define(TEST_FILE, "test/basic.nbt").
-define(EXPECTED_NBT, #{"hello world" => #{"name" => "Bananrama"}}).
-define(EXPECTED_NBT,
#{
"hello world" => #{
"name" => {?TAG_STRING_TYPE, "Bananrama"}
}
}).

basic_test_() -> [
{"decode file basic.nbt",
fun () ->
{ok, InputData} = file:read_file(?TEST_FILE),
{ok, DecodedNbt} = erl_nbt:decode(InputData),
{"decode file basic.nbt",
fun () ->
{ok, InputData} = file:read_file(?TEST_FILE),
{ok, DecodedNbt} = erl_nbt:decode(InputData),

?assertEqual(?EXPECTED_NBT, DecodedNbt)
end
},
{"encode map and compare with file basic.nbt",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, CorrectData} = file:read_file(?TEST_FILE),
?assertEqual(?EXPECTED_NBT, DecodedNbt)
end
},
{"encode map and compare with file basic.nbt",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, CorrectData} = file:read_file(?TEST_FILE),

?assertEqual(CorrectData, EncodedNbt)
end
}
?assertEqual(CorrectData, EncodedNbt)
end
},
{"encode then decode",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, DecodedNbt} = erl_nbt:decode(EncodedNbt),
?assertEqual(DecodedNbt, ?EXPECTED_NBT)
end
}
].
7 changes: 7 additions & 0 deletions test/compounds_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,12 @@ compounds_test_() -> [
% So we only test if the length of these binaries are equal.
?assertEqual(byte_size(CorrectData), byte_size(EncodedNbt))
end
},
{"encode then decode",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, DecodedNbt} = erl_nbt:decode(EncodedNbt),
?assertEqual(DecodedNbt, ?EXPECTED_NBT)
end
}
].
10 changes: 9 additions & 1 deletion test/floating_points_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"z" => {?TAG_DOUBLE_TYPE, -799.2},
"stance" => {?TAG_FLOAT_TYPE, 87.0}
}
}).
}
).

floating_points_test_() -> [
{"decode file floating_points.nbt",
Expand All @@ -44,5 +45,12 @@ floating_points_test_() -> [
% So we only test if the length of these binaries are equal.
?assertEqual(byte_size(CorrectData), byte_size(EncodedNbt))
end
},
{"encode then decode",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, DecodedNbt} = erl_nbt:decode(EncodedNbt),
?assertEqual(DecodedNbt, ?EXPECTED_NBT)
end
}
].
Binary file added test/integer_arrays.nbt
Binary file not shown.
54 changes: 54 additions & 0 deletions test/integer_arrays_test.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
%%%-------------------------------------------------------------------
%%% @author Matthijs Bakker <matthijs at hypothermic .nl>
%%% @copyright (C) 2021 hypothermic.nl
%%% @doc
%%% Tests the erl_nbt functionality on the "integers.nbt" example
%%% @end
%%% Created : 8. Jul 2021 10:25 PM
%%%-------------------------------------------------------------------
-module(integer_arrays_test).
-author("Matthijs Bakker <matthijs at hypothermic .nl>").
-copyright("Copyright (C) 2021 hypothermic.nl").

-include_lib("erl_nbt.hrl").
-include_lib("eunit/include/eunit.hrl").

-define(TEST_FILE, "test/integer_arrays.nbt").
-define(EXPECTED_NBT,
#{
"arrays" => #{
"bunch of bytes" => {?TAG_BYTE_ARRAY_TYPE, [-43, 67, 49, 108]},
"flock of ints" => {?TAG_INT_ARRAY_TYPE, [420, 2147483646, -82131929]},
"lots of longs" => {?TAG_LONG_ARRAY_TYPE, [-921312010, 69, 129391203]}
}
}
).

integer_arrays_test_() -> [
{"decode file integer_arrays.nbt",
fun () ->
{ok, InputData} = file:read_file(?TEST_FILE),
{ok, DecodedNbt} = erl_nbt:decode(InputData),

?assertEqual(?EXPECTED_NBT, DecodedNbt)
end
},
{"encode map and compare with file integer_arrays.nbt",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, CorrectData} = file:read_file(?TEST_FILE),

% Because child order of compound tags is not guaranteed
% to be preserved with NBT tags, we can't test for equality.
% So we only test if the length of these binaries are equal.
?assertEqual(byte_size(CorrectData), byte_size(EncodedNbt))
end
},
{"encode then decode",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, DecodedNbt} = erl_nbt:decode(EncodedNbt),
?assertEqual(DecodedNbt, ?EXPECTED_NBT)
end
}
].
10 changes: 9 additions & 1 deletion test/integers_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"my int" => {?TAG_INT_TYPE, 20012318},
"my long" => {?TAG_LONG_TYPE, 91499321421}
}
}).
}
).

integers_test_() -> [
{"decode file integers.nbt",
Expand All @@ -43,5 +44,12 @@ integers_test_() -> [
% So we only test if the length of these binaries are equal.
?assertEqual(byte_size(CorrectData), byte_size(EncodedNbt))
end
},
{"encode then decode",
fun () ->
{ok, EncodedNbt} = erl_nbt:encode(?EXPECTED_NBT),
{ok, DecodedNbt} = erl_nbt:decode(EncodedNbt),
?assertEqual(DecodedNbt, ?EXPECTED_NBT)
end
}
].

0 comments on commit 60c50cb

Please sign in to comment.