modsvaskr 0.1.11 → 0.1.12

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
  SHA256:
3
- metadata.gz: 49c9d23f41a3e7e6b7f60f4961e402e28e2136074f6e01e62da3d9a36ec41fce
4
- data.tar.gz: 276d691af40d723b2412951654299e7a4d745ac83de2e4c76bd2df1baba4c3a6
3
+ metadata.gz: 2c41c160023d7591e52e7b93fde57bc33462627d61714384aa3051c8f8052f99
4
+ data.tar.gz: 4e30b5822a983d6e57772ba65e19d7b8b657163c5b3c4d275bb55a61376c9e2c
5
5
  SHA512:
6
- metadata.gz: 15c41045b26d2c8d05ab3ec94aa06420745a121402f08a5a0fb3aec1d7ab0bab0a459cb69a9083d34cdcb5b06574ea45a7cedd982be8a9503c791cfe82bff9df
7
- data.tar.gz: 9c6370ad8b2f54728beb175a10887227193ad27817237db090d9a99e5a8f1d5eb5c0adac790c5596bb5cfbbd566f6750be665a60ab5e1ef06396a5dec72e0cbf
6
+ metadata.gz: 61ced31f7a9f75bb9679e47045cdfd38d6071f1eb91587cab8a3aa83c8c6d2de559402e3995b65f020a1acf6ce31d2ca96e31341960906c87f0717991a669f1e
7
+ data.tar.gz: c7e3610bc8f62f290fa586e66489f5f4e69b40942814a0ba56237716c925cb5ee7878152f58f9dfc94746242d84dd48bd67f96a37e903db3169cafd19ee2f533
data/bin/modsvaskr CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env ruby
2
- # require 'mod_organizer'
3
- require 'modsvaskr/config'
4
- require 'modsvaskr/ui'
5
-
6
- begin
7
- Modsvaskr::Ui.new(config: Modsvaskr::Config.new('./modsvaskr.yaml')).run
8
- rescue
9
- puts 'Press Enter to exit.'
10
- $stdin.gets
11
- end
1
+ #!/usr/bin/env ruby
2
+ # require 'mod_organizer'
3
+ require 'modsvaskr/config'
4
+ require 'modsvaskr/ui'
5
+
6
+ begin
7
+ Modsvaskr::Ui.new(config: Modsvaskr::Config.new('./modsvaskr.yaml')).run
8
+ rescue
9
+ puts 'Press Enter to exit.'
10
+ $stdin.gets
11
+ end
@@ -1,79 +1,78 @@
1
- require 'yaml'
2
- require 'modsvaskr/game'
3
- require 'modsvaskr/xedit'
4
-
5
- module Modsvaskr
6
-
7
- # Configuration
8
- class Config
9
-
10
- # Constructor
11
- #
12
- # Parameters::
13
- # * *file* (String): File containing configuration
14
- def initialize(file)
15
- @config = YAML.load(File.read(file)) || {}
16
- # Parse all game types plugins
17
- # Hash<Symbol, Class>
18
- @game_types = Hash[
19
- Dir.glob("#{__dir__}/games/*.rb").map do |game_type_file|
20
- require game_type_file
21
- base_name = File.basename(game_type_file, '.rb')
22
- [
23
- base_name.to_sym,
24
- Games.const_get(base_name.split('_').collect(&:capitalize).join.to_sym)
25
- ]
26
- end
27
- ]
28
- end
29
-
30
- # Get the games list
31
- #
32
- # Result::
33
- # * Array<Game>: List of games
34
- def games
35
- unless defined?(@games)
36
- @games = (@config['games'] || []).map do |game_info|
37
- game_type = game_info['type'].to_sym
38
- raise "Unknown game type: #{game_type}. Available ones are #{@game_types.keys.join(', ')}" unless @game_types.key?(game_type)
39
- @game_types[game_type].new(self, game_info)
40
- end
41
- end
42
- @games
43
- end
44
-
45
- # Return the xEdit path
46
- #
47
- # Result::
48
- # * String: The xEdit path
49
- def xedit_path
50
- @config['xedit']
51
- end
52
-
53
- # Return the 7-Zip path
54
- #
55
- # Result::
56
- # * String: The 7-Zip path
57
- def seven_zip_path
58
- @config['7zip']
59
- end
60
-
61
- # Return the automated keys to apply
62
- #
63
- # Result::
64
- # * Array<String>: The list of automated keys
65
- def auto_keys
66
- @config['auto_keys'] || []
67
- end
68
-
69
- # Return the no_prompt flag
70
- #
71
- # Result::
72
- # * Boolean: no_prompt flag
73
- def no_prompt
74
- @config['no_prompt'] || false
75
- end
76
-
77
- end
78
-
79
- end
1
+ require 'yaml'
2
+ require 'modsvaskr/game'
3
+ require 'modsvaskr/xedit'
4
+
5
+ module Modsvaskr
6
+
7
+ # Configuration
8
+ class Config
9
+
10
+ # Constructor
11
+ #
12
+ # Parameters::
13
+ # * *file* (String): File containing configuration
14
+ def initialize(file)
15
+ @config = YAML.safe_load(File.read(file)) || {}
16
+ # Parse all game types plugins
17
+ # Hash<Symbol, Class>
18
+ @game_types = Dir.glob("#{__dir__}/games/*.rb").map do |game_type_file|
19
+ require game_type_file
20
+ base_name = File.basename(game_type_file, '.rb')
21
+ [
22
+ base_name.to_sym,
23
+ Games.const_get(base_name.split('_').collect(&:capitalize).join.to_sym)
24
+ ]
25
+ end.to_h
26
+ end
27
+
28
+ # Get the games list
29
+ #
30
+ # Result::
31
+ # * Array<Game>: List of games
32
+ def games
33
+ unless defined?(@games)
34
+ @games = (@config['games'] || []).map do |game_info|
35
+ game_type = game_info['type'].to_sym
36
+ raise "Unknown game type: #{game_type}. Available ones are #{@game_types.keys.join(', ')}" unless @game_types.key?(game_type)
37
+
38
+ @game_types[game_type].new(self, game_info)
39
+ end
40
+ end
41
+ @games
42
+ end
43
+
44
+ # Return the xEdit path
45
+ #
46
+ # Result::
47
+ # * String: The xEdit path
48
+ def xedit_path
49
+ @config['xedit']
50
+ end
51
+
52
+ # Return the 7-Zip path
53
+ #
54
+ # Result::
55
+ # * String: The 7-Zip path
56
+ def seven_zip_path
57
+ @config['7zip']
58
+ end
59
+
60
+ # Return the automated keys to apply
61
+ #
62
+ # Result::
63
+ # * Array<String>: The list of automated keys
64
+ def auto_keys
65
+ @config['auto_keys'] || []
66
+ end
67
+
68
+ # Return the no_prompt flag
69
+ #
70
+ # Result::
71
+ # * Boolean: no_prompt flag
72
+ def no_prompt
73
+ @config['no_prompt'] || false
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -1,5 +1,6 @@
1
1
  module Modsvaskr
2
2
 
3
+ # Provide helpers to encode Windows and Linux strings to UTF-8
3
4
  module Encoding
4
5
 
5
6
  # Convert a string to UTF-8
@@ -8,7 +9,7 @@ module Modsvaskr
8
9
  # * *str* (String): The string to convert
9
10
  # Result::
10
11
  # * String: The converted string
11
- def self.to_utf8(str)
12
+ def self.to_utf_8(str)
12
13
  orig_encoding = str.encoding
13
14
  encoding = nil
14
15
  begin
@@ -21,6 +22,7 @@ module Modsvaskr
21
22
  str.force_encoding(orig_encoding)
22
23
  end
23
24
  raise "Unknown encoding for string #{str[0..127].inspect}" if encoding.nil?
25
+
24
26
  str.encode('UTF-8', encoding)
25
27
  end
26
28
 
@@ -1,203 +1,208 @@
1
- require 'yaml'
2
- require 'modsvaskr/logger'
3
- require 'modsvaskr/run_cmd'
4
-
5
- module Modsvaskr
6
-
7
- # Common functionality for any Game
8
- class Game
9
-
10
- include Logger, RunCmd
11
-
12
- # Constructor
13
- #
14
- # Parameters::
15
- # * *config* (Config): The config
16
- # * *game_info* (Hash<String,Object>): Game info:
17
- # * *name* (String): Game name
18
- # * *path* (String): Game installation dir
19
- # * *launch_exe* (String): Executable to be launched
20
- # * *min_launch_time_secs* (Integer): Minimum expected lauch time for the game, in seconds [default: 10]
21
- # * *tests_poll_secs* (Integer): Interval in seconds to be respected between 2 test statuses polling [default: 5]
22
- # * *timeout_frozen_tests_secs* (Integer): Timeout in seconds of a frozen game [default: 300]
23
- # * *timeout_interrupt_tests_secs* (Integer): Timeout in seconds for the player to interrupt a tests session before restarting the game [default: 10]
24
- def initialize(config, game_info)
25
- @config = config
26
- # Set default values here
27
- @game_info = {
28
- 'min_launch_time_secs' => 10,
29
- 'tests_poll_secs' => 5,
30
- 'timeout_frozen_tests_secs' => 300,
31
- 'timeout_interrupt_tests_secs' => 10
32
- }.merge(game_info)
33
- @name = name
34
- @pid = nil
35
- init if self.respond_to?(:init)
36
- end
37
-
38
- # Return the game name
39
- #
40
- # Result::
41
- # * String: Game name
42
- def name
43
- @game_info['name']
44
- end
45
-
46
- # Return the game path
47
- #
48
- # Result::
49
- # * String: Game path
50
- def path
51
- @game_info['path']
52
- end
53
-
54
- # Return the launch executable
55
- #
56
- # Result::
57
- # * String: Launch executable
58
- def launch_exe
59
- @game_info['launch_exe']
60
- end
61
-
62
- # Return the tests polling interval
63
- #
64
- # Result::
65
- # * Integer: Tests polling interval
66
- def tests_poll_secs
67
- @game_info['tests_poll_secs']
68
- end
69
-
70
- # Return the timeout to detect a frozen game
71
- #
72
- # Result::
73
- # * Integer: Timeout to detect a frozen game
74
- def timeout_frozen_tests_secs
75
- @game_info['timeout_frozen_tests_secs']
76
- end
77
-
78
- # Return the timeout before restarting a game tests session
79
- #
80
- # Result::
81
- # * Integer: Timeout before restarting a game tests session
82
- def timeout_interrupt_tests_secs
83
- @game_info['timeout_interrupt_tests_secs']
84
- end
85
-
86
- # Return an xEdit instance for this game
87
- #
88
- # Result::
89
- # * Xedit: The xEdit instance
90
- def xedit
91
- @xedit = Xedit.new(@config.xedit_path, path) unless defined?(@xedit)
92
- @xedit
93
- end
94
-
95
- # Launch the game, and wait for launch to be successful
96
- #
97
- # Parameters::
98
- # * *autoload* (Boolean or String): If false, then launch the game using the normal launcher. If String, then use AutoLoad to load a given saved file (or empty to continue latest save) [default: false].
99
- def launch(autoload: false)
100
- # Launch the game
101
- @idx_launch = 0 unless defined?(@idx_launch)
102
- if autoload
103
- log "[ Game #{name} ] - Launch game (##{@idx_launch}) using AutoLoad #{autoload}..."
104
- autoload_file = "#{path}/Data/AutoLoad.cmd"
105
- if File.exist?(autoload_file)
106
- run_cmd({
107
- dir: path,
108
- exe: 'Data\AutoLoad.cmd',
109
- args: [autoload]
110
- })
111
- else
112
- log "[ Game #{name} ] - Missing file #{autoload_file}. Can't use AutoLoad to load game automatically. Please install the AutoLoad mod."
113
- end
114
- else
115
- log "[ Game #{name} ] - Launch game (##{@idx_launch}) using configured launcher (#{launch_exe})..."
116
- run_cmd({
117
- dir: path,
118
- exe: launch_exe
119
- })
120
- end
121
- @idx_launch += 1
122
- # The game launches asynchronously, so just wait a little bit and check for the process existence
123
- sleep @game_info['min_launch_time_secs']
124
- tasklist_stdout = nil
125
- loop do
126
- tasklist_stdout = `tasklist | find "#{running_exe}"`.strip
127
- break unless tasklist_stdout.empty?
128
- log "[ Game #{name} ] - #{running_exe} is not running. Wait for its startup..."
129
- sleep 1
130
- end
131
- @pid = Integer(tasklist_stdout.split(' ')[1])
132
- log "[ Game #{name} ] - #{running_exe} has started with PID #{@pid}"
133
- end
134
-
135
- # Is the game currently running?
136
- #
137
- # Result::
138
- # * Boolean: Is the game currently running?
139
- def running?
140
- if @pid
141
- running = true
142
- begin
143
- # Process.kill does not work when the game has crashed (the process is still detected as zombie)
144
- # running = Process.kill(0, @pid) == 1
145
- tasklist_stdout = `tasklist | find "#{running_exe}"`.strip
146
- running = !tasklist_stdout.empty?
147
- # log "[ Game #{name} ] - Tasklist returned no #{running_exe}:\n#{tasklist_stdout}" unless running
148
- rescue Errno::ESRCH
149
- log "[ Game #{name} ] - Got error while waiting for #{running_exe} PID #{@pid}: #{$!}"
150
- running = false
151
- end
152
- @pid = nil unless running
153
- running
154
- else
155
- false
156
- end
157
- end
158
-
159
- # Kill the game, and wait till it is killed
160
- def kill
161
- if @pid
162
- first_time = true
163
- while @pid do
164
- system "taskkill #{first_time ? '' : '/F '}/pid #{@pid}"
165
- first_time = false
166
- sleep 1
167
- if running?
168
- log "[ Game #{name} ] - #{running_exe} is still running (PID #{@pid}). Wait for its kill..."
169
- sleep 5
170
- end
171
- end
172
- else
173
- log "[ Game #{name} ] - Game not started, so nothing to kill."
174
- end
175
- end
176
-
177
- # Get the load order.
178
- # Keep a cache of it.
179
- #
180
- # Result::
181
- # * Array<String>: List of all active plugins, including masters
182
- def get_load_order
183
- @cache_load_order = load_order unless defined?(@cache_load_order)
184
- @cache_load_order
185
- end
186
-
187
- # Get the mod and base id corresponding to a given form id.
188
- # Uses load order to determine it.
189
- #
190
- # Parameters::
191
- # * *form_id* (String or Integer): Form ID, either as hexadecimal string, or numercial value
192
- # Result::
193
- # * String: Plugin name
194
- # * Integer: Base form id, independent from the load order
195
- def decode_form_id(form_id)
196
- form_id = form_id.to_i(16) if form_id.is_a?(String)
197
- [get_load_order[form_id / 16_777_216], form_id % 16_777_216]
198
- end
199
-
200
-
201
- end
202
-
203
- end
1
+ require 'yaml'
2
+ require 'modsvaskr/logger'
3
+ require 'modsvaskr/run_cmd'
4
+
5
+ module Modsvaskr
6
+
7
+ # Common functionality for any Game
8
+ class Game
9
+
10
+ include RunCmd
11
+ include Logger
12
+
13
+ # Constructor
14
+ #
15
+ # Parameters::
16
+ # * *config* (Config): The config
17
+ # * *game_info* (Hash<String,Object>): Game info:
18
+ # * *name* (String): Game name
19
+ # * *path* (String): Game installation dir
20
+ # * *launch_exe* (String): Executable to be launched
21
+ # * *min_launch_time_secs* (Integer): Minimum expected lauch time for the game, in seconds [default: 10]
22
+ # * *tests_poll_secs* (Integer): Interval in seconds to be respected between 2 test statuses polling [default: 5]
23
+ # * *timeout_frozen_tests_secs* (Integer): Timeout in seconds of a frozen game [default: 300]
24
+ # * *timeout_interrupt_tests_secs* (Integer): Timeout in seconds for the player to interrupt a tests session before restarting the game [default: 10]
25
+ def initialize(config, game_info)
26
+ @config = config
27
+ # Set default values here
28
+ @game_info = {
29
+ 'min_launch_time_secs' => 10,
30
+ 'tests_poll_secs' => 5,
31
+ 'timeout_frozen_tests_secs' => 300,
32
+ 'timeout_interrupt_tests_secs' => 10
33
+ }.merge(game_info)
34
+ @name = name
35
+ @pid = nil
36
+ init if respond_to?(:init)
37
+ end
38
+
39
+ # Return the game name
40
+ #
41
+ # Result::
42
+ # * String: Game name
43
+ def name
44
+ @game_info['name']
45
+ end
46
+
47
+ # Return the game path
48
+ #
49
+ # Result::
50
+ # * String: Game path
51
+ def path
52
+ @game_info['path']
53
+ end
54
+
55
+ # Return the launch executable
56
+ #
57
+ # Result::
58
+ # * String: Launch executable
59
+ def launch_exe
60
+ @game_info['launch_exe']
61
+ end
62
+
63
+ # Return the tests polling interval
64
+ #
65
+ # Result::
66
+ # * Integer: Tests polling interval
67
+ def tests_poll_secs
68
+ @game_info['tests_poll_secs']
69
+ end
70
+
71
+ # Return the timeout to detect a frozen game
72
+ #
73
+ # Result::
74
+ # * Integer: Timeout to detect a frozen game
75
+ def timeout_frozen_tests_secs
76
+ @game_info['timeout_frozen_tests_secs']
77
+ end
78
+
79
+ # Return the timeout before restarting a game tests session
80
+ #
81
+ # Result::
82
+ # * Integer: Timeout before restarting a game tests session
83
+ def timeout_interrupt_tests_secs
84
+ @game_info['timeout_interrupt_tests_secs']
85
+ end
86
+
87
+ # Return an xEdit instance for this game
88
+ #
89
+ # Result::
90
+ # * Xedit: The xEdit instance
91
+ def xedit
92
+ @xedit = Xedit.new(@config.xedit_path, path) unless defined?(@xedit)
93
+ @xedit
94
+ end
95
+
96
+ # Launch the game, and wait for launch to be successful
97
+ #
98
+ # Parameters::
99
+ # * *autoload* (Boolean or String): If false, then launch the game using the normal launcher. If String, then use AutoLoad to load a given saved file (or empty to continue latest save) [default: false].
100
+ def launch(autoload: false)
101
+ # Launch the game
102
+ @idx_launch = 0 unless defined?(@idx_launch)
103
+ if autoload
104
+ log "[ Game #{name} ] - Launch game (##{@idx_launch}) using AutoLoad #{autoload}..."
105
+ autoload_file = "#{path}/Data/AutoLoad.cmd"
106
+ if File.exist?(autoload_file)
107
+ run_cmd(
108
+ {
109
+ dir: path,
110
+ exe: 'Data\AutoLoad.cmd',
111
+ args: [autoload]
112
+ }
113
+ )
114
+ else
115
+ log "[ Game #{name} ] - Missing file #{autoload_file}. Can't use AutoLoad to load game automatically. Please install the AutoLoad mod."
116
+ end
117
+ else
118
+ log "[ Game #{name} ] - Launch game (##{@idx_launch}) using configured launcher (#{launch_exe})..."
119
+ run_cmd(
120
+ {
121
+ dir: path,
122
+ exe: launch_exe
123
+ }
124
+ )
125
+ end
126
+ @idx_launch += 1
127
+ # The game launches asynchronously, so just wait a little bit and check for the process existence
128
+ sleep @game_info['min_launch_time_secs']
129
+ tasklist_stdout = nil
130
+ loop do
131
+ tasklist_stdout = `tasklist | find "#{running_exe}"`.strip
132
+ break unless tasklist_stdout.empty?
133
+
134
+ log "[ Game #{name} ] - #{running_exe} is not running. Wait for its startup..."
135
+ sleep 1
136
+ end
137
+ @pid = Integer(tasklist_stdout.split[1])
138
+ log "[ Game #{name} ] - #{running_exe} has started with PID #{@pid}"
139
+ end
140
+
141
+ # Is the game currently running?
142
+ #
143
+ # Result::
144
+ # * Boolean: Is the game currently running?
145
+ def running?
146
+ if @pid
147
+ running = true
148
+ begin
149
+ # Process.kill does not work when the game has crashed (the process is still detected as zombie)
150
+ # running = Process.kill(0, @pid) == 1
151
+ tasklist_stdout = `tasklist | find "#{running_exe}"`.strip
152
+ running = !tasklist_stdout.empty?
153
+ # log "[ Game #{name} ] - Tasklist returned no #{running_exe}:\n#{tasklist_stdout}" unless running
154
+ rescue Errno::ESRCH
155
+ log "[ Game #{name} ] - Got error while waiting for #{running_exe} PID #{@pid}: #{$ERROR_INFO}"
156
+ running = false
157
+ end
158
+ @pid = nil unless running
159
+ running
160
+ else
161
+ false
162
+ end
163
+ end
164
+
165
+ # Kill the game, and wait till it is killed
166
+ def kill
167
+ if @pid
168
+ first_time = true
169
+ while @pid
170
+ system "taskkill #{first_time ? '' : '/F '}/pid #{@pid}"
171
+ first_time = false
172
+ sleep 1
173
+ if running?
174
+ log "[ Game #{name} ] - #{running_exe} is still running (PID #{@pid}). Wait for its kill..."
175
+ sleep 5
176
+ end
177
+ end
178
+ else
179
+ log "[ Game #{name} ] - Game not started, so nothing to kill."
180
+ end
181
+ end
182
+
183
+ # Get the load order.
184
+ # Keep a cache of it.
185
+ #
186
+ # Result::
187
+ # * Array<String>: List of all active plugins, including masters
188
+ def load_order
189
+ @cache_load_order = read_load_order unless defined?(@cache_load_order)
190
+ @cache_load_order
191
+ end
192
+
193
+ # Get the mod and base id corresponding to a given form id.
194
+ # Uses load order to determine it.
195
+ #
196
+ # Parameters::
197
+ # * *form_id* (String or Integer): Form ID, either as hexadecimal string, or numercial value
198
+ # Result::
199
+ # * String: Plugin name
200
+ # * Integer: Base form id, independent from the load order
201
+ def decode_form_id(form_id)
202
+ form_id = form_id.to_i(16) if form_id.is_a?(String)
203
+ [load_order[form_id / 16_777_216], form_id % 16_777_216]
204
+ end
205
+
206
+ end
207
+
208
+ end