rbstarbound 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bc2319a0b69674dfcdccfd64791c37858fecc10f
4
- data.tar.gz: 7fe512695536fd226115bc2116786145865f71c2
3
+ metadata.gz: d05f85c2aa5f0cfb19c28e8d45f9c378da343273
4
+ data.tar.gz: 2be0f43338decdda10062f93a5f0018e4db6f33e
5
5
  SHA512:
6
- metadata.gz: 8a1fe19f1faa35fdaabe9e0bcd0ff1c2ba8f6cd9adb9f5a298efe9c15ced2c9596c02bcf2057e3750cd6f26c4e7f51f74a9522b5f23650fe307f31d5211fb38e
7
- data.tar.gz: 86452ce8a5f13cd43bcdec1b217bcd7992a634226dfde90c8ae9dd405e39e618b84286da2902f4f5a90d81198c5161306abb6245e94e7eeae42d42d5eabee223
6
+ metadata.gz: 9cc42be3a1e207957bac7e4252572e0b1a764f10c40959b1e0a6275291e3123ed0b3ca6b225fb54f629a6ffd43cb3e0fa4513f08320a053d0d5e85d05550e1dc
7
+ data.tar.gz: 181f76077841725a6f27e916b7b7c8f4892bd4b7dfab3ae6cf2080ce54052930cd6294d20e97007d45e3cec60a51ce9fe20ae5e0e0c7b51b6ed2a9d69335a0a6
data/README.md CHANGED
@@ -9,7 +9,10 @@ Frankly, it's just a ruby version of
9
9
  All thanks to
10
10
  [blixt](https://github.com/blixt).
11
11
 
12
- --------------------------------------------------------------------------------
12
+ Also, see
13
+ [changelog
14
+ ](https://github.com/JetPirate/rb-starbound/blob/master/changelog.yaml)
15
+ to know what's new.
13
16
  ## Installation
14
17
 
15
18
  #### I. option
@@ -43,11 +46,16 @@ You just
43
46
 
44
47
  To get YAML file with all parsed player data:
45
48
 
46
- **Attention: will be changed!**
49
+ $ ./bin/rbstarbound dump -f yaml -i path/to/save/file -o path/to/output/file
50
+
51
+ For more see:
52
+
53
+ $ ./bin/rbstarbound --help
54
+
47
55
 
48
- $ ./bin/rbstarbound path/to/save/file path/to/output/file
56
+ with installed as a gem you could use (from any folder):
49
57
 
50
- ... will be added ...
58
+ $ rbstarbound --help
51
59
 
52
60
  ### with the gem/library
53
61
 
@@ -66,7 +74,8 @@ puts player['data']['identity']['name']
66
74
 
67
75
  ```
68
76
 
69
- ... will be added ...
77
+ For more see:
78
+ https://github.com/JetPirate/rb-starbound/blob/master/examples
70
79
 
71
80
  --------------------------------------------------------------------------------
72
81
 
data/bin/rbstarbound CHANGED
@@ -1,12 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'rbstarbound'
4
- require 'yaml'
5
- unless ARGV.count == 2
6
- puts 'You need to input file paths'
7
- return
8
- end
9
- player = RBStarbound.parse_player_save_file(ARGV[0].to_s)
10
- File.open(ARGV[1].to_s, 'w') do |file|
11
- file.write(player['data'].to_yaml)
12
- end
4
+
5
+ exit RBStarbound::MainCommand.run || RBStarbound::EX_OK
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBStarbound
4
+ class DumpCommand < Clamp::Command
5
+ option ['-f', '--format'], 'FORMAT', 'file\'s output format; one of: yaml',
6
+ required: true
7
+ option ['-i', '--save-file'], 'FILE',
8
+ 'file to fetch player data from',
9
+ required: true
10
+ option ['-o', '--formatted-file'], 'FILE', 'file to save player data to',
11
+ required: true
12
+
13
+ def execute
14
+ return unless format.casecmp('yaml').zero?
15
+ dump_to_yaml(save_file.to_s, formatted_file.to_s)
16
+ end
17
+
18
+ private
19
+
20
+ def dump_to_yaml(input, output)
21
+ player_data = RBStarbound.parse_player_save_file(input.to_s)
22
+ return RBStarbound::EX_ERR if player_data.nil?
23
+ begin
24
+ formatted_file = File.open(output, 'w')
25
+ formatted_file.write(player_data['data'].to_yaml)
26
+ return RBStarbound::EX_OK
27
+ rescue StandardError => e
28
+ RBStarbound.print_error(e)
29
+ return RBStarbound::EX_ERR
30
+ ensure
31
+ formatted_file.close unless formatted_file.nil?
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module RBStarbound
6
+ class SerializeCommand < Clamp::Command
7
+ option ['-f', '--format'], 'FORMAT', 'file\'s input format; one of: yaml',
8
+ required: true
9
+ option ['-i', '--formatted-file'], 'FILE',
10
+ 'file to fetch player data from',
11
+ required: true
12
+ option ['-o', '--save-file'], 'FILE', 'file to save player data to',
13
+ required: true
14
+
15
+ def execute
16
+ return unless format.casecmp('yaml').zero?
17
+ serialize_from_yaml(formatted_file.to_s, save_file.to_s)
18
+ end
19
+
20
+ private
21
+
22
+ def serialize_from_yaml(input, output)
23
+ formatted_file = File.open(input)
24
+ fetched_data = YAML.load(formatted_file)
25
+ player_data = RBStarbound::Player::Data.new(
26
+ RBStarbound::Player::DATA_NAME,
27
+ RBStarbound::Player::DATA_VERSION,
28
+ fetched_data
29
+ )
30
+ dumped = RBStarbound.dump_player_save_file(output, player_data)
31
+ return dumped ? RBStarbound::EX_OK : RBStarbound::EX_ERR
32
+ rescue StandardError => e
33
+ RBStarbound.print_error(e)
34
+ return RBStarbound::EX_ERR
35
+ ensure
36
+ formatted_file.close unless formatted_file.nil?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+ module RBStarbound
2
+ EX_OK = 0
3
+ EX_ERR = 1
4
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'clamp'
4
+ require 'rbstarbound/exit_codes'
5
+ require 'rbstarbound/commands/dump'
6
+ require 'rbstarbound/commands/serialize'
7
+
8
+ module RBStarbound
9
+ class MainCommand < Clamp::Command
10
+ option '--version', :flag, 'show version' do
11
+ puts RBStarbound::VERSION
12
+ exit RBStarbound::EX_OK
13
+ end
14
+
15
+ subcommand 'dump',
16
+ 'dump a player save file as a formatted file',
17
+ RBStarbound::DumpCommand
18
+ subcommand 'serialize',
19
+ 'serialize a player save file form a formatted file',
20
+ RBStarbound::SerializeCommand
21
+ end
22
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBStarbound
4
+ module Player
5
+ DATA_NAME = 'PlayerEntity'
6
+ DATA_VERSION = 30
7
+ Data = Struct.new(:name, :version, :data) do
8
+ def player_name; data['identity']['name']; end
9
+ def player_name=(name); data['identity']['name'] = name; end
10
+ def gender; data['identity']['gender']; end
11
+ def gender=(value); data['identity']['gender'] = value; end
12
+ def hair_type; data['identity']['hairType']; end
13
+ def hair_type=(type); data['identity']['hairType'] = type; end
14
+ def personality; data['identity']['personalityIdle']; end
15
+ def personality=(value); data['identity']['personalityIdle'] = value; end
16
+ def death_count; data['identity']['deathCount']; end
17
+ def death_count=(count); data['identity']['deathCount'] = count; end
18
+ def play_time; data['identity']['playTime']; end
19
+ def uuid; data['identity']['uuid']; end
20
+ def intro_complete?; data['identity']['introComplete']; end
21
+ def intro_complete=(bool); data['identity']['introComplete'] = bool; end
22
+ def ship_level; data['identity']['shipLevel']; end
23
+ def ship_level=(level); data['identity']['shipLevel'] = level; end
24
+ end
25
+ end
26
+ end
@@ -11,6 +11,16 @@ module RBStarbound
11
11
  end
12
12
  end
13
13
 
14
+ def self.write_varint(io, value)
15
+ buf = (value & 0b01_11_11_11).chr
16
+ value >>= 7
17
+ until value.zero?
18
+ buf = (value & 0b01_11_11_11 | 0b10_00_00_00).chr + buf
19
+ value >>= 7
20
+ end
21
+ io.write(buf)
22
+ end
23
+
14
24
  def self.read_varint_signed(io)
15
25
  value = read_varint(io)
16
26
  # Least significant bit represents the sign.
@@ -18,15 +28,32 @@ module RBStarbound
18
28
  value >> 1
19
29
  end
20
30
 
31
+ def self.write_varint_signed(io, value)
32
+ if value < 0
33
+ write_varint(io, (-(value + 1) << 1 | 1))
34
+ else
35
+ write_varint(io, value << 1)
36
+ end
37
+ end
38
+
21
39
  def self.read_bytes(io)
22
40
  length = read_varint(io)
23
41
  io.read(length)
24
42
  end
25
43
 
44
+ def self.write_bytes(io, data)
45
+ write_varint(io, data.length)
46
+ io.write(data)
47
+ end
48
+
26
49
  def self.read_string(io)
27
50
  read_bytes(io)
28
51
  end
29
52
 
53
+ def self.write_string(io, data)
54
+ write_bytes(io, data)
55
+ end
56
+
30
57
  def self.read_list(io)
31
58
  list = []
32
59
  read_varint(io).times do
@@ -35,6 +62,13 @@ module RBStarbound
35
62
  list
36
63
  end
37
64
 
65
+ def self.write_list(io, list)
66
+ write_varint(io, list.length)
67
+ list.each do |item|
68
+ write_dynamic(io, item)
69
+ end
70
+ end
71
+
38
72
  def self.read_map(io)
39
73
  map = {}
40
74
  read_varint(io).times do
@@ -44,25 +78,114 @@ module RBStarbound
44
78
  map
45
79
  end
46
80
 
81
+ def self.write_map(io, map)
82
+ write_varint(io, map.length)
83
+ map.each_pair do |key, val|
84
+ write_string(io, key)
85
+ write_dynamic(io, val)
86
+ end
87
+ end
88
+
47
89
  def self.read_dynamic(io)
48
- case read_ord(io)
49
- when 1 then nil # nil value
50
- when 2 then io.read(8).unpack('G')[0] # double; big-endian
51
- when 3 then !read_ord(io).zero? # boolean value
52
- when 4 then read_varint_signed(io) # VLQ; signed
53
- when 5 then read_string(io) # string value
54
- when 6 then read_list(io) # list value (array)
55
- when 7 then read_map(io) # map value (hash)
90
+ case read_data_type(io)
91
+ when 1 then read_nil(io)
92
+ when 2 then read_double(io) # big-endian
93
+ when 3 then read_bool(io)
94
+ when 4 then read_integer(io) # VLQ; signed
95
+ when 5 then read_str(io)
96
+ when 6 then read_array(io)
97
+ when 7 then read_hash(io)
56
98
  else raise RBStarbound::ValueError, 'Unknown dynamic type'
57
99
  end
58
100
  end
59
101
 
60
- def self.read_ord(io)
102
+ def self.write_dynamic(io, data)
103
+ case data
104
+ when nil then write_nil(io)
105
+ when Float then write_double(io, data)
106
+ when TrueClass, FalseClass then write_bool(io, data)
107
+ when Integer then write_integer(io, data)
108
+ when String then write_str(io, data)
109
+ when Array then write_array(io, data)
110
+ when Hash then write_hash(io, data)
111
+ else raise ValueError, 'Cannot write value'
112
+ end
113
+ end
114
+
115
+ def self.read_data_type(io)
61
116
  io.readchar.ord
62
117
  end
63
118
 
119
+ def self.read_bool(io)
120
+ !read_data_type(io).zero?
121
+ end
122
+
123
+ def self.write_bool(io, value)
124
+ if value.is_a? TrueClass
125
+ io.write("\x03\x01".b)
126
+ else
127
+ io.write("\x03\x00".b)
128
+ end
129
+ end
130
+
131
+ def self.read_double(io)
132
+ io.read(8).unpack('G').first
133
+ end
134
+
135
+ def self.write_double(io, value)
136
+ io.write("\x02".b)
137
+ io.write([value].pack('G'))
138
+ end
139
+
140
+ def self.read_integer(io)
141
+ read_varint_signed(io)
142
+ end
143
+
144
+ def self.write_integer(io, data)
145
+ io.write("\x04".b)
146
+ write_varint_signed(io, data)
147
+ end
148
+
149
+ def self.read_str(io)
150
+ read_string(io)
151
+ end
152
+
153
+ def self.write_str(io, data)
154
+ io.write("\x05".b)
155
+ write_string(io, data)
156
+ end
157
+
158
+ def self.read_array(io)
159
+ read_list(io)
160
+ end
161
+
162
+ def self.write_array(io, data)
163
+ io.write("\x06".b)
164
+ write_list(io, data)
165
+ end
166
+
167
+ def self.read_hash(io)
168
+ read_map(io)
169
+ end
170
+
171
+ def self.write_hash(io, data)
172
+ io.write("\x07".b)
173
+ write_map(io, data)
174
+ end
175
+
176
+ def self.read_nil(_)
177
+ nil
178
+ end
179
+
180
+ def self.write_nil(io)
181
+ io.write("\x01".b)
182
+ end
183
+
64
184
  class << self
65
- private :read_ord
185
+ private :read_data_type, :read_nil, :read_bool, :read_double,
186
+ :read_integer, :read_str, :read_array, :read_hash, :write_nil,
187
+ :write_bool, :write_double, :write_integer, :write_str,
188
+ :write_array, :write_hash
66
189
  end
67
190
  end
68
191
  end
@@ -1,42 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'rbstarbound/player'
4
+
3
5
  module RBStarbound
4
6
  module SBVJ01
5
- Player = Struct.new(:name, :version, :data) do
6
- def player_name
7
- data['identity']['name']
8
- end
9
-
10
- def uuid
11
- data['identity']['uuid']
12
- end
13
- end
14
-
15
7
  def self.parse(io)
16
8
  unless sbvj01_header?(io)
17
9
  raise RBStarbound::SBVJ01Error, 'Not a SBVJ01 file'
18
10
  end
19
- get_player_data(io)
11
+ read_player_data(io)
20
12
  end
21
13
 
22
- def self.write(io)
23
- raise RBStarbound::NotImplementedError
14
+ def self.dump(io, player_data)
15
+ sbvj01_header(io)
16
+ write_player_data(io, player_data)
24
17
  end
25
18
 
26
- def self.get_player_data(io)
19
+ def self.read_player_data(io)
27
20
  name = RBStarbound::SBON.read_string(io)
28
- version = 'None'
29
- version = io.read(4).unpack('i>') if io.readchar.ord != 0
21
+ version = nil
22
+ version = io.read(4).unpack('i>').first unless io.readchar.ord.zero?
30
23
  data = RBStarbound::SBON.read_dynamic(io)
31
- Player.new(name, version, data)
24
+ RBStarbound::Player::Data.new(name, version, data)
25
+ end
26
+
27
+ def self.write_player_data(io, player_data)
28
+ RBStarbound::SBON.write_string(io, player_data.name)
29
+ if player_data.version.nil?
30
+ io.write([0].pack('c'))
31
+ else
32
+ io.write([1, player_data.version].pack('ci>'))
33
+ end
34
+ RBStarbound::SBON.write_dynamic(io, player_data.data)
32
35
  end
33
36
 
34
37
  def self.sbvj01_header?(io)
35
38
  io.read(6) == 'SBVJ01'
36
39
  end
37
40
 
41
+ def self.sbvj01_header(io)
42
+ io.write('SBVJ01')
43
+ end
44
+
38
45
  class << self
39
- private :write, :get_player_data, :sbvj01_header?
46
+ private :sbvj01_header?, :sbvj01_header, :read_player_data,
47
+ :write_player_data
40
48
  end
41
49
  end
42
50
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RBStarbound
4
+ def self.print_error(error)
5
+ puts 'An error occured: ' + error.message.split(' @ ').first
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RBStarbound
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/rbstarbound.rb CHANGED
@@ -1,21 +1,35 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rbstarbound/version'
2
4
  require 'rbstarbound/exceptions'
5
+ require 'rbstarbound/utils'
3
6
  require 'rbstarbound/sbvj01'
4
7
  require 'rbstarbound/sbon'
8
+ require 'rbstarbound/exit_codes'
9
+ require 'rbstarbound/main_command'
5
10
 
6
11
  module RBStarbound
7
- def self.ping
8
- puts 'Pong!'
9
- end
10
-
11
12
  def self.parse_player_save_file(path)
12
13
  save_file = File.open(path, 'rb')
13
14
  parsed_data = SBVJ01.parse(save_file)
14
15
  rescue StandardError => e
15
- puts 'An error occured: ' + e.message.split(' @ ').first
16
+ print_error(e)
17
+ return nil
16
18
  else
17
19
  return parsed_data
18
20
  ensure
19
21
  save_file.close unless save_file.nil?
20
22
  end
23
+
24
+ def self.dump_player_save_file(path, player_data)
25
+ save_file = File.open(path, 'w')
26
+ SBVJ01.dump(save_file, player_data)
27
+ rescue StandardError => e
28
+ print_error(e)
29
+ return false
30
+ else
31
+ return true
32
+ ensure
33
+ save_file.close unless save_file.nil?
34
+ end
21
35
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join(File.dirname(__FILE__), '../test_helper')
4
+
5
+ class DumpCommandTest < Minitest::Test
6
+ def test_it_dumps_to_yaml
7
+ cmd = RBStarbound::MainCommand.new('', {})
8
+ file = Tempfile.new('dump_cmd_test_temp')
9
+ params = %w[dump -f yaml -i] << SAVE_FILE_PATH << '-o' << file.path
10
+ begin
11
+ cmd.run(params)
12
+ data = file.gets(DATA_COUNT)
13
+ refute_empty(data)
14
+ ensure
15
+ file.close
16
+ file.unlink
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.join(File.dirname(__FILE__), '../test_helper')
4
+
5
+ class SerializeCommandTest < Minitest::Test
6
+ def test_it_serializes_from_yaml
7
+ cmd = RBStarbound::MainCommand.new('', {})
8
+ file = Tempfile.new('serialize_cmd_test_temp')
9
+ params = %w[serialize -f yaml -i] << YAML_FILE_PATH << '-o' << file.path
10
+ begin
11
+ cmd.run(params)
12
+ data = file.gets(DATA_COUNT)
13
+ refute_empty(data)
14
+ ensure
15
+ file.close
16
+ file.unlink
17
+ end
18
+ end
19
+ end