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.
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ task(:lexer) do
4
+ `rex lib/craftbook/nbt/snbt/snbt.rex -o lib/craftbook/nbt/snbt/lexer.rb`
5
+ end
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,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -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