bindata 1.2.2 → 1.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of bindata might be problematic. Click here for more details.
- data/ChangeLog +12 -0
- data/NEWS +53 -0
- data/Rakefile +2 -1
- data/examples/NBT.txt +149 -0
- data/examples/ip_address.rb +1 -2
- data/examples/list.rb +124 -0
- data/examples/nbt.rb +195 -0
- data/lib/bindata.rb +4 -3
- data/lib/bindata/alignment.rb +86 -0
- data/lib/bindata/array.rb +21 -29
- data/lib/bindata/base.rb +82 -81
- data/lib/bindata/base_primitive.rb +66 -48
- data/lib/bindata/choice.rb +18 -28
- data/lib/bindata/deprecated.rb +17 -0
- data/lib/bindata/dsl.rb +25 -15
- data/lib/bindata/int.rb +2 -2
- data/lib/bindata/io.rb +8 -6
- data/lib/bindata/offset.rb +91 -0
- data/lib/bindata/primitive.rb +22 -11
- data/lib/bindata/record.rb +40 -10
- data/lib/bindata/sanitize.rb +15 -30
- data/lib/bindata/string.rb +16 -17
- data/lib/bindata/stringz.rb +0 -1
- data/lib/bindata/struct.rb +17 -6
- data/lib/bindata/trace.rb +52 -0
- data/lib/bindata/wrapper.rb +28 -6
- data/manual.haml +56 -10
- data/manual.md +318 -113
- data/spec/alignment_spec.rb +61 -0
- data/spec/array_spec.rb +139 -178
- data/spec/base_primitive_spec.rb +86 -111
- data/spec/base_spec.rb +200 -172
- data/spec/bits_spec.rb +45 -53
- data/spec/choice_spec.rb +91 -87
- data/spec/deprecated_spec.rb +36 -14
- data/spec/float_spec.rb +16 -68
- data/spec/int_spec.rb +26 -27
- data/spec/io_spec.rb +105 -105
- data/spec/lazy_spec.rb +50 -50
- data/spec/primitive_spec.rb +36 -36
- data/spec/record_spec.rb +134 -134
- data/spec/registry_spec.rb +34 -38
- data/spec/rest_spec.rb +8 -11
- data/spec/skip_spec.rb +9 -17
- data/spec/spec_common.rb +4 -0
- data/spec/string_spec.rb +92 -115
- data/spec/stringz_spec.rb +41 -74
- data/spec/struct_spec.rb +132 -153
- data/spec/system_spec.rb +115 -60
- data/spec/wrapper_spec.rb +63 -31
- data/tasks/pkg.rake +1 -1
- metadata +15 -7
data/ChangeLog
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
= BinData Changelog
|
2
2
|
|
3
|
+
== Version 1.3.0 (2011-01-25)
|
4
|
+
|
5
|
+
* BinData objects can now assign values when instantiating.
|
6
|
+
* Improved support for bit-based alignment.
|
7
|
+
* Updated reference manual.
|
8
|
+
* Added examples for declaring recursive structures.
|
9
|
+
* Objects deriving from BinData::Base should no longer override #initialize.
|
10
|
+
* Added BinData::Base(#new, #initialize_instance) to speed up
|
11
|
+
instantiation of multiple objects.
|
12
|
+
* Updated specs to rspec-1.3.0
|
13
|
+
* BinData::Struct.hide now expects symbols instead of strings.
|
14
|
+
|
3
15
|
== Version 1.2.2 (2010-12-14)
|
4
16
|
|
5
17
|
* Added Base.bindata_name method.
|
data/NEWS
CHANGED
@@ -1,3 +1,56 @@
|
|
1
|
+
= 1.3.0
|
2
|
+
|
3
|
+
== New features
|
4
|
+
|
5
|
+
You can now assign values to BinData objects when instantiating them.
|
6
|
+
|
7
|
+
Previously:
|
8
|
+
obj = BinData::Stringz.new(:max_length => 10)
|
9
|
+
obj.assign("abc")
|
10
|
+
|
11
|
+
Now:
|
12
|
+
obj = BinData::Stringz.new("abc", :max_length => 10)
|
13
|
+
|
14
|
+
== Backwards incompatible changes
|
15
|
+
|
16
|
+
This version makes some backwards incompatible changes for more advanced
|
17
|
+
users of BinData.
|
18
|
+
|
19
|
+
This only affects you if you have created your own custom types by
|
20
|
+
subclassing BinData::Base or BinData::BasePrimitive.
|
21
|
+
|
22
|
+
All instance variables must now be initialized in #initialize_instance.
|
23
|
+
Implementing #initialize is no longer allowed.
|
24
|
+
|
25
|
+
Run your existing code in $VERBOSE mode ("ruby -w"), and BinData will
|
26
|
+
inform you of any changes you need to make to your code.
|
27
|
+
|
28
|
+
If your code previously looked like:
|
29
|
+
|
30
|
+
class MyType < BinData::Base
|
31
|
+
register_self
|
32
|
+
|
33
|
+
def initialize(parameters = {}, parent = nil)
|
34
|
+
super
|
35
|
+
|
36
|
+
@var1 = ...
|
37
|
+
@var2 = ...
|
38
|
+
end
|
39
|
+
...
|
40
|
+
end
|
41
|
+
|
42
|
+
You must change it to look like:
|
43
|
+
|
44
|
+
class MyType < BinData::Base
|
45
|
+
register_self
|
46
|
+
|
47
|
+
def initialize_instance
|
48
|
+
@var1 = ...
|
49
|
+
@var2 = ...
|
50
|
+
end
|
51
|
+
...
|
52
|
+
end
|
53
|
+
|
1
54
|
= 1.0.0
|
2
55
|
|
3
56
|
Version 1.0.0 removes all deprecated features. If you are upgrading from a
|
data/Rakefile
CHANGED
data/examples/NBT.txt
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
Named Binary Tag specification
|
2
|
+
|
3
|
+
NBT (Named Binary Tag) is a tag based binary format designed to carry large amounts of binary data with smaller amounts of additional data.
|
4
|
+
An NBT file consists of a single GZIPped Named Tag of type TAG_Compound.
|
5
|
+
|
6
|
+
A Named Tag has the following format:
|
7
|
+
|
8
|
+
byte tagType
|
9
|
+
TAG_String name
|
10
|
+
[payload]
|
11
|
+
|
12
|
+
The tagType is a single byte defining the contents of the payload of the tag.
|
13
|
+
|
14
|
+
The name is a descriptive name, and can be anything (eg "cat", "banana", "Hello World!"). It has nothing to do with the tagType.
|
15
|
+
The purpose for this name is to name tags so parsing is easier and can be made to only look for certain recognized tag names.
|
16
|
+
Exception: If tagType is TAG_End, the name is skipped and assumed to be "".
|
17
|
+
|
18
|
+
The [payload] varies by tagType.
|
19
|
+
|
20
|
+
Note that ONLY Named Tags carry the name and tagType data. Explicitly identified Tags (such as TAG_String above) only contains the payload.
|
21
|
+
|
22
|
+
|
23
|
+
The tag types and respective payloads are:
|
24
|
+
|
25
|
+
TYPE: 0 NAME: TAG_End
|
26
|
+
Payload: None.
|
27
|
+
Note: This tag is used to mark the end of a list.
|
28
|
+
Cannot be named! If type 0 appears where a Named Tag is expected, the name is assumed to be "".
|
29
|
+
(In other words, this Tag is always just a single 0 byte when named, and nothing in all other cases)
|
30
|
+
|
31
|
+
TYPE: 1 NAME: TAG_Byte
|
32
|
+
Payload: A single signed byte (8 bits)
|
33
|
+
|
34
|
+
TYPE: 2 NAME: TAG_Short
|
35
|
+
Payload: A signed short (16 bits, big endian)
|
36
|
+
|
37
|
+
TYPE: 3 NAME: TAG_Int
|
38
|
+
Payload: A signed short (32 bits, big endian)
|
39
|
+
|
40
|
+
TYPE: 4 NAME: TAG_Long
|
41
|
+
Payload: A signed long (64 bits, big endian)
|
42
|
+
|
43
|
+
TYPE: 5 NAME: TAG_Float
|
44
|
+
Payload: A floating point value (32 bits, big endian, IEEE 754-2008, binary32)
|
45
|
+
|
46
|
+
TYPE: 6 NAME: TAG_Double
|
47
|
+
Payload: A floating point value (64 bits, big endian, IEEE 754-2008, binary64)
|
48
|
+
|
49
|
+
TYPE: 7 NAME: TAG_Byte_Array
|
50
|
+
Payload: TAG_Int length
|
51
|
+
An array of bytes of unspecified format. The length of this array is <length> bytes
|
52
|
+
|
53
|
+
TYPE: 8 NAME: TAG_String
|
54
|
+
Payload: TAG_Short length
|
55
|
+
An array of bytes defining a string in UTF-8 format. The length of this array is <length> bytes
|
56
|
+
|
57
|
+
TYPE: 9 NAME: TAG_List
|
58
|
+
Payload: TAG_Byte tagId
|
59
|
+
TAG_Int length
|
60
|
+
A sequential list of Tags (not Named Tags), of type <typeId>. The length of this array is <length> Tags
|
61
|
+
Notes: All tags share the same type.
|
62
|
+
|
63
|
+
TYPE: 10 NAME: TAG_Compound
|
64
|
+
Payload: A sequential list of Named Tags. This array keeps going until a TAG_End is found.
|
65
|
+
TAG_End end
|
66
|
+
Notes: If there's a nested TAG_Compound within this tag, that one will also have a TAG_End, so simply reading until the next TAG_End will not work.
|
67
|
+
The names of the named tags have to be unique within each TAG_Compound
|
68
|
+
The order of the tags is not guaranteed.
|
69
|
+
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
Decoding example:
|
75
|
+
(Use http://www.minecraft.net/docs/test.nbt to test your implementation)
|
76
|
+
|
77
|
+
|
78
|
+
First we start by reading a Named Tag.
|
79
|
+
After unzipping the stream, the first byte is a 10. That means the tag is a TAG_Compound (as expected by the specification).
|
80
|
+
|
81
|
+
The next two bytes are 0 and 11, meaning the name string consists of 11 UTF-8 characters. In this case, they happen to be "hello world".
|
82
|
+
That means our root tag is named "hello world". We can now move on to the payload.
|
83
|
+
|
84
|
+
From the specification, we see that TAG_Compound consists of a series of Named Tags, so we read another byte to find the tagType.
|
85
|
+
It happens to be an 8. The name is 4 letters long, and happens to be "name". Type 8 is TAG_String, meaning we read another two bytes to get the length,
|
86
|
+
then read that many bytes to get the contents. In this case, it's "Bananrama".
|
87
|
+
|
88
|
+
So now we know the TAG_Compound contains a TAG_String named "name" with the content "Bananrama"
|
89
|
+
|
90
|
+
We move on to reading the next Named Tag, and get a 0. This is TAG_End, which always has an implied name of "". That means that the list of entries
|
91
|
+
in the TAG_Compound is over, and indeed all of the NBT file.
|
92
|
+
|
93
|
+
So we ended up with this:
|
94
|
+
|
95
|
+
TAG_Compound("hello world"): 1 entries
|
96
|
+
{
|
97
|
+
TAG_String("name"): Bananrama
|
98
|
+
}
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
For a slightly longer test, download http://www.minecraft.net/docs/bigtest.nbt
|
103
|
+
You should end up with this:
|
104
|
+
|
105
|
+
TAG_Compound("Level"): 11 entries
|
106
|
+
{
|
107
|
+
TAG_Short("shortTest"): 32767
|
108
|
+
TAG_Long("longTest"): 9223372036854775807
|
109
|
+
TAG_Float("floatTest"): 0.49823147
|
110
|
+
TAG_String("stringTest"): HELLO WORLD THIS IS A TEST STRING ���!
|
111
|
+
TAG_Int("intTest"): 2147483647
|
112
|
+
TAG_Compound("nested compound test"): 2 entries
|
113
|
+
{
|
114
|
+
TAG_Compound("ham"): 2 entries
|
115
|
+
{
|
116
|
+
TAG_String("name"): Hampus
|
117
|
+
TAG_Float("value"): 0.75
|
118
|
+
}
|
119
|
+
TAG_Compound("egg"): 2 entries
|
120
|
+
{
|
121
|
+
TAG_String("name"): Eggbert
|
122
|
+
TAG_Float("value"): 0.5
|
123
|
+
}
|
124
|
+
}
|
125
|
+
TAG_List("listTest (long)"): 5 entries of type TAG_Long
|
126
|
+
{
|
127
|
+
TAG_Long: 11
|
128
|
+
TAG_Long: 12
|
129
|
+
TAG_Long: 13
|
130
|
+
TAG_Long: 14
|
131
|
+
TAG_Long: 15
|
132
|
+
}
|
133
|
+
TAG_Byte("byteTest"): 127
|
134
|
+
TAG_List("listTest (compound)"): 2 entries of type TAG_Compound
|
135
|
+
{
|
136
|
+
TAG_Compound: 2 entries
|
137
|
+
{
|
138
|
+
TAG_String("name"): Compound tag #0
|
139
|
+
TAG_Long("created-on"): 1264099775885
|
140
|
+
}
|
141
|
+
TAG_Compound: 2 entries
|
142
|
+
{
|
143
|
+
TAG_String("name"): Compound tag #1
|
144
|
+
TAG_Long("created-on"): 1264099775885
|
145
|
+
}
|
146
|
+
}
|
147
|
+
TAG_Byte_Array("byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))"): [1000 bytes]
|
148
|
+
TAG_Double("doubleTest"): 0.4931287132182315
|
149
|
+
}
|
data/examples/ip_address.rb
CHANGED
@@ -16,8 +16,7 @@ class IPAddr < BinData::Primitive
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
ip = IPAddr.new
|
20
|
-
ip.value = "127.0.0.1"
|
19
|
+
ip = IPAddr.new("127.0.0.1")
|
21
20
|
|
22
21
|
puts "human readable value: #{ip}" #=> 127.0.0.1
|
23
22
|
puts "binary representation: #{ip.to_binary_s.inspect}" #=> "\177\000\000\001"
|
data/examples/list.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
# An example of a recursively defined data format.
|
4
|
+
#
|
5
|
+
# This format is used to describe atoms and lists.
|
6
|
+
# It is recursive because lists can contain other lists.
|
7
|
+
#
|
8
|
+
# Atoms - contain a single integer
|
9
|
+
# Lists - contain a mixture of atoms and lists
|
10
|
+
#
|
11
|
+
# The binary representation is:
|
12
|
+
#
|
13
|
+
# Atoms - A single byte 'a' followed by an int32 containing the value.
|
14
|
+
# Lists - A single byte 'l' followed by an int32 denoting the number of
|
15
|
+
# items in the list. This is followed by all the items in the list.
|
16
|
+
#
|
17
|
+
# All integers are big endian.
|
18
|
+
#
|
19
|
+
#
|
20
|
+
# A first attempt at a declaration would be:
|
21
|
+
#
|
22
|
+
# class Atom < BinData::Record
|
23
|
+
# string :tag, :length => 1, :check_value => 'a'
|
24
|
+
# int32be :val
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# class List < BinData::Record
|
28
|
+
# string :tag, :length => 1, :check_value => 'l'
|
29
|
+
# int32be :num, :value => lambda { vals.length }
|
30
|
+
# array :vals, :initial_length => :num do
|
31
|
+
# choice :selection => ??? do
|
32
|
+
# atom
|
33
|
+
# list
|
34
|
+
# end
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
#
|
38
|
+
# Notice how we get stuck on attemping to write a declaration for
|
39
|
+
# the contents of the list. We can't determine if the list item is
|
40
|
+
# an atom or list because we haven't read it yet. It appears that
|
41
|
+
# we can't proceed.
|
42
|
+
#
|
43
|
+
# The cause of the problem is that the tag identifying the type is
|
44
|
+
# coupled with that type.
|
45
|
+
#
|
46
|
+
# The solution is to decouple the tag from the type. We introduce a
|
47
|
+
# new type 'Term' that is a thin container around the tag plus the
|
48
|
+
# type (atom or list).
|
49
|
+
#
|
50
|
+
# The declaration then becomes:
|
51
|
+
#
|
52
|
+
# class Term < BinData::Record; end # forward declaration
|
53
|
+
#
|
54
|
+
# class Atom < BinData::Wrapper
|
55
|
+
# int32be
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# class List < BinData::Record
|
59
|
+
# int32be :num, :value => lambda { vals.length }
|
60
|
+
# array :vals, :type => :term, :initial_length => :num
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# class Term < BinData::Record
|
64
|
+
# string :tag, :length => 1
|
65
|
+
# choice :term, :selection => :tag do
|
66
|
+
# atom 'a'
|
67
|
+
# list 'l'
|
68
|
+
# end
|
69
|
+
# end
|
70
|
+
|
71
|
+
|
72
|
+
class Term < BinData::Record; end # Forward declaration
|
73
|
+
|
74
|
+
class Atom < BinData::Wrapper
|
75
|
+
int32be
|
76
|
+
|
77
|
+
def decode
|
78
|
+
snapshot
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.encode(val)
|
82
|
+
Atom.new(val)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
class List < BinData::Record
|
87
|
+
int32be :num, :value => lambda { vals.length }
|
88
|
+
array :vals, :initial_length => :num, :type => :term
|
89
|
+
|
90
|
+
def decode
|
91
|
+
vals.collect { |v| v.decode }
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.encode(val)
|
95
|
+
List.new(:vals => val.collect { |v| Term.encode(v) })
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Term < BinData::Record
|
100
|
+
string :tag, :length => 1
|
101
|
+
choice :term, :selection => :tag do
|
102
|
+
atom 'a'
|
103
|
+
list 'l'
|
104
|
+
end
|
105
|
+
|
106
|
+
def decode
|
107
|
+
term.decode
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.encode(val)
|
111
|
+
if Fixnum === val
|
112
|
+
Term.new(:tag => 'a', :term => Atom.encode(val))
|
113
|
+
else
|
114
|
+
Term.new(:tag => 'l', :term => List.encode(val))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
p Term.encode(4)
|
121
|
+
p Term.encode(4).decode
|
122
|
+
puts
|
123
|
+
p Term.encode([1, [2, 3], 4])
|
124
|
+
p Term.encode([1, [2, 3], 4]).decode
|
data/examples/nbt.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
require 'bindata'
|
2
|
+
|
3
|
+
# An example reader for Minecraft's NBT format.
|
4
|
+
# http://www.minecraft.net/docs/NBT.txt
|
5
|
+
#
|
6
|
+
# This is an example of how to write a BinData
|
7
|
+
# declaration for a recursively defined file format.
|
8
|
+
module Nbt
|
9
|
+
|
10
|
+
TAG_NAMES = {
|
11
|
+
0 => "End",
|
12
|
+
1 => "Byte",
|
13
|
+
2 => "Short",
|
14
|
+
3 => "Int",
|
15
|
+
4 => "Long",
|
16
|
+
5 => "Float",
|
17
|
+
6 => "Double",
|
18
|
+
7 => "Byte_Array",
|
19
|
+
8 => "String",
|
20
|
+
9 => "List",
|
21
|
+
10 => "Compound"
|
22
|
+
}
|
23
|
+
|
24
|
+
# NBT.txt line 25
|
25
|
+
class TagEnd < BinData::Primitive
|
26
|
+
def get; ""; end
|
27
|
+
def set(v); end
|
28
|
+
|
29
|
+
def to_formatted_s(indent = 0); to_s; end
|
30
|
+
end
|
31
|
+
|
32
|
+
# NBT.txt line 31
|
33
|
+
class TagByte < BinData::Wrapper
|
34
|
+
int8
|
35
|
+
|
36
|
+
def to_formatted_s(indent = 0); to_s; end
|
37
|
+
end
|
38
|
+
|
39
|
+
# NBT.txt line 34
|
40
|
+
class TagShort < BinData::Wrapper
|
41
|
+
int16be
|
42
|
+
|
43
|
+
def to_formatted_s(indent = 0); to_s; end
|
44
|
+
end
|
45
|
+
|
46
|
+
# NBT.txt line 37
|
47
|
+
class TagInt < BinData::Wrapper
|
48
|
+
int32be
|
49
|
+
|
50
|
+
def to_formatted_s(indent = 0); to_s; end
|
51
|
+
end
|
52
|
+
|
53
|
+
# NBT.txt line 40
|
54
|
+
class TagLong < BinData::Wrapper
|
55
|
+
int64be
|
56
|
+
|
57
|
+
def to_formatted_s(indent = 0); to_s; end
|
58
|
+
end
|
59
|
+
|
60
|
+
# NBT.txt line 43
|
61
|
+
class TagFloat < BinData::Wrapper
|
62
|
+
float_be
|
63
|
+
|
64
|
+
def to_formatted_s(indent = 0); to_s; end
|
65
|
+
end
|
66
|
+
|
67
|
+
# NBT.txt line 46
|
68
|
+
class TagDouble < BinData::Wrapper
|
69
|
+
double_be
|
70
|
+
|
71
|
+
def to_formatted_s(indent = 0); to_s; end
|
72
|
+
end
|
73
|
+
|
74
|
+
# NBT.txt line 49
|
75
|
+
class TagByteArray < BinData::Record
|
76
|
+
int32be :len, :value => lambda { data.length }
|
77
|
+
string :data, :read_length => :len
|
78
|
+
|
79
|
+
def to_formatted_s(indent = 0)
|
80
|
+
"[#{len} bytes]"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# NBT.txt line 53
|
85
|
+
class TagString < BinData::Primitive
|
86
|
+
int16be :len, :value => lambda { data.length }
|
87
|
+
string :data, :read_length => :len
|
88
|
+
|
89
|
+
def get
|
90
|
+
self.data
|
91
|
+
end
|
92
|
+
|
93
|
+
def set(v)
|
94
|
+
self.data = v
|
95
|
+
end
|
96
|
+
|
97
|
+
def to_formatted_s(indent = 0); to_s; end
|
98
|
+
end
|
99
|
+
|
100
|
+
## Payload is the most important class to understand.
|
101
|
+
## This abstraction allows recursive formats.
|
102
|
+
## eg. lists can contain lists can contain lists.
|
103
|
+
|
104
|
+
# Forward references used by Payload
|
105
|
+
class TagCompound < BinData::Record; end
|
106
|
+
class TagList < BinData::Record; end
|
107
|
+
|
108
|
+
# NBT.txt line 10
|
109
|
+
class Payload < BinData::Wrapper
|
110
|
+
mandatory_parameter :tag_selector
|
111
|
+
choice :selection => :tag_selector do
|
112
|
+
tag_end 0
|
113
|
+
tag_byte 1
|
114
|
+
tag_short 2
|
115
|
+
tag_int 3
|
116
|
+
tag_long 4
|
117
|
+
tag_float 5
|
118
|
+
tag_double 6
|
119
|
+
tag_byte_array 7
|
120
|
+
tag_string 8
|
121
|
+
tag_list 9
|
122
|
+
tag_compound 10
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# NBT.txt line 6, 27
|
127
|
+
class NamedTag < BinData::Record
|
128
|
+
int8 :tag_id
|
129
|
+
tag_string :name, :onlyif => :not_end_tag?
|
130
|
+
payload :payload, :onlyif => :not_end_tag?, :tag_selector => :tag_id
|
131
|
+
|
132
|
+
def not_end_tag?
|
133
|
+
tag_id != 0
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_formatted_s(indent = 0)
|
137
|
+
" " * indent +
|
138
|
+
"TAG_#{TAG_NAMES[tag_id]}(\"#{name}\"): " +
|
139
|
+
payload.to_formatted_s(indent) + "\n"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# NBT.txt line 57
|
144
|
+
class TagList < BinData::Record
|
145
|
+
int8 :tag_id
|
146
|
+
int32be :len, :value => lambda { data.length }
|
147
|
+
array :data, :initial_length => :len do
|
148
|
+
payload :tag_selector => :tag_id
|
149
|
+
end
|
150
|
+
|
151
|
+
def to_formatted_s(indent = 0)
|
152
|
+
pre = " " * indent
|
153
|
+
tag_type = "TAG_#{TAG_NAMES[tag_id]}"
|
154
|
+
|
155
|
+
"#{len} entries of type #{tag_type}\n" +
|
156
|
+
pre + "{\n" +
|
157
|
+
data.collect { |el| " #{pre}#{tag_type}: #{el.to_formatted_s(indent + 1)}\n" }.join("") +
|
158
|
+
pre + "}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# NBT.txt line 63
|
163
|
+
class TagCompound < BinData::Record
|
164
|
+
array :data, :read_until => lambda { element.tag_id == 0 } do
|
165
|
+
named_tag
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_formatted_s(indent = 0)
|
169
|
+
pre = " " * indent
|
170
|
+
"#{data.length - 1} entries\n" +
|
171
|
+
pre + "{\n" +
|
172
|
+
data[0..-2].collect { |el| el.to_formatted_s(indent + 1) }.join("") +
|
173
|
+
pre + "}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# NBT.txt line 3
|
178
|
+
class Nbt < BinData::Wrapper
|
179
|
+
named_tag
|
180
|
+
|
181
|
+
def self.read(io)
|
182
|
+
require 'zlib'
|
183
|
+
super(Zlib::GzipReader.new(io))
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
if $0 == __FILE__
|
189
|
+
require 'stringio'
|
190
|
+
|
191
|
+
bigtest_nbt = StringIO.new "\037\213\b\000\000\000\000\000\000\003\355T\317O\032A\024~\302\002\313\226\202\261\304\020c\314\253\265\204\245\333\315B\021\211\261\210\026,\232\r\032\330\2501\206\270+\303\202.\273fw\260\361\324K{lz\353?\323#\177C\317\275\366\277\240\303/{i\317\275\3602\311\367\346\275o\346{o&y\002\004TrO,\016x\313\261M\215x\364\343pb>\b{\035\307\245\223\030\017\202G\335\356\204\002b\265\242\252\307xv\\W\313\250U\017\e\310\326\036j\225\206\206\r\255~X{\217\203\317\203O\203o\317\003\020n[\216>\276\2458Ld\375\020\352\332t\246\#@\334f.i\341\265\323\273s\372v\v)\333\v\340\357\350=\0368[\357\021\bV\365\336]\337\v@\340^\267\372d\267\004\000\214ALs\306\bUL\323 .}\244\300\310\302\020\263\272\336X\vS\243\356D\216E\0030\261'S\214L\361\351\024\243S\214\205\341\331\237\343\263\362D\201\245|3\335\330\273\307\252u\023_(\034\b\327.\321Y?\257\035\e`!Y\337\372\361\005\376\301\316\374\235\275\000\274\361@\311\370\205B@F\376\236\353\352\017\223:h\207`\273\35327\243(\n\216\273\365\320ic\312N\333\351\354\346\346+;\275%\276dI\t=\252\273\224\375\030~\350\322\016\332o\025L\261h>+\341\233\234\204\231\274\204\005\teY\026E\000\377/(\256/\362\302\262\244.\035 wZ;\271\214\312\347)\337QA\311\026\265\305m\241*\255,\3051\177\272z\222\216^\235_\370\022\005#\e\321\366\267w\252\315\225r\274\236\337X]K\227\256\222\027\271D\320\200\310\372>\277\263\334T\313\aun\243\266vY\222\223\251\334QP\231k\3145\346\032\377W#\bB\313\351\e\326x\302\354\376\374z\373}x\323\204\337\324\362\244\373\b\006\000\000"
|
192
|
+
|
193
|
+
nbt = Nbt::Nbt.read(bigtest_nbt)
|
194
|
+
puts nbt.to_formatted_s
|
195
|
+
end
|