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 +4 -4
- data/README.md +14 -5
- data/bin/rbstarbound +2 -9
- data/lib/rbstarbound/commands/dump.rb +35 -0
- data/lib/rbstarbound/commands/serialize.rb +39 -0
- data/lib/rbstarbound/exit_codes.rb +4 -0
- data/lib/rbstarbound/main_command.rb +22 -0
- data/lib/rbstarbound/player.rb +26 -0
- data/lib/rbstarbound/sbon.rb +133 -10
- data/lib/rbstarbound/sbvj01.rb +26 -18
- data/lib/rbstarbound/utils.rb +7 -0
- data/lib/rbstarbound/version.rb +1 -1
- data/lib/rbstarbound.rb +19 -5
- data/test/commands/dump_test.rb +19 -0
- data/test/commands/serialize_test.rb +19 -0
- data/test/data/1.3.3/test.yaml +3956 -0
- data/test/player_test.rb +17 -0
- data/test/rbstarbound_test.rb +0 -5
- data/test/sbvj01_test.rb +7 -3
- data/test/test_helper.rb +2 -0
- metadata +30 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d05f85c2aa5f0cfb19c28e8d45f9c378da343273
|
4
|
+
data.tar.gz: 2be0f43338decdda10062f93a5f0018e4db6f33e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
56
|
+
with installed as a gem you could use (from any folder):
|
49
57
|
|
50
|
-
|
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
|
-
|
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
|
-
|
5
|
-
|
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,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
|
data/lib/rbstarbound/sbon.rb
CHANGED
@@ -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
|
49
|
-
when 1 then
|
50
|
-
when 2 then io
|
51
|
-
when 3 then
|
52
|
-
when 4 then
|
53
|
-
when 5 then
|
54
|
-
when 6 then
|
55
|
-
when 7 then
|
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.
|
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 :
|
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
|
data/lib/rbstarbound/sbvj01.rb
CHANGED
@@ -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
|
-
|
11
|
+
read_player_data(io)
|
20
12
|
end
|
21
13
|
|
22
|
-
def self.
|
23
|
-
|
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.
|
19
|
+
def self.read_player_data(io)
|
27
20
|
name = RBStarbound::SBON.read_string(io)
|
28
|
-
version =
|
29
|
-
version = io.read(4).unpack('i>')
|
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 :
|
46
|
+
private :sbvj01_header?, :sbvj01_header, :read_player_data,
|
47
|
+
:write_player_data
|
40
48
|
end
|
41
49
|
end
|
42
50
|
end
|
data/lib/rbstarbound/version.rb
CHANGED
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
|
-
|
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
|