examples/nbt.rb
require 'bindata'
# An example reader for Minecraft's NBT format.
# http://www.minecraft.net/docs/NBT.txt
#
# This is an example of how to write a BinData
# declaration for a recursively defined file format.
module Nbt
TAG_NAMES = {
0 => "End",
1 => "Byte",
2 => "Short",
3 => "Int",
4 => "Long",
5 => "Float",
6 => "Double",
7 => "Byte_Array",
8 => "String",
9 => "List",
10 => "Compound"
}
# NBT.txt line 25
class TagEnd < BinData::Primitive
def get; ""; end
def set(v); end
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 31
class TagByte < BinData::Int8
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 34
class TagShort < BinData::Int16be
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 37
class TagInt < BinData::Int32be
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 40
class TagLong < BinData::Int64be
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 43
class TagFloat < BinData::FloatBe
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 46
class TagDouble < BinData::DoubleBe
def to_formatted_s(indent = 0); to_s; end
end
# NBT.txt line 49
class TagByteArray < BinData::Record
int32be :len, value: -> { data.length }
string :data, read_length: :len
def to_formatted_s(indent = 0)
"[#{len} bytes]"
end
end
# NBT.txt line 53
class TagString < BinData::Primitive
int16be :len, value: -> { data.length }
string :data, read_length: :len
def get
self.data
end
def set(v)
self.data = v
end
def to_formatted_s(indent = 0); to_s; end
end
## Payload is the most important class to understand.
## This abstraction allows recursive formats.
## eg. lists can contain lists can contain lists.
# Forward references used by Payload
class TagCompound < BinData::Record; end
class TagList < BinData::Record; end
# NBT.txt line 10
class Payload < BinData::Choice
tag_end 0
tag_byte 1
tag_short 2
tag_int 3
tag_long 4
tag_float 5
tag_double 6
tag_byte_array 7
tag_string 8
tag_list 9
tag_compound 10
end
# NBT.txt line 6, 27
class NamedTag < BinData::Record
int8 :tag_id
tag_string :name, onlyif: :not_end_tag?
payload :payload, onlyif: :not_end_tag?, selection: :tag_id
def not_end_tag?
tag_id != 0
end
def to_formatted_s(indent = 0)
" " * indent +
"TAG_#{TAG_NAMES[tag_id]}(\"#{name}\"): " +
payload.to_formatted_s(indent) + "\n"
end
end
# NBT.txt line 57
class TagList < BinData::Record
int8 :tag_id
int32be :len, value: -> { data.length }
array :data, initial_length: :len do
payload selection: :tag_id
end
def to_formatted_s(indent = 0)
pre = " " * indent
tag_type = "TAG_#{TAG_NAMES[tag_id]}"
"#{len} entries of type #{tag_type}\n" +
pre + "{\n" +
data.collect { |el| " #{pre}#{tag_type}: #{el.to_formatted_s(indent + 1)}\n" }.join("") +
pre + "}"
end
end
# NBT.txt line 63
class TagCompound < BinData::Record
array :data, read_until: -> { element.tag_id == 0 } do
named_tag
end
def to_formatted_s(indent = 0)
pre = " " * indent
"#{data.length - 1} entries\n" +
pre + "{\n" +
data[0..-2].collect { |el| el.to_formatted_s(indent + 1) }.join("") +
pre + "}"
end
end
# NBT.txt line 3
class Nbt < NamedTag
def self.read(io)
require 'zlib'
super(Zlib::GzipReader.new(io))
end
end
end
if $0 == __FILE__
require 'stringio'
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"
nbt = Nbt::Nbt.read(bigtest_nbt)
puts nbt.to_formatted_s
end