craftbook-nbt 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +420 -0
- data/Rakefile +5 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/craftbook-nbt.gemspec +34 -0
- data/lib/craftbook/nbt/byte_array_tag.rb +37 -0
- data/lib/craftbook/nbt/byte_tag.rb +61 -0
- data/lib/craftbook/nbt/compound_tag.rb +32 -0
- data/lib/craftbook/nbt/container_tag.rb +55 -0
- data/lib/craftbook/nbt/double_tag.rb +40 -0
- data/lib/craftbook/nbt/enumerable_tag.rb +106 -0
- data/lib/craftbook/nbt/float_tag.rb +35 -0
- data/lib/craftbook/nbt/int_array_tag.rb +38 -0
- data/lib/craftbook/nbt/int_tag.rb +49 -0
- data/lib/craftbook/nbt/list_tag.rb +69 -0
- data/lib/craftbook/nbt/long_array_tag.rb +37 -0
- data/lib/craftbook/nbt/long_tag.rb +50 -0
- data/lib/craftbook/nbt/short_tag.rb +48 -0
- data/lib/craftbook/nbt/snbt/lexer.rb +161 -0
- data/lib/craftbook/nbt/snbt/snbt.rb +121 -0
- data/lib/craftbook/nbt/snbt/snbt.rex +74 -0
- data/lib/craftbook/nbt/snbt.rb +2 -0
- data/lib/craftbook/nbt/string_tag.rb +40 -0
- data/lib/craftbook/nbt/tag.rb +197 -0
- data/lib/craftbook/nbt/tag_builder.rb +220 -0
- data/lib/craftbook/nbt/value_tag.rb +51 -0
- data/lib/craftbook/nbt/version.rb +10 -0
- data/lib/craftbook/nbt.rb +298 -0
- metadata +112 -0
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "craftbook/nbt"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/craftbook/nbt/version'
|
4
|
+
|
5
|
+
#noinspection RubyResolve
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'craftbook-nbt'
|
8
|
+
spec.version = CraftBook::NBT::VERSION
|
9
|
+
spec.authors = ['ForeverZer0']
|
10
|
+
spec.email = ['efreed09@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = 'A feature-rich and complete Ruby implementation of the Named Binary Tag (NBT) format and SNBT parser.'
|
13
|
+
spec.description = 'A feature-rich and complete Ruby implementation of the Named Binary Tag (NBT) format. While it is an integral part of the broader CraftBook API, it is an independent module with no dependencies, and can be used for any purpose where reading/writing/converting the NBT format is required.'
|
14
|
+
spec.homepage = 'https://github.com/ForeverZer0/craftbook-nbt'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
spec.required_ruby_version = '>= 2.4.0'
|
17
|
+
|
18
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
19
|
+
spec.metadata['source_code_uri'] = 'https://github.com/ForeverZer0/craftbook-nbt'
|
20
|
+
spec.metadata['changelog_uri'] = 'https://github.com/ForeverZer0/craftbook-nbt/CHANGELOG.md'
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
26
|
+
end
|
27
|
+
spec.bindir = 'exe'
|
28
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
|
31
|
+
spec.add_development_dependency('rake', '~> 13.0')
|
32
|
+
spec.add_development_dependency('rexical', '~> 1.0')
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
##
|
6
|
+
# Represents a contiguous collection of signed 8-bit integers in the range of `-128` to `127` inclusive.
|
7
|
+
class ByteArrayTag < EnumerableTag
|
8
|
+
|
9
|
+
##
|
10
|
+
# Creates a new instance of the {ByteArrayTag} class.
|
11
|
+
#
|
12
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
13
|
+
# @param values [Array<Numeric>] Zero or more values to add during initialization.
|
14
|
+
def initialize(name, *values)
|
15
|
+
super(TYPE_BYTE_ARRAY, name, values)
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# @return [Hash{Symbol => Object}] the hash-representation of this object.
|
20
|
+
def to_h
|
21
|
+
{ name: @name, type: @type, values: @values }
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# @return [String] the NBT tag as a formatted and human-readable string.
|
26
|
+
def to_s
|
27
|
+
"TAG_Byte_Array(#{@name ? "\"#{@name}\"" : 'None'}): #{size} #{size == 1 ? 'item' : 'items'}"
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# @return [String] the NBT tag as an SNBT string.
|
32
|
+
def stringify
|
33
|
+
"#{snbt_prefix}[B;#{to_a.join(',')}]"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
|
6
|
+
##
|
7
|
+
# A tag type representing an 8-bit signed integer in the range of `-128` and `127` inclusive.
|
8
|
+
class ByteTag < ValueTag
|
9
|
+
|
10
|
+
##
|
11
|
+
# The minimum permissible value for this type.
|
12
|
+
MIN = -0x80
|
13
|
+
|
14
|
+
##
|
15
|
+
# The maximum permissible value for this type.
|
16
|
+
MAX = 0x7F
|
17
|
+
|
18
|
+
##
|
19
|
+
# @!attribute [rw] value
|
20
|
+
# The value of the tag. Values of `true` and `false` will be converted to `1` and `0` respectfully.
|
21
|
+
# @return [Numeric,Boolean] the value of the tag.
|
22
|
+
|
23
|
+
##
|
24
|
+
# Creates a new instance of the {DoubleTag} class.
|
25
|
+
#
|
26
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
27
|
+
# @param value [Numeric,Boolean] The value of the tag.
|
28
|
+
#
|
29
|
+
# @note Values of `true` and `false` will be converted to `1` and `0` respectfully.
|
30
|
+
def initialize(name, value = 0)
|
31
|
+
super(TYPE_BYTE, name, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def value=(value)
|
35
|
+
@value = case value
|
36
|
+
when TrueClass then 1
|
37
|
+
when FalseClass then 0
|
38
|
+
else validate(value, MIN, MAX)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# @return [Boolean] the value of the tag as boolean.
|
44
|
+
def bool
|
45
|
+
@value != 0
|
46
|
+
end
|
47
|
+
|
48
|
+
##
|
49
|
+
# @return [String] the NBT tag as a formatted and human-readable string.
|
50
|
+
def to_s
|
51
|
+
"TAG_Byte(#{@name ? "\"#{@name}\"" : 'None'}): #{@value}"
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# @return [String] the NBT tag as an SNBT string.
|
56
|
+
def stringify
|
57
|
+
"#{snbt_prefix}#{@value}B"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
|
6
|
+
##
|
7
|
+
# Represents a collection of **named** tags, order not guaranteed.
|
8
|
+
class CompoundTag < ContainerTag
|
9
|
+
|
10
|
+
##
|
11
|
+
# Creates a new instance of the {CompoundTag} class.
|
12
|
+
#
|
13
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
14
|
+
# @param values [Array<Tag>] Zero or more values to add during initialization.
|
15
|
+
def initialize(name, *values)
|
16
|
+
super(TYPE_COMPOUND, name, *values)
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# @return [String] the NBT tag as a formatted and human-readable string.
|
21
|
+
def to_s
|
22
|
+
"TAG_Compound(#{@name ? "\"#{@name}\"" : 'None'}): #{size} #{size == 1 ? 'child' : 'children'}"
|
23
|
+
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# @return [String] the NBT tag as an SNBT string.
|
27
|
+
def stringify
|
28
|
+
"{#{snbt_prefix}{#{map(&:stringify).join(',')}}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
module NBT
|
4
|
+
|
5
|
+
##
|
6
|
+
# @abstract
|
7
|
+
# Abstract base class for tag types that can contain other {Tag} objects as children.
|
8
|
+
class ContainerTag < EnumerableTag
|
9
|
+
|
10
|
+
##
|
11
|
+
# Creates a new instance of the {EnumerableTag} class.
|
12
|
+
#
|
13
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
14
|
+
# @param values [Array<Tag>] Zero or more values to add during initialization.
|
15
|
+
def initialize(type, name, *values)
|
16
|
+
super(type, name, *values)
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# @return [Hash{Symbol => Object}] the hash-representation of this object.
|
21
|
+
def to_h
|
22
|
+
a = @values.map { |child| child.to_h }
|
23
|
+
{ name: @name, type: @type, values: a }
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Outputs the NBT tag as a formatted and tree-structured string.
|
28
|
+
#
|
29
|
+
# @param io [IO,#puts] An IO-like object that responds to #puts.
|
30
|
+
# @param level [Integer] The indentation level.
|
31
|
+
# @param indent [String] The string inserted for each level of indent.
|
32
|
+
#
|
33
|
+
# @return [void]
|
34
|
+
def pretty_print(io = STDOUT, level = 0, indent = ' ')
|
35
|
+
space = indent * level
|
36
|
+
io.puts(space + to_s)
|
37
|
+
io.puts(space + '{')
|
38
|
+
each { |child| child.pretty_print(io, level + 1, indent) }
|
39
|
+
io.puts(space + '}')
|
40
|
+
end
|
41
|
+
|
42
|
+
protected
|
43
|
+
|
44
|
+
def parse_hash(hash)
|
45
|
+
@values = []
|
46
|
+
values = hash[:values]
|
47
|
+
raise(ParseError, "invalid array") unless values.is_a?(Array)
|
48
|
+
values.each do |value|
|
49
|
+
raise(ParseError, "expected JSON object") unless value.is_a?(Hash)
|
50
|
+
push(Tag.from_hash(value))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
|
6
|
+
##
|
7
|
+
# Represents a IEEE-754 double-precision floating point number (NaN possible).
|
8
|
+
class DoubleTag < ValueTag
|
9
|
+
|
10
|
+
##
|
11
|
+
# @!attribute [rw] value
|
12
|
+
# @return [Float] the value of the tag.
|
13
|
+
|
14
|
+
##
|
15
|
+
# Creates a new instance of the {DoubleTag} class.
|
16
|
+
#
|
17
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
18
|
+
# @param value [Float] The value of the tag.
|
19
|
+
def initialize(name, value = 0.0)
|
20
|
+
super(TYPE_DOUBLE, name, value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def value=(value)
|
24
|
+
@value = Float(value)
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# @return [String] the NBT tag as a formatted and human-readable string.
|
29
|
+
def to_s
|
30
|
+
"TAG_Double(#{@name ? "\"#{@name}\"" : 'None'}): #{@value}"
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# @return [String] the NBT tag as an SNBT string.
|
35
|
+
def stringify
|
36
|
+
"#{snbt_prefix}#{@value}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
##
|
6
|
+
# @abstract
|
7
|
+
# Abstract base class for tags that can be enumerated.
|
8
|
+
class EnumerableTag < Tag
|
9
|
+
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
##
|
13
|
+
# Creates a new instance of the {EnumerableTag} class.
|
14
|
+
#
|
15
|
+
# @param type [Integer] One of the `TAG_*` constants indicating the primitive tag type.
|
16
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
17
|
+
# @param values [Array<Object>] Zero or more values to add during initialization.
|
18
|
+
def initialize(type, name, *values)
|
19
|
+
super(type, name)
|
20
|
+
@values = Array.new
|
21
|
+
values.each { |value| push(value) }
|
22
|
+
end
|
23
|
+
|
24
|
+
##
|
25
|
+
# Appends a value as a child of this instance.
|
26
|
+
#
|
27
|
+
# @param child [Object] The value to add.
|
28
|
+
#
|
29
|
+
# @raise [TypeError] When `child` is `nil`.
|
30
|
+
# @return [Object] the value that was added.
|
31
|
+
def push(child)
|
32
|
+
@values.push(validate(child))
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# @overload each(&block)
|
37
|
+
# When called with a block, yields each child element to the block before returning `self`.
|
38
|
+
# @yieldparam child [Object] Yields a child element to the block.
|
39
|
+
# @return [self]
|
40
|
+
#
|
41
|
+
# @overload each
|
42
|
+
# When called without a block, returns an Enumerator object for this instance.
|
43
|
+
# @return [Enumerable]
|
44
|
+
def each
|
45
|
+
return enum_for(__method__) unless block_given?
|
46
|
+
@values.compact.each { |child| yield child }
|
47
|
+
self
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# @return [Hash{Symbol => Object}] the hash-representation of this object.
|
52
|
+
def to_h
|
53
|
+
{ name: @name, type: @type, values: @values }
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# @return [Integer] the number of child elements.
|
58
|
+
def size
|
59
|
+
@values.compact.size
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Retrieves the child at the given `index`, or `nil` if index is out of bounds.
|
64
|
+
#
|
65
|
+
# @param index [Integer] The zero-based index of the child element to retrieve.
|
66
|
+
# @return [Object] The element, or `nil` if index was out of bounds.
|
67
|
+
def [](index)
|
68
|
+
@values[index]
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Sets the child element at the given `index`.
|
73
|
+
#
|
74
|
+
# @param index [Integer] The zero-based index of the child element to set.
|
75
|
+
# @param value [Object] The value to set.
|
76
|
+
#
|
77
|
+
# @return [Object] the value that was passed in.
|
78
|
+
#
|
79
|
+
# @raise [TypeError] When `value` is `nil`.
|
80
|
+
# @note Unlike normal Array object, when index is beyond the bounds of the collection, it will not insert `nil`
|
81
|
+
# elements to fill the space, the new item is simply appended to the end of the collection.
|
82
|
+
def []=(index, value)
|
83
|
+
validate(value)
|
84
|
+
@values[index] = value
|
85
|
+
end
|
86
|
+
|
87
|
+
alias_method :<<, :push
|
88
|
+
alias_method :add, :push
|
89
|
+
alias_method :length, :size
|
90
|
+
|
91
|
+
protected
|
92
|
+
|
93
|
+
def validate(child)
|
94
|
+
raise(TypeError, 'enumerable tag cannot contain nil') unless child
|
95
|
+
child
|
96
|
+
end
|
97
|
+
|
98
|
+
def parse_hash(hash)
|
99
|
+
@values = []
|
100
|
+
values = hash[:values]
|
101
|
+
raise(ParseError, "invalid array") unless values.is_a?(Array)
|
102
|
+
values.each { |value| push(value) }
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
|
6
|
+
##
|
7
|
+
# Represents a IEEE-754 single-precision floating point number (NaN possible).
|
8
|
+
class FloatTag < ValueTag
|
9
|
+
|
10
|
+
##
|
11
|
+
# @!attribute [rw] value
|
12
|
+
# @return [Numeric] the value of the tag.
|
13
|
+
|
14
|
+
def initialize(name, value = 0.0)
|
15
|
+
super(TYPE_FLOAT, name, value)
|
16
|
+
end
|
17
|
+
|
18
|
+
def value=(value)
|
19
|
+
@value = Float(value)
|
20
|
+
end
|
21
|
+
|
22
|
+
##
|
23
|
+
# @return [String] the NBT tag as a formatted and human-readable string.
|
24
|
+
def to_s
|
25
|
+
"TAG_Float(#{@name ? "\"#{@name}\"" : 'None'}): #{@value}"
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# @return [String] the NBT tag as an SNBT string.
|
30
|
+
def stringify
|
31
|
+
"#{snbt_prefix}#{@value}F"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|