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
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
module CraftBook
|
3
|
+
|
4
|
+
module NBT
|
5
|
+
##
|
6
|
+
# @abstract
|
7
|
+
# Abstract base class for tags that can contain a single primitive value.
|
8
|
+
class ValueTag < Tag
|
9
|
+
|
10
|
+
##
|
11
|
+
# @!attribute [rw] value
|
12
|
+
# @return [Object] the value of the tag.
|
13
|
+
|
14
|
+
attr_reader :value
|
15
|
+
|
16
|
+
##
|
17
|
+
# Creates a new instance of the {ValueTag} class.
|
18
|
+
#
|
19
|
+
# @param name [String,NilClass] The name of the tag, or `nil` when unnamed.
|
20
|
+
# @param value [Object] The value of the tag.
|
21
|
+
def initialize(type, name, value)
|
22
|
+
super(type, name)
|
23
|
+
self.value = value
|
24
|
+
end
|
25
|
+
|
26
|
+
def value=(value)
|
27
|
+
@value = value
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# @return [Hash{Symbol => Object}] the hash-representation of this object.
|
32
|
+
def to_h
|
33
|
+
{ name: @name, type: @type, value: @value }
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
def validate(value, min, max)
|
39
|
+
raise(TypeError, 'value cannot be nil') unless value
|
40
|
+
return value if value.between?(min, max)
|
41
|
+
raise(ArgumentError, sprintf("value must be between 0x%X and 0x%X inclusive", min, max))
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def parse_hash(hash)
|
47
|
+
self.value = hash[:value]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,298 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'stringio'
|
5
|
+
require 'zlib'
|
6
|
+
|
7
|
+
require_relative 'nbt/version'
|
8
|
+
require_relative 'nbt/tag'
|
9
|
+
require_relative 'nbt/value_tag'
|
10
|
+
require_relative 'nbt/enumerable_tag'
|
11
|
+
require_relative 'nbt/container_tag'
|
12
|
+
require_relative 'nbt/byte_tag'
|
13
|
+
require_relative 'nbt/short_tag'
|
14
|
+
require_relative 'nbt/int_tag'
|
15
|
+
require_relative 'nbt/long_tag'
|
16
|
+
require_relative 'nbt/float_tag'
|
17
|
+
require_relative 'nbt/double_tag'
|
18
|
+
require_relative 'nbt/string_tag'
|
19
|
+
require_relative 'nbt/byte_array_tag'
|
20
|
+
require_relative 'nbt/int_array_tag'
|
21
|
+
require_relative 'nbt/long_array_tag'
|
22
|
+
require_relative 'nbt/list_tag'
|
23
|
+
require_relative 'nbt/compound_tag'
|
24
|
+
require_relative 'nbt/tag_builder'
|
25
|
+
require_relative 'nbt/snbt/snbt'
|
26
|
+
|
27
|
+
##
|
28
|
+
# Top-level namespace for the CraftBook API.
|
29
|
+
module CraftBook
|
30
|
+
|
31
|
+
##
|
32
|
+
# Top-level namespace for the independent Named Binary Tag (NBT) module of the CraftBook API, providing classes and
|
33
|
+
# for reading and writing NBT tags used by the Java editions of Minecraft.
|
34
|
+
#
|
35
|
+
# @api NBT
|
36
|
+
# @author Eric "ForeverZer0" Freed
|
37
|
+
module NBT
|
38
|
+
|
39
|
+
##
|
40
|
+
# Exception class used for errors relating to parsing and invalid formats.
|
41
|
+
class ParseError < StandardError
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# The encoding used for all strings.
|
46
|
+
ENCODING = Encoding::UTF_8
|
47
|
+
|
48
|
+
##
|
49
|
+
# Serializes and writes the specified {Tag} to an IO-like object.
|
50
|
+
#
|
51
|
+
# @param io [IO,#write] An IO-like object that responds to `#write`
|
52
|
+
# @param tag [Tag] A {Tag} instance to write. If `io` represents a file stream, the specification expects this to
|
53
|
+
# be a {CompoundTag}.
|
54
|
+
#
|
55
|
+
# @return [Integer] The number of bytes written.
|
56
|
+
def self.write(io, tag)
|
57
|
+
unless io.is_a?(IO) || io.respond_to?(:write)
|
58
|
+
raise(ArgumentError, "object must be an IO instance or respond to #write")
|
59
|
+
end
|
60
|
+
write_tag(io, tag, false)
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Serializes and writes the specified {Tag} to a file at the specified `path`. If file already exists at that
|
65
|
+
# location, it will be overwritten.
|
66
|
+
#
|
67
|
+
# @param path [String] The path to the file to write to.
|
68
|
+
# @param compound_tag [Tag] A {CompoundTag} instance to write.
|
69
|
+
# @param opts [Hash{Symbol => Symbol}] Options hash.
|
70
|
+
#
|
71
|
+
# @option opts [Symbol] :compression (:gzip) The type of compression to use when writing, if any.
|
72
|
+
# Valid values include:
|
73
|
+
# <ul>
|
74
|
+
# <li><code>:none</code> No compression</li>
|
75
|
+
# <li><code>:gzip</code> GZip compression</li>
|
76
|
+
# <li><code>:zlib</code> ZLib compression (DEFLATE with 2 byte header and post-fixed CRC checksum)</li>
|
77
|
+
# </ul>
|
78
|
+
# @option opts [Symbol] :level (:default) The level of compression to use, ignored when no compression is specified.
|
79
|
+
# Valid values include:
|
80
|
+
# <ul>
|
81
|
+
# <li><code>:default</code> The default compression employed by the specified algorithm.</li>
|
82
|
+
# <li><code>:none</code> No compression. Compressions formats will still include their additional meta-data.</li>
|
83
|
+
# <li><code>:optimal</code> Favor high compression-rate over speed.</li>
|
84
|
+
# <li><code>:fastest</code> Favor speed over compression-rate.</li>
|
85
|
+
# </ul>
|
86
|
+
#
|
87
|
+
# @return [Integer] The number of bytes written.
|
88
|
+
def self.write_file(path, compound_tag, **opts)
|
89
|
+
|
90
|
+
compression = opts[:compression] || :gzip
|
91
|
+
level = case opts[:level]
|
92
|
+
when nil then Zlib::DEFAULT_COMPRESSION
|
93
|
+
when :default then Zlib::DEFAULT_COMPRESSION
|
94
|
+
when :none then Zlib::NO_COMPRESSION
|
95
|
+
when :optimal then Zlib::BEST_COMPRESSION
|
96
|
+
when :fastest then Zlib::BEST_SPEED
|
97
|
+
else raise(ArgumentError, "invalid compression level specified: #{opts[:level]}")
|
98
|
+
end
|
99
|
+
|
100
|
+
written = 0
|
101
|
+
File.open(path, 'wb') do |io|
|
102
|
+
|
103
|
+
case compression
|
104
|
+
when :none then written = write(io, compound_tag)
|
105
|
+
when :gzip
|
106
|
+
gzip = Zlib::GzipWriter.new(io, level)
|
107
|
+
#noinspection RubyMismatchedParameterType
|
108
|
+
written = write(gzip, compound_tag)
|
109
|
+
gzip.finish
|
110
|
+
when :zlib
|
111
|
+
buffer = StringIO.new
|
112
|
+
#noinspection RubyMismatchedParameterType
|
113
|
+
write(buffer, compound_tag)
|
114
|
+
compressed = Zlib::Deflate.deflate(buffer.string, level)
|
115
|
+
written = io.write(compressed)
|
116
|
+
else
|
117
|
+
raise(ArgumentError, "invalid compression specified: #{compression}")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
written
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
# Deserializes a {Tag} instance from the specified IO-like object.
|
126
|
+
# @param io [IO,#read] A IO-like object that responds to `#read`.
|
127
|
+
#
|
128
|
+
# @return [Tag] The deserialized tag object.
|
129
|
+
def self.read(io)
|
130
|
+
unless io.is_a?(IO) || io.respond_to?(:read)
|
131
|
+
raise(ArgumentError, "object must be an IO instance or respond to #read")
|
132
|
+
end
|
133
|
+
type = io.readbyte
|
134
|
+
read_type(io, type, read_string(io))
|
135
|
+
end
|
136
|
+
|
137
|
+
##
|
138
|
+
# Reads and deserializes a {Tag} from a file stored at the specified `path`.
|
139
|
+
# @param path [String] The path to a file to read from.
|
140
|
+
#
|
141
|
+
# @note Compression formats supported by the specification (GZip, ZLib) will be detected and handled automatically.
|
142
|
+
#
|
143
|
+
# @return [Tag] The deserialized tag object.
|
144
|
+
def self.read_file(path)
|
145
|
+
|
146
|
+
File.open(path, 'rb') do |io|
|
147
|
+
byte = io.readbyte
|
148
|
+
io.seek(0, IO::SEEK_SET)
|
149
|
+
|
150
|
+
stream = case byte
|
151
|
+
when 0x78 then StringIO.new(Zlib::Inflate.inflate(io.read))
|
152
|
+
when 0x1F then Zlib::GzipReader.new(io)
|
153
|
+
when 0x0A then io
|
154
|
+
else raise(ParseError, 'invalid NBT format')
|
155
|
+
end
|
156
|
+
read(stream)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
|
162
|
+
def self.write_tag(io, tag, list_child = false)
|
163
|
+
|
164
|
+
written = 0
|
165
|
+
unless list_child
|
166
|
+
written += io.write([tag.type].pack('C'))
|
167
|
+
written += write_string(io, tag.name)
|
168
|
+
written
|
169
|
+
end
|
170
|
+
|
171
|
+
written += case tag.type
|
172
|
+
when Tag::TYPE_END then io.write("\0")
|
173
|
+
when Tag::TYPE_BYTE then io.write([tag.value].pack('c'))
|
174
|
+
when Tag::TYPE_SHORT then io.write([tag.value].pack('s>'))
|
175
|
+
when Tag::TYPE_INT then io.write([tag.value].pack('l>'))
|
176
|
+
when Tag::TYPE_LONG then io.write([tag.value].pack('q>'))
|
177
|
+
when Tag::TYPE_FLOAT then io.write([tag.value].pack('g'))
|
178
|
+
when Tag::TYPE_DOUBLE then io.write([tag.value].pack('G'))
|
179
|
+
when Tag::TYPE_BYTE_ARRAY
|
180
|
+
written += io.write([tag.size].pack('l>'))
|
181
|
+
io.write(tag.to_a.pack('c*'))
|
182
|
+
when Tag::TYPE_STRING then write_string(io, tag.value)
|
183
|
+
when Tag::TYPE_LIST
|
184
|
+
written += io.write([tag.child_type].pack('C'))
|
185
|
+
written += io.write([tag.size].pack('l>'))
|
186
|
+
tag.map { |child| write_tag(io, child, true) }.sum
|
187
|
+
when Tag::TYPE_COMPOUND
|
188
|
+
tag.each { |child| written += write_tag(io, child, false) }
|
189
|
+
io.write("\0")
|
190
|
+
when Tag::TYPE_INT_ARRAY
|
191
|
+
written += io.write([tag.size].pack('l>'))
|
192
|
+
io.write(tag.to_a.pack('l>*'))
|
193
|
+
when Tag::TYPE_LONG_ARRAY
|
194
|
+
written += io.write([tag.size].pack('l>'))
|
195
|
+
io.write(tag.to_a.pack('q>*'))
|
196
|
+
else
|
197
|
+
raise(RuntimeError, sprintf("invalid type specifier: 0x%X2", tag.type))
|
198
|
+
end
|
199
|
+
|
200
|
+
written
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.write_string(io, str)
|
204
|
+
count = 0
|
205
|
+
if str
|
206
|
+
if str.encoding != ENCODING
|
207
|
+
str = str.encode(ENCODING)
|
208
|
+
warn("invalid UTF-8 characters in string") unless str.valid_encoding?
|
209
|
+
end
|
210
|
+
count += io.write([str.bytesize].pack('S>'))
|
211
|
+
count += io.write(str)
|
212
|
+
else
|
213
|
+
count += io.write([0].pack('S>'))
|
214
|
+
end
|
215
|
+
count
|
216
|
+
end
|
217
|
+
|
218
|
+
##
|
219
|
+
# @param io [IO]
|
220
|
+
# @param type [Integer]
|
221
|
+
# @param name [String,NilClass]
|
222
|
+
def self.read_type(io, type, name)
|
223
|
+
case type
|
224
|
+
when Tag::TYPE_BYTE then read_value_tag(io, ByteTag, name, 'c', 1)
|
225
|
+
when Tag::TYPE_SHORT then read_value_tag(io, ShortTag, name, 's>', 2)
|
226
|
+
when Tag::TYPE_INT then read_value_tag(io, IntTag, name, 'l>', 4)
|
227
|
+
when Tag::TYPE_LONG then read_value_tag(io, LongTag, name, 'q>', 8)
|
228
|
+
when Tag::TYPE_FLOAT then read_value_tag(io, FloatTag, name, 'g', 4)
|
229
|
+
when Tag::TYPE_DOUBLE then read_value_tag(io, DoubleTag, name, 'G', 8)
|
230
|
+
when Tag::TYPE_BYTE_ARRAY then read_array_tag(io, ByteArrayTag, name, 'c*', 1)
|
231
|
+
when Tag::TYPE_STRING then StringTag.new(name, read_string(io))
|
232
|
+
when Tag::TYPE_LIST then read_list_tag(io, name)
|
233
|
+
when Tag::TYPE_COMPOUND then read_compound_tag(io, name)
|
234
|
+
when Tag::TYPE_INT_ARRAY then read_array_tag(io, IntArrayTag, name, 'l>*', 4)
|
235
|
+
when Tag::TYPE_LONG_ARRAY then read_array_tag(io, LongArrayTag, name, 'q>*', 8)
|
236
|
+
else raise(ParseError, 'invalid type specifier, likely due to incorrect stream position')
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
##
|
241
|
+
# @param io [IO]
|
242
|
+
# @param klass [Class]
|
243
|
+
# @param name [String,NilClass]
|
244
|
+
# @param unpack [String]
|
245
|
+
# @param size [Integer]
|
246
|
+
def self.read_value_tag(io, klass, name, unpack, size)
|
247
|
+
#noinspection RubyNilAnalysis
|
248
|
+
value = io.read(size).unpack1(unpack)
|
249
|
+
#noinspection RubyArgCount
|
250
|
+
klass.new(name, value)
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# @param io [IO]
|
255
|
+
# @param klass [Class]
|
256
|
+
# @param name [String,NilClass]
|
257
|
+
# @param unpack [String]
|
258
|
+
# @param size [Integer]
|
259
|
+
def self.read_array_tag(io, klass, name, unpack, size)
|
260
|
+
#noinspection RubyNilAnalysis
|
261
|
+
count = io.read(4).unpack1('l>')
|
262
|
+
#noinspection RubyNilAnalysis
|
263
|
+
values = io.read(count * size).unpack(unpack)
|
264
|
+
tag = klass.new(name)
|
265
|
+
tag.instance_variable_set(:@values, values)
|
266
|
+
tag
|
267
|
+
end
|
268
|
+
|
269
|
+
##
|
270
|
+
# @return [String]
|
271
|
+
def self.read_string(io)
|
272
|
+
length = io.read(2).unpack1('S>')
|
273
|
+
#noinspection RubyResolve
|
274
|
+
length.zero? ? '' : io.read(length).force_encoding(ENCODING)
|
275
|
+
end
|
276
|
+
|
277
|
+
def self.read_list_tag(io, name)
|
278
|
+
child_type = io.readbyte
|
279
|
+
count = io.read(4).unpack1('l>')
|
280
|
+
list = ListTag.new(name, child_type)
|
281
|
+
values = (0...count).map { read_type(io, child_type, nil) }
|
282
|
+
list.instance_variable_set(:@values, values)
|
283
|
+
list
|
284
|
+
end
|
285
|
+
|
286
|
+
def self.read_compound_tag(io, name)
|
287
|
+
compound = CompoundTag.new(name)
|
288
|
+
loop do
|
289
|
+
type = io.readbyte
|
290
|
+
break if type == Tag::TYPE_END
|
291
|
+
child_name = read_string(io)
|
292
|
+
compound.push(read_type(io, type, child_name))
|
293
|
+
end
|
294
|
+
compound
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: craftbook-nbt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ForeverZer0
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-08-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '13.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '13.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rexical
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.0'
|
41
|
+
description: A feature-rich and complete Ruby implementation of the Named Binary Tag
|
42
|
+
(NBT) format. While it is an integral part of the broader CraftBook API, it is an
|
43
|
+
independent module with no dependencies, and can be used for any purpose where reading/writing/converting
|
44
|
+
the NBT format is required.
|
45
|
+
email:
|
46
|
+
- efreed09@gmail.com
|
47
|
+
executables: []
|
48
|
+
extensions: []
|
49
|
+
extra_rdoc_files: []
|
50
|
+
files:
|
51
|
+
- ".gitignore"
|
52
|
+
- ".yardopts"
|
53
|
+
- CHANGELOG.md
|
54
|
+
- CODE_OF_CONDUCT.md
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- bin/console
|
60
|
+
- bin/setup
|
61
|
+
- craftbook-nbt.gemspec
|
62
|
+
- lib/craftbook/nbt.rb
|
63
|
+
- lib/craftbook/nbt/byte_array_tag.rb
|
64
|
+
- lib/craftbook/nbt/byte_tag.rb
|
65
|
+
- lib/craftbook/nbt/compound_tag.rb
|
66
|
+
- lib/craftbook/nbt/container_tag.rb
|
67
|
+
- lib/craftbook/nbt/double_tag.rb
|
68
|
+
- lib/craftbook/nbt/enumerable_tag.rb
|
69
|
+
- lib/craftbook/nbt/float_tag.rb
|
70
|
+
- lib/craftbook/nbt/int_array_tag.rb
|
71
|
+
- lib/craftbook/nbt/int_tag.rb
|
72
|
+
- lib/craftbook/nbt/list_tag.rb
|
73
|
+
- lib/craftbook/nbt/long_array_tag.rb
|
74
|
+
- lib/craftbook/nbt/long_tag.rb
|
75
|
+
- lib/craftbook/nbt/short_tag.rb
|
76
|
+
- lib/craftbook/nbt/snbt.rb
|
77
|
+
- lib/craftbook/nbt/snbt/lexer.rb
|
78
|
+
- lib/craftbook/nbt/snbt/snbt.rb
|
79
|
+
- lib/craftbook/nbt/snbt/snbt.rex
|
80
|
+
- lib/craftbook/nbt/string_tag.rb
|
81
|
+
- lib/craftbook/nbt/tag.rb
|
82
|
+
- lib/craftbook/nbt/tag_builder.rb
|
83
|
+
- lib/craftbook/nbt/value_tag.rb
|
84
|
+
- lib/craftbook/nbt/version.rb
|
85
|
+
homepage: https://github.com/ForeverZer0/craftbook-nbt
|
86
|
+
licenses:
|
87
|
+
- MIT
|
88
|
+
metadata:
|
89
|
+
homepage_uri: https://github.com/ForeverZer0/craftbook-nbt
|
90
|
+
source_code_uri: https://github.com/ForeverZer0/craftbook-nbt
|
91
|
+
changelog_uri: https://github.com/ForeverZer0/craftbook-nbt/CHANGELOG.md
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: 2.4.0
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubygems_version: 3.2.21
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: A feature-rich and complete Ruby implementation of the Named Binary Tag (NBT)
|
111
|
+
format and SNBT parser.
|
112
|
+
test_files: []
|