openra 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 34349d5f69e993dc5c62afef3b1faabf20bd56c6
4
+ data.tar.gz: ee22bb04ead0b72e1a5526448bc901b9fb447cea
5
+ SHA512:
6
+ metadata.gz: c0f19e0167ac3ec21e7edeab7e58495e8cc3414e56b98da98b3519430510fcce723b5693877777abb68e3da075f6453c0515a4ddbc123dd518b13725a6401626
7
+ data.tar.gz: b88312025fe593bd92996790f618b87489dc69a1af381885696424396a4476ef8edc78f1ab9a3ca294fcc65309fd2fbe4e35497eaf2adde163a124bb7f0b0cf8
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ Gemfile.lock
46
+ .ruby-version
47
+ .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Andy Holland
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # Openra Rubygem
2
+
3
+ ### Current state
4
+ Early WIP, expermimental.
5
+
6
+ ### Installation
7
+ ```
8
+ gem install openra
9
+ ```
10
+
11
+ ### Example usage
12
+
13
+ ```sh
14
+ openra replay-data /payh/to/replay.orarep [--format json|pretty-json]
15
+ ```
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/bin/openra ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'openra-cli'
5
+
6
+ Openra::CLI.new.call
@@ -0,0 +1,43 @@
1
+ class BigInteger < BinData::BasePrimitive
2
+ def value_to_binary_string(value)
3
+ value = value.abs
4
+ bytes = []
5
+
6
+ loop do
7
+ seven_bit_byte = value & 0x7f
8
+ value >>= 7
9
+ has_more = value.nonzero? ? 0x80 : 0
10
+ byte = has_more | seven_bit_byte
11
+ bytes.push(byte)
12
+
13
+ break if has_more.zero?
14
+ end
15
+
16
+ bytes.collect { |b| b.chr }.join
17
+ end
18
+
19
+ def read_and_return_value(io)
20
+ value = 0
21
+ bit_shift = 0
22
+
23
+ loop do
24
+ byte = read_uint8(io)
25
+ has_more = byte & 0x80
26
+ seven_bit_byte = byte & 0x7f
27
+ value |= seven_bit_byte << bit_shift
28
+ bit_shift += 7
29
+
30
+ break if has_more.zero?
31
+ end
32
+
33
+ value
34
+ end
35
+
36
+ def sensible_default
37
+ 0
38
+ end
39
+
40
+ def read_uint8(io)
41
+ io.readbytes(1).unpack("C").at(0)
42
+ end
43
+ end
data/lib/openra-cli.rb ADDED
@@ -0,0 +1 @@
1
+ require 'openra/cli'
data/lib/openra.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'openra/version'
2
+ require 'openra/replays'
data/lib/openra/cli.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'hanami/cli'
2
+ require 'openra'
3
+ require 'openra/cli/command_registry'
4
+
5
+ module Openra
6
+ class CLI
7
+ def call(*args)
8
+ Hanami::CLI.new(CommandRegistry).call(*args)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ require 'openra/cli/commands/replay_data'
2
+ require 'openra/cli/commands/version'
3
+
4
+ module Openra
5
+ class CLI
6
+ class CommandRegistry
7
+ extend Hanami::CLI::Registry
8
+
9
+ register 'replay-data', Commands::ReplayData
10
+ register 'version', Commands::Version
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+
3
+ module Openra
4
+ class CLI
5
+ module Commands
6
+ class ReplayData < Hanami::CLI::Command
7
+ desc 'Output replay data to stdout'
8
+
9
+ argument :replay, required: true, desc: 'Path of the replay file to read data from'
10
+ option :format, default: 'json', values: %w(json pretty-json), desc: 'Output format'
11
+
12
+ def call(replay:, **options)
13
+ replay = Openra::Replays::Replay.new(replay)
14
+
15
+ client_index_mapping = {}
16
+ players = replay.metadata.each_with_object([]) do |(key, value), arr|
17
+ next unless key.start_with?('Player')
18
+ arr << value['Name']
19
+ end
20
+
21
+ replay_data = {
22
+ mod: replay.mod,
23
+ version: replay.version,
24
+ map: {
25
+ name: replay.map_title,
26
+ hash: replay.map_id
27
+ },
28
+ game: {
29
+ start_time: replay.start_time,
30
+ end_time: replay.end_time,
31
+ duration: replay.duration
32
+ },
33
+ clients: [],
34
+ chat: []
35
+ }
36
+
37
+ # Get all clients
38
+ replay.orders.select { |order| order.command == 'SyncLobbyClients' }.each do |order|
39
+ clients = Openra::YAML.load(order.target)
40
+ clients.each_pair do |_, client|
41
+ next if client_index_mapping.include?(client['Index'])
42
+
43
+ replay_data[:clients] << {
44
+ index: client['Index'],
45
+ name: client['Name'],
46
+ preferred_color: client['PreferredColor'],
47
+ color: client['Color'],
48
+ faction: client['Faction'],
49
+ ip: client['IpAddress'],
50
+ team: client['Team'] == 0 ? nil : client['Team'],
51
+ is_bot: client['Bot'].nil? ? false : true,
52
+ is_admin: client['IsAdmin'] == 'True',
53
+ is_player: players.include?(client['Name']),
54
+ build: []
55
+ }
56
+
57
+ client_index_mapping[client['Index']] = client
58
+ end
59
+ end
60
+
61
+ replay.orders.each do |order|
62
+ case order.command
63
+ when 'PlaceBuilding'
64
+ client = replay_data[:clients].find do |candidate|
65
+ candidate[:index] == order.client_index.to_s
66
+ end
67
+
68
+ client[:build] << {
69
+ structure: order.target_string,
70
+ placement: {
71
+ x: order.target_x,
72
+ y: order.target_y
73
+ }
74
+ }
75
+ when 'Chat'
76
+ client = replay_data[:clients].find do |candidate|
77
+ candidate[:index] == order.client_index.to_s
78
+ end
79
+
80
+ replay_data[:chat] << {
81
+ name: client[:name],
82
+ message: order.target
83
+ }
84
+ end
85
+ end
86
+
87
+ case options[:format]
88
+ when 'json'
89
+ puts JSON.dump(replay_data)
90
+ when 'pretty-json'
91
+ puts JSON.pretty_generate(replay_data)
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,13 @@
1
+ module Openra
2
+ class CLI
3
+ module Commands
4
+ class Version < Hanami::CLI::Command
5
+ desc 'Print current version number'
6
+
7
+ def call(*)
8
+ puts Openra::VERSION
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'bindata'
2
+ require 'big_integer'
3
+ require 'pascal_string'
4
+ require 'openra/yaml'
5
+ require 'openra/replays/order_decorator'
6
+ require 'openra/replays/order'
7
+ require 'openra/replays/order_list'
8
+ require 'openra/replays/packet'
9
+ require 'openra/replays/packet_list'
10
+ require 'openra/replays/metadata_marker'
11
+ require 'openra/replays/metadata'
12
+ require 'openra/replays/file'
13
+ require 'openra/replays/replay'
@@ -0,0 +1,39 @@
1
+ module Openra
2
+ module Replays
3
+ class File
4
+ def initialize(filename)
5
+ @filename = filename
6
+ end
7
+
8
+ def packets
9
+ @packets ||= PacketList.read(fs)
10
+ end
11
+
12
+ def orders
13
+ @orders ||= packets.orders
14
+ end
15
+
16
+ def metadata
17
+ metadata_fs = fs
18
+ offset = -(metadata_marker.data_length + 4)
19
+ metadata_fs.seek(offset, IO::SEEK_END)
20
+
21
+ @metadata ||= Metadata.read(metadata_fs)
22
+ end
23
+
24
+
25
+ private
26
+
27
+ attr_reader :filename
28
+
29
+ def fs
30
+ @fs ||= ::File.open(filename, 'rb')
31
+ @fs.tap(&:rewind)
32
+ end
33
+
34
+ def metadata_marker
35
+ @metadata_marker ||= MetadataMarker.read(fs)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,9 @@
1
+ module Openra
2
+ module Replays
3
+ class Metadata < BinData::Record
4
+ endian :little
5
+ count_bytes_remaining :total_size
6
+ string :data, length: -> { total_size - 8 }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,10 @@
1
+ module Openra
2
+ module Replays
3
+ class MetadataMarker < BinData::Record
4
+ endian :little
5
+ count_bytes_remaining :total_size
6
+ skip to_abs_offset: -> { total_size - 8 }
7
+ int32 :data_length
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,67 @@
1
+ module Openra
2
+ module Replays
3
+ class Order < BinData::Record
4
+ HEX_FE = ?\xFE.force_encoding('ASCII-8BIT').freeze
5
+ HEX_FF = ?\xFF.force_encoding('ASCII-8BIT').freeze
6
+ # NEED TO LEARN HOW TO READ FLAGS AND TARGET TYPE
7
+ IS_STANDARD_ORDER = -> { order_type == HEX_FF }
8
+ IS_IMMEDIATE_ORDER = -> { order_type == HEX_FE }
9
+ HAS_TARGET = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 1) == 1 }
10
+ TARGET_IS_ACTOR = -> { instance_exec(&HAS_TARGET) && target_type == 1 }
11
+ TARGET_IS_TERRAIN = -> { instance_exec(&HAS_TARGET) && target_type == 2 }
12
+ TARGET_IS_FROZEN_ACTOR = -> { instance_exec(&HAS_TARGET) && target_type == 3 }
13
+ TARGET_IS_CELL = -> { instance_exec(&TARGET_IS_TERRAIN) && (flags & 64) == 64 }
14
+ TARGET_NOT_CELL = -> { instance_exec(&TARGET_IS_TERRAIN) && (flags & 64) != 64 }
15
+ HAS_TARGET_STRING = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 4) == 4 }
16
+ HAS_EXTRA_LOCATION = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 16) == 16 }
17
+ HAS_EXTRA_DATA = -> { instance_exec(&IS_STANDARD_ORDER) && (flags & 32) == 32 }
18
+
19
+ endian :little
20
+ # Common
21
+ string :order_type, read_length: 1
22
+ pascal_string :command
23
+ # Immediate Order Data
24
+ pascal_string :target, onlyif: IS_IMMEDIATE_ORDER
25
+ # Standard Order Data
26
+ uint32 :subject_id, onlyif: IS_STANDARD_ORDER
27
+ uint8 :flags, onlyif: IS_STANDARD_ORDER
28
+ uint8 :target_type, onlyif: HAS_TARGET
29
+ uint32 :actor_id, onlyif: TARGET_IS_ACTOR
30
+ uint32 :player_actor_id, onlyif: TARGET_IS_FROZEN_ACTOR
31
+ uint32 :frozen_actor_id, onlyif: TARGET_IS_FROZEN_ACTOR
32
+ int32 :target_x, onlyif: TARGET_IS_CELL
33
+ int32 :target_y, onlyif: TARGET_IS_CELL
34
+ string :target_layer, read_length: 1, onlyif: TARGET_IS_CELL
35
+ int32 :target_sub_cell, onlyif: TARGET_IS_CELL
36
+ int32 :pos_x, onlyif: TARGET_NOT_CELL
37
+ int32 :pos_y, onlyif: TARGET_NOT_CELL
38
+ int32 :pos_z, onlyif: TARGET_NOT_CELL
39
+ pascal_string :target_string, onlyif: HAS_TARGET_STRING
40
+ int32 :extra_pos_x, onlyif: HAS_EXTRA_LOCATION
41
+ int32 :extra_pos_y, onlyif: HAS_EXTRA_LOCATION
42
+ string :extra_pos_layer, read_length: 1, onlyif: HAS_EXTRA_LOCATION
43
+ uint32 :extra_data, onlyif: HAS_EXTRA_DATA
44
+
45
+ def type
46
+ case order_type
47
+ when HEX_FF
48
+ :standard
49
+ when HEX_FE
50
+ :immediate
51
+ end
52
+ end
53
+
54
+ def standard?
55
+ type == :standard
56
+ end
57
+
58
+ def immediate?
59
+ type == :immediate
60
+ end
61
+
62
+ def queued?
63
+ flags & 8 == 8
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,12 @@
1
+ module Openra
2
+ module Replays
3
+ class OrderDecorator < SimpleDelegator
4
+ attr_reader :client_index
5
+
6
+ def initialize(order, client_index)
7
+ @client_index = client_index
8
+ super(order)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Openra
2
+ module Replays
3
+ class OrderList < BinData::Record
4
+ endian :little
5
+ skip length: 4
6
+ array :orders, type: :order, read_until: :eof
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,28 @@
1
+ module Openra
2
+ module Replays
3
+ class Packet < BinData::Record
4
+ endian :little
5
+ int32 :client_index
6
+ int32 :data_length
7
+ string :data, read_length: :data_length
8
+
9
+ def orders
10
+ return unless valid_order_list?
11
+
12
+ @orders ||= OrderList.read(data).orders.map do |order|
13
+ OrderDecorator.new(order, client_index)
14
+ end
15
+ end
16
+
17
+ def valid_order_list?
18
+ return @valid_order_list if defined?(@valid_order_list)
19
+
20
+ @valid_order_list = begin
21
+ !(data.bytesize < 5 ||
22
+ data.bytesize == 5 && data.bytes.last == 0xBF ||
23
+ data.bytesize >= 5 && data.bytes[4] == 0x65)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ module Openra
2
+ module Replays
3
+ class PacketList < BinData::Record
4
+ endian :little
5
+ array :packets, type: :packet, read_until: :eof
6
+
7
+ def orders
8
+ @orders ||= packets.select(&:valid_order_list?).flat_map(&:orders)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,53 @@
1
+ module Openra
2
+ module Replays
3
+ class Replay
4
+ attr_reader :file
5
+
6
+ def initialize(filename)
7
+ @file = Openra::Replays::File.new(filename)
8
+ end
9
+
10
+ def metadata
11
+ @metadata ||= Openra::YAML.load(file.metadata.data)
12
+ end
13
+
14
+ def orders
15
+ @orders ||= file.orders
16
+ end
17
+
18
+ def mod
19
+ @mod ||= metadata['Root']['Mod']
20
+ end
21
+
22
+ def version
23
+ @version ||= metadata['Root']['Version']
24
+ end
25
+
26
+ def map_id
27
+ @map_id ||= metadata['Root']['MapUid']
28
+ end
29
+
30
+ def map_title
31
+ @map_title ||= metadata['Root']['MapTitle']
32
+ end
33
+
34
+ def start_time
35
+ @start_time ||= DateTime.strptime(
36
+ metadata['Root']['StartTimeUtc'],
37
+ '%Y-%m-%d %H-%M-%S'
38
+ ).to_time
39
+ end
40
+
41
+ def end_time
42
+ @end_time ||= DateTime.strptime(
43
+ metadata['Root']['EndTimeUtc'],
44
+ '%Y-%m-%d %H-%M-%S'
45
+ ).to_time
46
+ end
47
+
48
+ def duration
49
+ @duration ||= (end_time - start_time).to_i
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module Openra
2
+ VERSION = '0.0.2'.freeze
3
+ end
@@ -0,0 +1,39 @@
1
+ module Openra
2
+ class YAML
3
+ MATCHER = /(?<indentation>\t+)?(?<key>[^\:]+)?\:?\s?(?<value>.+)?/.freeze
4
+
5
+ def self.load(yaml_string)
6
+ last_indentation = -1
7
+ last_key = nil
8
+ data = {}
9
+ structs = []
10
+
11
+ yaml_string.split("\n").inject(data) do |struct, line|
12
+ next if line =~ /^\s+$/
13
+
14
+ matchdata = line.match(MATCHER)
15
+ indentation = matchdata[:indentation].to_s.bytesize
16
+ key = matchdata[:key]
17
+ value = matchdata[:value]
18
+
19
+ if indentation > last_indentation
20
+ struct[last_key] = {}
21
+ structs.push(struct[last_key])
22
+ struct = struct[last_key]
23
+ elsif indentation < last_indentation
24
+ diff = last_indentation - indentation
25
+ structs = structs.slice(0..-diff.next)
26
+ struct = structs.last
27
+ end
28
+
29
+ struct[key] = value unless key.nil? || value.nil?
30
+
31
+ last_key = key
32
+ last_indentation = indentation
33
+ struct
34
+ end
35
+
36
+ data[nil]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,12 @@
1
+ class PascalString < BinData::Primitive
2
+ big_integer :len, value: -> { data.length }
3
+ string :data, read_length: :len
4
+
5
+ def get
6
+ self.data
7
+ end
8
+
9
+ def set(v)
10
+ self.data = v
11
+ end
12
+ end
data/openra.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ require File.expand_path('../lib/openra/version', __FILE__)
3
+
4
+ Gem::Specification.new do |spec|
5
+ spec.name = 'openra'
6
+ spec.version = Openra::VERSION
7
+ spec.authors = ['Andy Holland']
8
+ spec.email = ['andyholland1991@aol.com']
9
+ spec.summary = 'Openra Rubygem'
10
+ spec.homepage = 'https://github.com/AMHOL/openra-ruby'
11
+ spec.license = 'MIT'
12
+
13
+ spec.files = `git ls-files -z`.split("\x0") - ['bin/console']
14
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
15
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
16
+ spec.require_paths = ['lib']
17
+
18
+ spec.add_dependency 'bindata', '2.4.3'
19
+ spec.add_dependency 'hanami-cli', '0.2.0'
20
+
21
+ spec.add_development_dependency 'bundler'
22
+ spec.add_development_dependency 'rake'
23
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openra
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Andy Holland
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-06-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bindata
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 2.4.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 2.4.3
27
+ - !ruby/object:Gem::Dependency
28
+ name: hanami-cli
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 0.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - andyholland1991@aol.com
72
+ executables:
73
+ - openra
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - bin/openra
83
+ - lib/big_integer.rb
84
+ - lib/openra-cli.rb
85
+ - lib/openra.rb
86
+ - lib/openra/cli.rb
87
+ - lib/openra/cli/command_registry.rb
88
+ - lib/openra/cli/commands/replay_data.rb
89
+ - lib/openra/cli/commands/version.rb
90
+ - lib/openra/replays.rb
91
+ - lib/openra/replays/file.rb
92
+ - lib/openra/replays/metadata.rb
93
+ - lib/openra/replays/metadata_marker.rb
94
+ - lib/openra/replays/order.rb
95
+ - lib/openra/replays/order_decorator.rb
96
+ - lib/openra/replays/order_list.rb
97
+ - lib/openra/replays/packet.rb
98
+ - lib/openra/replays/packet_list.rb
99
+ - lib/openra/replays/replay.rb
100
+ - lib/openra/version.rb
101
+ - lib/openra/yaml.rb
102
+ - lib/pascal_string.rb
103
+ - openra.gemspec
104
+ homepage: https://github.com/AMHOL/openra-ruby
105
+ licenses:
106
+ - MIT
107
+ metadata: {}
108
+ post_install_message:
109
+ rdoc_options: []
110
+ require_paths:
111
+ - lib
112
+ required_ruby_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ required_rubygems_version: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - ">="
120
+ - !ruby/object:Gem::Version
121
+ version: '0'
122
+ requirements: []
123
+ rubyforge_project:
124
+ rubygems_version: 2.6.13
125
+ signing_key:
126
+ specification_version: 4
127
+ summary: Openra Rubygem
128
+ test_files: []