rbstarbound 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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