openrareplay 0.1.1

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