openrareplay 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +30 -0
- data/.travis.yml +5 -0
- data/Gemfile +24 -0
- data/LICENSE.md +660 -0
- data/README.md +49 -0
- data/Rakefile +30 -0
- data/exe/openra-sanitize +23 -0
- data/lib/openrareplay.rb +26 -0
- data/lib/openrareplay/binary.rb +63 -0
- data/lib/openrareplay/miniyaml.rb +80 -0
- data/lib/openrareplay/order.rb +24 -0
- data/lib/openrareplay/order/client.rb +72 -0
- data/lib/openrareplay/order/notanorder.rb +52 -0
- data/lib/openrareplay/order/order.rb +86 -0
- data/lib/openrareplay/order/server.rb +44 -0
- data/lib/openrareplay/packet.rb +23 -0
- data/lib/openrareplay/packet/metadata_packet.rb +67 -0
- data/lib/openrareplay/packet/packet.rb +102 -0
- data/lib/openrareplay/packet/packet_parser.rb +47 -0
- data/lib/openrareplay/sanitize.rb +23 -0
- data/lib/openrareplay/sanitize/cli.rb +160 -0
- data/lib/openrareplay/sanitize/packet_sanitizer.rb +125 -0
- data/lib/openrareplay/sanitize/replay_sanitizer.rb +51 -0
- data/lib/openrareplay/version.rb +23 -0
- data/openrareplay.gemspec +46 -0
- metadata +113 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative 'order'
|
22
|
+
|
23
|
+
module OpenRAReplay
|
24
|
+
class ServerOrder < Order
|
25
|
+
def self.construct(input)
|
26
|
+
# Credit to Stekke for putting me on the right
|
27
|
+
# direction for these orders
|
28
|
+
command_length = decode_uleb128_io input
|
29
|
+
command = input.read(command_length)
|
30
|
+
data_length = decode_uleb128_io input
|
31
|
+
data = input.read(data_length)
|
32
|
+
new(command: command, data: data)
|
33
|
+
end
|
34
|
+
|
35
|
+
def server_order?
|
36
|
+
true
|
37
|
+
end
|
38
|
+
|
39
|
+
def serialize
|
40
|
+
(Order::SERVER_COMMAND + encode_uleb128(command.length) +
|
41
|
+
command + encode_uleb128(data.length) + data)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative 'packet/packet'
|
22
|
+
require_relative 'packet/metadata_packet'
|
23
|
+
require_relative 'packet/packet_parser'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative 'packet'
|
22
|
+
|
23
|
+
module OpenRAReplay
|
24
|
+
class MetadataPacket < Packet
|
25
|
+
METADATA_HEADER = "\xFF\xFF\xFF\xFF".force_encoding('ASCII-8BIT').freeze
|
26
|
+
METADATA_VERSION = "\x01\x00\x00\x00".force_encoding('ASCII-8BIT').freeze
|
27
|
+
METADATA_FOOTER = "\xFE\xFF\xFF\xFF".force_encoding('ASCII-8BIT').freeze
|
28
|
+
|
29
|
+
def unknown?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
def metadata?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse_byte_array
|
40
|
+
header = byte_array[0..3]
|
41
|
+
raise "Metadata header is not a header: #{header.inspect}" unless header == METADATA_HEADER
|
42
|
+
version = byte_array[4..7]
|
43
|
+
raise "Metadata version doesn't match current version: #{version.inspect}" unless version == METADATA_VERSION
|
44
|
+
@length = decode_u32 byte_array[8..11]
|
45
|
+
@client_id = -1
|
46
|
+
@frame = -1
|
47
|
+
@data = byte_array[12..(11 + length)]
|
48
|
+
length2 = decode_u32 byte_array[(12 + length)..(15 + length)]
|
49
|
+
raise "Appended length isn't correct: #{length2} vs expected #{length + 4}" unless length2 == (length + 4)
|
50
|
+
footer = byte_array[(16 + length)..(19 + length)]
|
51
|
+
raise "Metadata footer is not a footer: #{footer.inspect}" unless footer == METADATA_FOOTER
|
52
|
+
@orders = []
|
53
|
+
end
|
54
|
+
|
55
|
+
def construct_byte_array(type)
|
56
|
+
raise "#{self.class.name} only supports :data construction!" unless type == :data
|
57
|
+
@orders = []
|
58
|
+
@client_id = -1
|
59
|
+
@frame = -1
|
60
|
+
@length = data.length
|
61
|
+
@byte_array = METADATA_HEADER + METADATA_VERSION +
|
62
|
+
(encode_u32 length) +
|
63
|
+
data.force_encoding('ASCII-8BIT') +
|
64
|
+
encode_u32(length + 4) + METADATA_FOOTER
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative '../binary'
|
22
|
+
require_relative '../order/order'
|
23
|
+
|
24
|
+
module OpenRAReplay
|
25
|
+
class Packet
|
26
|
+
include Binary
|
27
|
+
|
28
|
+
attr_reader :client_id, :length, :frame,
|
29
|
+
:data, :orders,
|
30
|
+
:byte_array
|
31
|
+
|
32
|
+
SERVER_LOBBY_PACKET = 0
|
33
|
+
|
34
|
+
def initialize(options = {})
|
35
|
+
if options[:byte_array]
|
36
|
+
@byte_array = options[:byte_array]
|
37
|
+
parse_byte_array
|
38
|
+
elsif options[:orders]
|
39
|
+
@client_id = options[:client_id]
|
40
|
+
@frame = options[:frame]
|
41
|
+
@orders = options[:orders]
|
42
|
+
construct_byte_array :orders
|
43
|
+
elsif options[:data]
|
44
|
+
@client_id = options[:client_id]
|
45
|
+
@frame = options[:frame]
|
46
|
+
@data = options[:data]
|
47
|
+
construct_byte_array :data
|
48
|
+
else
|
49
|
+
raise "Improper options given to initialize! #{options}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def server_lobby_packet?
|
54
|
+
(frame == SERVER_LOBBY_PACKET)
|
55
|
+
end
|
56
|
+
|
57
|
+
alias to_s byte_array
|
58
|
+
|
59
|
+
def inspect
|
60
|
+
byte_array.inspect
|
61
|
+
end
|
62
|
+
|
63
|
+
def unknown?
|
64
|
+
if orders.length == 1
|
65
|
+
order = orders.first
|
66
|
+
return false unless order.order? || order.special_command?
|
67
|
+
end
|
68
|
+
false
|
69
|
+
end
|
70
|
+
|
71
|
+
def metadata?
|
72
|
+
false
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def parse_byte_array
|
78
|
+
@client_id = decode_u32 byte_array[0..3]
|
79
|
+
@length = decode_u32 byte_array[4..7]
|
80
|
+
@frame = decode_u32 byte_array[8..11]
|
81
|
+
@data = byte_array[12..-1]
|
82
|
+
@orders = []
|
83
|
+
d = StringIO.new(data)
|
84
|
+
@orders.append Order.construct(d) until d.eof?
|
85
|
+
end
|
86
|
+
|
87
|
+
def construct_byte_array(type)
|
88
|
+
case type
|
89
|
+
when :data
|
90
|
+
@length = data.length + 4
|
91
|
+
@byte_array = (encode_u32 client_id) + (encode_u32 length) +
|
92
|
+
(encode_u32 frame) + data
|
93
|
+
when :orders
|
94
|
+
@data = ''.force_encoding('ASCII-8BIT')
|
95
|
+
orders.each do |order|
|
96
|
+
@data += order.serialize
|
97
|
+
end
|
98
|
+
construct_byte_array :data
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative 'packet'
|
22
|
+
require_relative 'metadata_packet'
|
23
|
+
|
24
|
+
module OpenRAReplay
|
25
|
+
class PacketParser
|
26
|
+
def initialize(file_stream)
|
27
|
+
@file_stream = file_stream
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_packet
|
31
|
+
byte_array = @file_stream.read 12
|
32
|
+
if byte_array[0..3] == "\xFF\xFF\xFF\xFF".force_encoding('ASCII-8BIT')
|
33
|
+
length = byte_array[8..11].unpack('_L').first + 8
|
34
|
+
byte_array += @file_stream.read length
|
35
|
+
yield OpenRAReplay::MetadataPacket.new(byte_array: byte_array)
|
36
|
+
else
|
37
|
+
length = byte_array[4..7].unpack('_L').first - 4
|
38
|
+
byte_array += @file_stream.read length
|
39
|
+
yield OpenRAReplay::Packet.new(byte_array: byte_array)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def eof?
|
44
|
+
@file_stream.closed? || @file_stream.eof?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative 'sanitize/packet_sanitizer'
|
22
|
+
require_relative 'sanitize/replay_sanitizer'
|
23
|
+
require_relative 'sanitize/cli'
|
@@ -0,0 +1,160 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# OpenRA Replay Sanitizer: Program/library to parse and generate
|
4
|
+
# OpenRA replay files
|
5
|
+
#
|
6
|
+
# Copyright (C) 2018 Luke Spangler
|
7
|
+
#
|
8
|
+
# This program is free software: you can redistribute it and/or modify
|
9
|
+
# it under the terms of the GNU Affero General Public License as
|
10
|
+
# published by the Free Software Foundation, either version 3 of the
|
11
|
+
# License, or (at your option) any later version.
|
12
|
+
#
|
13
|
+
# This program is distributed in the hope that it will be useful,
|
14
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
15
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
16
|
+
# GNU Affero General Public License for more details.
|
17
|
+
|
18
|
+
# You should have received a copy of the GNU Affero General Public License
|
19
|
+
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
20
|
+
|
21
|
+
require_relative 'replay_sanitizer'
|
22
|
+
require_relative '../version'
|
23
|
+
require 'optparse'
|
24
|
+
|
25
|
+
module OpenRAReplay
|
26
|
+
module Sanitize
|
27
|
+
class CLI
|
28
|
+
attr_accessor :ping, :message, :chat, :ip, :time, :password,
|
29
|
+
:player_name, :server_name, :in_name, :out_name, :force
|
30
|
+
|
31
|
+
WARN_COUNT = 'Please provide an input and an output file!'.freeze
|
32
|
+
WARN_INPUT = 'Input file must exist!'.freeze
|
33
|
+
WARN_EXIST = "Output file exists! Pass the '--force' argument" \
|
34
|
+
' to overwrite it.'.freeze
|
35
|
+
|
36
|
+
def initialize(arguments)
|
37
|
+
OptionParser.new do |parser|
|
38
|
+
header parser
|
39
|
+
option_ping parser
|
40
|
+
option_message parser
|
41
|
+
option_chat parser
|
42
|
+
option_ip parser
|
43
|
+
option_time parser
|
44
|
+
option_password parser
|
45
|
+
option_player_name parser
|
46
|
+
option_server_name parser
|
47
|
+
option_force parser
|
48
|
+
footer parser
|
49
|
+
end.parse! arguments
|
50
|
+
handle_input arguments
|
51
|
+
end
|
52
|
+
|
53
|
+
def sanitize
|
54
|
+
OpenRAReplay::Sanitize::ReplaySanitizer.new(in_name, out_name,
|
55
|
+
ping: ping,
|
56
|
+
message: message,
|
57
|
+
chat: chat,
|
58
|
+
ip: ip,
|
59
|
+
time: time,
|
60
|
+
password: password,
|
61
|
+
player_name: player_name,
|
62
|
+
server_name: server_name).sanitize
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def header(parser)
|
68
|
+
parser.banner = "Usage: openra-sanitize [options] in_file out_file\n" \
|
69
|
+
'Reads an OpenRA replay file, trims/masks it, and outputs' \
|
70
|
+
' the result as a new file' \
|
71
|
+
|
72
|
+
parser.separator ''
|
73
|
+
parser.separator 'Specific options:'
|
74
|
+
end
|
75
|
+
|
76
|
+
def footer(parser)
|
77
|
+
parser.separator ''
|
78
|
+
parser.separator 'Common options:'
|
79
|
+
|
80
|
+
parser.on_tail('-h', '--help', 'Show this message') do
|
81
|
+
puts parser
|
82
|
+
Kernel.exit
|
83
|
+
end
|
84
|
+
|
85
|
+
parser.on_tail('--version', 'Show version') do
|
86
|
+
puts "OpenRAReplay-Sanitize: #{OpenRAReplay::VERSION}"
|
87
|
+
Kernel.exit
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def option_ping(parser)
|
92
|
+
parser.on('-p', '--ping', TrueClass, 'Trim all ping-related content') do |p|
|
93
|
+
self.ping = p
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def option_message(parser)
|
98
|
+
parser.on('-m', '--message', TrueClass, 'Trim all server messages') do |m|
|
99
|
+
self.message = m
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def option_chat(parser)
|
104
|
+
parser.on('-c', '--chat', TrueClass, 'Trim all chat messages') do |c|
|
105
|
+
self.chat = c
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def option_ip(parser)
|
110
|
+
parser.on('-i', '--ip', TrueClass, 'Trim all IP addresses') do |i|
|
111
|
+
self.ip = i
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def option_time(parser)
|
116
|
+
parser.on('-t', '--time', TrueClass, 'Mask all dates and times') do |i|
|
117
|
+
self.ip = i
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def option_password(parser)
|
122
|
+
parser.on('-P', '--password', TrueClass, 'Trims the server password') do |p|
|
123
|
+
self.password = p
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def option_player_name(parser)
|
128
|
+
parser.on('-n', '--player-name', TrueClass, 'Masks all player names') do |n|
|
129
|
+
self.player_name = n
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def option_server_name(parser)
|
134
|
+
parser.on('-s', '--server-name', TrueClass, 'Trim the server name') do |s|
|
135
|
+
self.server_name = s
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def option_force(parser)
|
140
|
+
parser.on('-f', '--force', TrueClass, 'Force overwriting out_file') do |f|
|
141
|
+
self.force = f
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def handle_input(arguments)
|
146
|
+
warn_and_exit WARN_COUNT unless arguments.length == 2
|
147
|
+
warn_and_exit WARN_INPUT unless File.exist? arguments.first
|
148
|
+
warn_and_exit WARN_EXIST unless force || !File.exist?(arguments.last)
|
149
|
+
self.in_name = arguments.first
|
150
|
+
self.out_name = arguments.last
|
151
|
+
end
|
152
|
+
|
153
|
+
def warn_and_exit(text)
|
154
|
+
warn text
|
155
|
+
warn "Try 'openra-sanitize --help' for more information."
|
156
|
+
Kernel.exit 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|