nbt_utils 0.0.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +6 -0
  3. data/.github/workflows/ci.yml +24 -0
  4. data/.gitignore +2 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +37 -0
  7. data/.ruby-version +1 -0
  8. data/Gemfile +2 -0
  9. data/Gemfile.lock +49 -4
  10. data/LICENSE +7 -0
  11. data/README.md +16 -18
  12. data/Rakefile +8 -0
  13. data/doc/bigtest_uncompressed.nbt +0 -0
  14. data/doc/test_uncompressed.nbt +0 -0
  15. data/lib/nbt_utils/file.rb +25 -8
  16. data/lib/nbt_utils/tag/byte.rb +4 -1
  17. data/lib/nbt_utils/tag/byte_array.rb +9 -7
  18. data/lib/nbt_utils/tag/compound.rb +22 -17
  19. data/lib/nbt_utils/tag/double.rb +4 -1
  20. data/lib/nbt_utils/tag/end.rb +6 -4
  21. data/lib/nbt_utils/tag/exceptions.rb +3 -1
  22. data/lib/nbt_utils/tag/float.rb +4 -1
  23. data/lib/nbt_utils/tag/int.rb +4 -1
  24. data/lib/nbt_utils/tag/int_array.rb +37 -0
  25. data/lib/nbt_utils/tag/list.rb +20 -15
  26. data/lib/nbt_utils/tag/long.rb +4 -1
  27. data/lib/nbt_utils/tag/short.rb +4 -1
  28. data/lib/nbt_utils/tag/string.rb +2 -0
  29. data/lib/nbt_utils/tag.rb +13 -17
  30. data/lib/nbt_utils/tag_name.rb +5 -3
  31. data/lib/nbt_utils/version.rb +3 -1
  32. data/lib/nbt_utils.rb +3 -0
  33. data/nbt_utils.gemspec +17 -11
  34. data/script.rb +21 -19
  35. data/spec/nbt_utils/file_spec.rb +60 -0
  36. data/spec/nbt_utils/tag/byte_array_spec.rb +5 -0
  37. data/spec/nbt_utils/tag/byte_spec.rb +5 -0
  38. data/spec/nbt_utils/tag/compound_spec.rb +5 -0
  39. data/spec/nbt_utils/tag/double_spec.rb +5 -0
  40. data/spec/nbt_utils/tag/end_spec.rb +5 -0
  41. data/spec/nbt_utils/tag/float_spec.rb +5 -0
  42. data/spec/nbt_utils/tag/int_array_spec.rb +5 -0
  43. data/spec/nbt_utils/tag/int_spec.rb +5 -0
  44. data/spec/nbt_utils/tag/list_spec.rb +5 -0
  45. data/spec/nbt_utils/tag/long_spec.rb +5 -0
  46. data/spec/nbt_utils/tag/short_spec.rb +5 -0
  47. data/spec/nbt_utils/tag/string_spec.rb +5 -0
  48. data/spec/nbt_utils/tag_name_spec.rb +20 -0
  49. data/spec/spec_helper.rb +102 -0
  50. data/spec/support/shared_examples/a_tag.rb +8 -0
  51. metadata +116 -54
data/lib/nbt_utils/tag.rb CHANGED
@@ -1,36 +1,34 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NBTUtils
2
4
  module Tag
3
- attr_reader :name
4
- attr_reader :payload
5
+ attr_reader :name, :payload
5
6
 
6
7
  def self.included(base)
7
8
  base.extend ClassMethods
8
9
  end
9
10
 
10
- def initialize(io, named = true)
11
+ def initialize(io, named: true)
11
12
  read_name(io) if named
12
13
  @payload = self.class.payload_class.new.read(io)
13
14
  end
14
-
15
+
15
16
  def type_id
16
17
  self.class.type_id
17
18
  end
18
19
 
19
20
  def binary_type_id
20
- # I hope i'm doing this wrong.
21
- byte = ::BinData::Int8be.new
22
- byte.value = type_id
23
- byte.to_binary_s
21
+ type_id.chr
24
22
  end
25
23
 
26
24
  def to_s(indent = 0)
27
25
  klass = self.class.to_s.split('::').last
28
- (' ' * indent) + "TAG_#{klass}#{@name ? "(\"#{@name}\")" : ''}: #{@payload.value}"
26
+ (' ' * indent) + "TAG_#{klass}#{name ? "(\"#{name}\")" : ''}: #{payload.value}"
29
27
  end
30
28
 
31
- def to_nbt_string(named = true)
29
+ def to_nbt_string(named: true)
32
30
  result = named ? binary_type_id + name_to_nbt_string : ''
33
- result + @payload.to_binary_s
31
+ result + payload.to_binary_s
34
32
  end
35
33
 
36
34
  def read_name(io)
@@ -39,7 +37,7 @@ module NBTUtils
39
37
 
40
38
  def name_to_nbt_string
41
39
  nm = NBTUtils::TagName.new
42
- nm.data = @name
40
+ nm.data = name
43
41
  nm.to_binary_s
44
42
  end
45
43
 
@@ -47,8 +45,8 @@ module NBTUtils
47
45
  NBTUtils::Tag.tag_type_to_class(tag_type)
48
46
  end
49
47
 
50
- def set_value(new_value, index = nil)
51
- @payload.value = new_value
48
+ def set_value(new_value, _index = nil)
49
+ payload.value = new_value
52
50
  end
53
51
 
54
52
  module ClassMethods
@@ -71,11 +69,9 @@ module NBTUtils
71
69
  @tag_types[tag_type.to_i]
72
70
  end
73
71
 
74
- protected
75
-
76
72
  def self.add_tag_type(index, tag_type)
77
73
  @tag_types ||= []
78
74
  @tag_types[index] = tag_type
79
75
  end
80
76
  end
81
- end
77
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NBTUtils
2
4
  class TagName < ::BinData::Record
3
5
  # Spec says this is a TAG_Short, which is signed. Staying strict
4
6
  # though this may really be unsigned in practice.
5
- int16be :len, :value => Proc.new { data.length }
6
- string :data, :read_length => :len
7
+ int16be :len, value: proc { data.length }
8
+ string :data, read_length: :len
7
9
 
8
10
  # things break for some reason if you just call the string :value
9
11
  def value
@@ -14,4 +16,4 @@ module NBTUtils
14
16
  self.data = (other_value)
15
17
  end
16
18
  end
17
- end
19
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module NBTUtils
2
- VERSION = "0.0.2"
4
+ VERSION = '0.5.0'
3
5
  end
data/lib/nbt_utils.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'zlib'
2
4
  require 'bindata'
3
5
 
@@ -17,3 +19,4 @@ require File.expand_path('nbt_utils/tag/byte_array', File.dirname(__FILE__))
17
19
  require File.expand_path('nbt_utils/tag/string', File.dirname(__FILE__))
18
20
  require File.expand_path('nbt_utils/tag/list', File.dirname(__FILE__))
19
21
  require File.expand_path('nbt_utils/tag/compound', File.dirname(__FILE__))
22
+ require File.expand_path('nbt_utils/tag/int_array', File.dirname(__FILE__))
data/nbt_utils.gemspec CHANGED
@@ -1,24 +1,30 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path("../lib/nbt_utils/version", __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('lib/nbt_utils/version', __dir__)
3
4
 
4
5
  Gem::Specification.new do |s|
5
- s.name = "nbt_utils"
6
+ s.name = 'nbt_utils'
6
7
  s.version = NBTUtils::VERSION
7
8
  s.platform = Gem::Platform::RUBY
8
9
  s.authors = ['Michael Dungan']
9
10
  s.email = ['mpd@jesters-court.net']
10
- s.homepage = "http://rubygems.org/gems/nbt_utils"
11
- s.summary = "Set of classes to read and write Minecraft .nbt files"
12
- s.description = "Some classes to read and write Minecraft .nbt files. See http://www.minecraft.net/docs/NBT.txt for format description."
11
+ s.homepage = 'https://github.com/xxx/nbt_utils'
12
+ s.summary = 'Set of classes to read and write Minecraft .nbt files'
13
+ s.description = 'Some classes to read and write Minecraft .nbt files. '\
14
+ 'See NBT.txt in the /doc directory for format description.'
13
15
 
14
- s.required_rubygems_version = ">= 1.3.6"
16
+ s.required_rubygems_version = '>= 1.3.6'
15
17
 
16
- s.add_dependency "bindata", "~> 1.2"
17
- s.add_development_dependency "bundler", ">= 1.0.0"
18
+ s.add_dependency 'bindata', '~> 2'
19
+ s.add_development_dependency 'rspec', '~> 3'
20
+ s.add_development_dependency 'rubocop', '~> 1'
21
+ s.add_development_dependency 'rubocop-performance', '~> 1'
22
+ s.add_development_dependency 'rubocop-rspec', '~> 2'
18
23
 
19
- s.required_ruby_version = "~> 1.9"
24
+ s.required_ruby_version = '>= 2.7'
20
25
 
21
26
  s.files = `git ls-files`.split("\n")
22
- s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
27
+ s.executables = `git ls-files`.split("\n").filter_map { |f| f =~ %r{^bin/(.*)} ? Regexp.last_match(1) : nil }
23
28
  s.require_path = 'lib'
29
+ s.metadata['rubygems_mfa_required'] = 'true'
24
30
  end
data/script.rb CHANGED
@@ -1,4 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
1
3
  # encoding: UTF-8
4
+ # frozen_string_literal: true
2
5
 
3
6
  require 'bundler'
4
7
  Bundler.require :default
@@ -8,32 +11,31 @@ require 'zlib'
8
11
 
9
12
  @compound = nil
10
13
 
11
- #file = NBTUtils::File.new('doc/test.nbt')
12
- #@compound = file.read
14
+ # file = NBTUtils::File.new('doc/test.nbt')
15
+ # @compound = file.read
13
16
  #
14
- #puts @compound.to_s
17
+ # puts @compound.to_s
15
18
  #
16
- #@compound.update_tag('name', 'asdf')
17
- #puts @compound.to_s
19
+ # @compound.update_tag('name', 'asdf')
20
+ # puts @compound.to_s
18
21
  #
19
- #@compound.remove_tag('name')
20
- #puts @compound.to_s
21
-
22
- #puts @compound.to_nbt_string
23
- #NBTUtils::File.new.write('biglolwut.nbt', @compound)
22
+ # @compound.remove_tag('name')
23
+ # puts @compound.to_s
24
24
 
25
- #@compound = NBTUtils::File.new.read('doc/bigtest.nbt')
26
- #puts @compound.to_s
25
+ # puts @compound.to_nbt_string
26
+ # NBTUtils::File.new.write('biglolwut.nbt', @compound)
27
27
 
28
+ # @compound = NBTUtils::File.new.read('doc/bigtest.nbt')
29
+ # puts @compound.to_s
28
30
 
29
31
  @compound = NBTUtils::File.new.read('doc/test.mclevel')
30
32
  puts @compound.to_s
31
33
 
32
- #puts @compound.to_nbt_string
33
- #NBTUtils::File.new.write('biglolwut.nbt', @compound)
34
+ # puts @compound.to_nbt_string
35
+ # NBTUtils::File.new.write('biglolwut.nbt', @compound)
34
36
 
35
- #p @compound.find_tag(/Test/)
36
- #p @compound.find_tag('Test')
37
- #p @compound.find_tag('intTest')
38
- #p @compound.find_tags(/(?:byte|int)Test/)
39
- #p @compound.find_tags 'intasdf'
37
+ # p @compound.find_tag(/Test/)
38
+ # p @compound.find_tag('Test')
39
+ # p @compound.find_tag('intTest')
40
+ # p @compound.find_tags(/(?:byte|int)Test/)
41
+ # p @compound.find_tags 'intasdf'
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::File do
4
+ let(:file) { described_class.new(File.expand_path("../../../doc/#{fixture}", __FILE__)) }
5
+ let(:fixture) { 'test_uncompressed.nbt' }
6
+
7
+ describe '#read' do
8
+ subject(:call) { file.read }
9
+
10
+ shared_examples 'reads the file' do
11
+ it 'reads the file and returns the root tag', :aggregate_failures do
12
+ expect(call.name).to eq('hello world')
13
+ expect(call.payload.first.payload.value).to eq('Bananrama')
14
+ end
15
+ end
16
+
17
+ context 'when the file is not compressed' do
18
+ let(:fixture) { 'test_uncompressed.nbt' }
19
+
20
+ include_examples 'reads the file'
21
+ end
22
+
23
+ context 'when the file is compressed' do
24
+ let(:fixture) { 'test.nbt' }
25
+
26
+ include_examples 'reads the file'
27
+ end
28
+ end
29
+
30
+ describe '#write' do
31
+ let(:root) { file.read }
32
+
33
+ let(:out_path) { "#{__dir__}/write-test.nbt" }
34
+
35
+ shared_examples 'writes the file' do
36
+ it 'writes the file out' do
37
+ tag = root.payload.first
38
+
39
+ file.write(out_path, tag, compressed)
40
+
41
+ read_in = described_class.new(out_path).read
42
+ expect(read_in.payload.value).to eq 'Bananrama'
43
+ ensure
44
+ File.delete(out_path) if File.exist?(out_path)
45
+ end
46
+ end
47
+
48
+ context 'when the file is not compressed' do
49
+ let(:compressed) { false }
50
+
51
+ include_examples 'writes the file'
52
+ end
53
+
54
+ context 'when the file is compressed' do
55
+ let(:compressed) { true }
56
+
57
+ include_examples 'writes the file'
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::ByteArray do
4
+ it_behaves_like 'a tag', 7, nil
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Byte do
4
+ it_behaves_like 'a tag', 1, BinData::Int8be
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Compound do
4
+ it_behaves_like 'a tag', 10, nil
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Double do
4
+ it_behaves_like 'a tag', 6, BinData::DoubleBe
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::End do
4
+ it_behaves_like 'a tag', 0, nil
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Float do
4
+ it_behaves_like 'a tag', 5, BinData::FloatBe
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::IntArray do
4
+ it_behaves_like 'a tag', 11, nil
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Int do
4
+ it_behaves_like 'a tag', 3, BinData::Int32be
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::List do
4
+ it_behaves_like 'a tag', 9, nil
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Long do
4
+ it_behaves_like 'a tag', 4, BinData::Int64be
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::Short do
4
+ it_behaves_like 'a tag', 2, BinData::Int16be
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::Tag::String do
4
+ it_behaves_like 'a tag', 8, NBTUtils::TagName
5
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe NBTUtils::TagName do
4
+ subject(:tag_name) { described_class.new }
5
+
6
+ describe '#value' do
7
+ it 'returns the data value' do
8
+ expect(tag_name.value).to eq('')
9
+ tag_name.data = 'foo'
10
+ expect(tag_name.value).to eq('foo')
11
+ end
12
+ end
13
+
14
+ describe '#value=' do
15
+ it 'returns the data value' do
16
+ tag_name.value = 'marf'
17
+ expect(tag_name.data).to eq('marf')
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'nbt_utils'
4
+
5
+ require 'support/shared_examples/a_tag'
6
+
7
+ # This file was generated by the `rspec --init` command. Conventionally, all
8
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
9
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
10
+ # this file to always be loaded, without a need to explicitly require it in any
11
+ # files.
12
+ #
13
+ # Given that it is always loaded, you are encouraged to keep this file as
14
+ # light-weight as possible. Requiring heavyweight dependencies from this file
15
+ # will add to the boot time of your test suite on EVERY test run, even for an
16
+ # individual file that may not need all of that loaded. Instead, consider making
17
+ # a separate helper file that requires the additional dependencies and performs
18
+ # the additional setup, and require it from the spec files that actually need
19
+ # it.
20
+ #
21
+ # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
22
+ RSpec.configure do |config|
23
+ # rspec-expectations config goes here. You can use an alternate
24
+ # assertion/expectation library such as wrong or the stdlib/minitest
25
+ # assertions if you prefer.
26
+ config.expect_with :rspec do |expectations|
27
+ # This option will default to `true` in RSpec 4. It makes the `description`
28
+ # and `failure_message` of custom matchers include text for helper methods
29
+ # defined using `chain`, e.g.:
30
+ # be_bigger_than(2).and_smaller_than(4).description
31
+ # # => "be bigger than 2 and smaller than 4"
32
+ # ...rather than:
33
+ # # => "be bigger than 2"
34
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
35
+ end
36
+
37
+ # rspec-mocks config goes here. You can use an alternate test double
38
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
39
+ config.mock_with :rspec do |mocks|
40
+ # Prevents you from mocking or stubbing a method that does not exist on
41
+ # a real object. This is generally recommended, and will default to
42
+ # `true` in RSpec 4.
43
+ mocks.verify_partial_doubles = true
44
+ end
45
+
46
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
47
+ # have no way to turn it off -- the option exists only for backwards
48
+ # compatibility in RSpec 3). It causes shared context metadata to be
49
+ # inherited by the metadata hash of host groups and examples, rather than
50
+ # triggering implicit auto-inclusion in groups with matching metadata.
51
+ config.shared_context_metadata_behavior = :apply_to_host_groups
52
+
53
+ # The settings below are suggested to provide a good initial experience
54
+ # with RSpec, but feel free to customize to your heart's content.
55
+ # This allows you to limit a spec run to individual examples or groups
56
+ # you care about by tagging them with `:focus` metadata. When nothing
57
+ # is tagged with `:focus`, all examples get run. RSpec also provides
58
+ # aliases for `it`, `describe`, and `context` that include `:focus`
59
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
60
+ config.filter_run_when_matching :focus
61
+
62
+ # Allows RSpec to persist some state between runs in order to support
63
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
64
+ # you configure your source control system to ignore this file.
65
+ # config.example_status_persistence_file_path = "spec/examples.txt"
66
+
67
+ # Limits the available syntax to the non-monkey patched syntax that is
68
+ # recommended. For more details, see:
69
+ # https://relishapp.com/rspec/rspec-core/docs/configuration/zero-monkey-patching-mode
70
+ config.disable_monkey_patching!
71
+
72
+ # This setting enables warnings. It's recommended, but in some cases may
73
+ # be too noisy due to issues in dependencies.
74
+ config.warnings = true
75
+
76
+ # Many RSpec users commonly either run the entire suite or an individual
77
+ # file, and it's useful to allow more verbose output when running an
78
+ # individual spec file.
79
+ if config.files_to_run.one?
80
+ # Use the documentation formatter for detailed output,
81
+ # unless a formatter has already been configured
82
+ # (e.g. via a command-line flag).
83
+ config.default_formatter = 'doc'
84
+ end
85
+
86
+ # Print the 10 slowest examples and example groups at the
87
+ # end of the spec run, to help surface which specs are running
88
+ # particularly slow.
89
+ config.profile_examples = 10
90
+
91
+ # Run specs in random order to surface order dependencies. If you find an
92
+ # order dependency and want to debug it, you can fix the order by providing
93
+ # the seed, which is printed after each run.
94
+ # --seed 1234
95
+ config.order = :random
96
+
97
+ # Seed global randomization in this process using the `--seed` CLI option.
98
+ # Setting this allows you to use `--seed` to deterministically reproduce
99
+ # test failures related to randomization by passing the same `--seed` value
100
+ # as the one that triggered the failure.
101
+ Kernel.srand config.seed
102
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.shared_examples_for 'a tag' do |type_id, payload_class|
4
+ it 'has the type_id and payload_class', :aggregate_failures do
5
+ expect(described_class.type_id).to eq(type_id)
6
+ expect(described_class.payload_class).to eq(payload_class)
7
+ end
8
+ end