modsvaskr 0.1.5 → 0.1.10

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: 891a0a4201c62018b26798acd86da140eb757f5633665d29ab2aadbb39fdb8e3
4
- data.tar.gz: e03c3b7816308b0a153d890f09f86cfadc892c0da1537770fedf4ecbdea5aca7
3
+ metadata.gz: 8d247b6d5b73f10426d5769b031213c408bc6ac8c390baea5e9f6a1b0af7495d
4
+ data.tar.gz: 97d98a1e6d93088e7a63f9a07ac31b01c3ed8e0aba4e4159a842fff9af8efaae
5
5
  SHA512:
6
- metadata.gz: 34f1d6bf79e7301e8bfb12d8ab2dcb6372f62589bd4c6a7f508e4636d337dc2c72e90db41c06ce2a3a65f49f8ae308eb0b11910fd206906aa92c817ba6529932
7
- data.tar.gz: 2035854e4ac51da63b9a6fc7006289b88a307acd92aa8213613dcb10306d787b49dcc40ec087740aaf932a1b56668820fbe3e595887d0df014eaba85b96da201
6
+ metadata.gz: d17a2422af5a5428266c5586887ab5210e0ad7026c8c07fced929b2731aafb07c97bad7c4c8be5886cac8d19ae7e51e1f3f386da6d00d64536e0032bde004e76
7
+ data.tar.gz: 775b864997420ad4e78af51e4511d0ad579a236781cbc30c1f1c577992ae3f486d3ab40f701acf4937d51b2bf4397d02fb54c409d37965a03596e0acc57ad799
@@ -0,0 +1,29 @@
1
+ module Modsvaskr
2
+
3
+ module Encoding
4
+
5
+ # Convert a string to UTF-8
6
+ #
7
+ # Parameters::
8
+ # * *str* (String): The string to convert
9
+ # Result::
10
+ # * String: The converted string
11
+ def self.to_utf8(str)
12
+ orig_encoding = str.encoding
13
+ encoding = nil
14
+ begin
15
+ encoding = %w[
16
+ UTF-8
17
+ Windows-1252
18
+ ISO-8859-1
19
+ ].find { |search_encoding| str.force_encoding(search_encoding).valid_encoding? }
20
+ ensure
21
+ str.force_encoding(orig_encoding)
22
+ end
23
+ raise "Unknown encoding for string #{str[0..127].inspect}" if encoding.nil?
24
+ str.encode('UTF-8', encoding)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -174,6 +174,30 @@ module Modsvaskr
174
174
  end
175
175
  end
176
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
+
177
201
  end
178
202
 
179
203
  end
@@ -37,7 +37,9 @@ module Modsvaskr
37
37
  'SkyrimSE.exe'
38
38
  end
39
39
 
40
- # List of default esps present in the game (the ones in the Data folder when 0 mod is being used)
40
+ # Ordered list of default esps present in the game (the ones in the Data folder when 0 mod is being used).
41
+ # The list is ordered according to the game's load order.
42
+ # [API] - This method is mandatory
41
43
  #
42
44
  # Result::
43
45
  # * Array<String>: List of esp/esm/esl base file names.
@@ -51,6 +53,18 @@ module Modsvaskr
51
53
  ]
52
54
  end
53
55
 
56
+ # Get the load order.
57
+ # [API] - This method is mandatory
58
+ #
59
+ # Result::
60
+ # * Array<String>: List of all active plugins, including masters
61
+ def load_order
62
+ game_esps +
63
+ File.read("#{ENV['USERPROFILE']}/AppData/Local/Skyrim Special Edition/plugins.txt").split("\n").map do |line|
64
+ line =~ /^\*(.+)$/ ? $1.downcase : nil
65
+ end.compact
66
+ end
67
+
54
68
  private
55
69
 
56
70
  # Install SKSE64 corresponding to our game
@@ -211,21 +211,23 @@ module Modsvaskr
211
211
  end
212
212
  end
213
213
  # We will start again. Leave some time to interrupt if we want.
214
- unless @config.no_prompt
214
+ if @config.no_prompt
215
+ out 'Start again automatically as no_prompt has been set.'
216
+ else
215
217
  # First, flush stdin of any pending character
216
218
  $stdin.getc while !select([$stdin], nil, nil, 2).nil?
217
- end
218
- out "We are going to start again in #{@game.timeout_interrupt_tests_secs} seconds. Press Enter now to interrupt it."
219
- key_pressed =
220
- begin
221
- Timeout.timeout(@game.timeout_interrupt_tests_secs) { $stdin.gets }
222
- rescue Timeout::Error
223
- nil
219
+ out "We are going to start again in #{@game.timeout_interrupt_tests_secs} seconds. Press Enter now to interrupt it."
220
+ key_pressed =
221
+ begin
222
+ Timeout.timeout(@game.timeout_interrupt_tests_secs) { $stdin.gets }
223
+ rescue Timeout::Error
224
+ nil
225
+ end
226
+ if key_pressed
227
+ log "[ In-game testing #{@game.name} ] - Run interrupted by user."
228
+ # TODO: Remove AutoTest start on load: it has been interrupted by the user, so we should not keep it in case the user launches the game by itself.
229
+ break
224
230
  end
225
- if key_pressed
226
- log "[ In-game testing #{@game.name} ] - Run interrupted by user."
227
- # TODO: Remove AutoTest start on load: it has been interrupted by the user, so we should not keep it in case the user launches the game by itself.
228
- break
229
231
  end
230
232
  end
231
233
  end
@@ -0,0 +1,39 @@
1
+ module Modsvaskr
2
+
3
+ # Mixin adding methods to map directly a tests suite to an in-game tests suite
4
+ # Uses the following methods:
5
+ # * *in_game_tests_suite* -> Symbol: Name of the in-gamer tests suite on which we forward the tests run
6
+ module InGameTestsSuite
7
+
8
+ # Get the list of tests to be run in-game for a given list of test names.
9
+ # [API] - This method is mandatory for tests needing to be run in-game.
10
+ #
11
+ # Parameters::
12
+ # * *tests* (Array<String>): List of test names
13
+ # Result::
14
+ # * Hash<Symbol, Array<String> >: List of in-game test names, per in-game tests suite
15
+ def in_game_tests_for(tests)
16
+ { in_game_tests_suite => tests }
17
+ end
18
+
19
+ # Set statuses based on the result of AutoTest statuses.
20
+ # AutoTest names are case insensitive.
21
+ # [API] - This method is mandatory for tests needing to be run in-game.
22
+ #
23
+ # Parameters::
24
+ # * *tests* (Array<String>): List of test names
25
+ # * *auto_test_statuses* (Hash<Symbol, Hash<String, String> >): In-game test statuses, per in-game test name, per in-game tests suite
26
+ # Result::
27
+ # * Array<[String, String]>: Corresponding list of [test name, test status]
28
+ def parse_auto_tests_statuses_for(tests, auto_test_statuses)
29
+ in_game_test_statuses = auto_test_statuses[in_game_tests_suite] || {}
30
+ tests.map do |test_name|
31
+ test_downcase = test_name.downcase
32
+ _in_game_test, in_game_test_status = in_game_test_statuses.find { |search_in_game_test, _search_in_game_test_status| search_in_game_test.downcase == test_downcase }
33
+ in_game_test_status.nil? ? nil : [test_name, in_game_test_status]
34
+ end.compact
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -1,9 +1,21 @@
1
+ require 'modsvaskr/in_game_tests_suite'
2
+
1
3
  module Modsvaskr
2
4
 
3
5
  module TestsSuites
4
6
 
5
7
  class ExteriorCell < TestsSuite
6
8
 
9
+ include InGameTestsSuite
10
+
11
+ # Return the in-game tests suite to which we forward the tests to be run
12
+ #
13
+ # Result::
14
+ # * Symbol: In-game tests suite
15
+ def in_game_tests_suite
16
+ :locations
17
+ end
18
+
7
19
  # Discover the list of tests information that could be run.
8
20
  # [API] - This method is mandatory
9
21
  #
@@ -14,7 +26,7 @@ module Modsvaskr
14
26
  # Hash< String, Hash< String, Array<[Integer, Integer]> > >
15
27
  exterior_cells = {}
16
28
  @game.xedit.run_script('DumpInfo', only_once: true)
17
- CSV.read("#{@game.xedit.install_path}/Edit Scripts/Modsvaskr_ExportedDumpInfo.csv", encoding: 'windows-1251:utf-8').each do |row|
29
+ @game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
18
30
  esp_name, record_type = row[0..1]
19
31
  if record_type.downcase == 'cell'
20
32
  cell_type, cell_name, cell_x, cell_y = row[3..6]
@@ -33,7 +45,7 @@ module Modsvaskr
33
45
  # Test only exterior cells that have been changed by mods, and make sure we test the minimum, knowing that each cell loaded in game tests 5x5 cells around
34
46
  vanilla_esps = @game.game_esps
35
47
  vanilla_exterior_cells = vanilla_esps.inject({}) do |merged_worldspaces, esp_name|
36
- merged_worldspaces.merge(exterior_cells[esp_name]) do |worldspace, ext_cells1, ext_cells2|
48
+ merged_worldspaces.merge(exterior_cells[esp_name] || {}) do |worldspace, ext_cells1, ext_cells2|
37
49
  (ext_cells1 + ext_cells2).sort.uniq
38
50
  end
39
51
  end
@@ -75,7 +87,7 @@ module Modsvaskr
75
87
  slice(*(candidate_cell_x - delta_cells..candidate_cell_x + delta_cells)).
76
88
  inject(0) { |sum_cells, (_cur_cell_x, cur_cell_ys)| sum_cells + cur_cell_ys.slice(*(candidate_cell_y - delta_cells..candidate_cell_y + delta_cells)).size }
77
89
  if best_cell_score.nil? || nbr_tested_cells > best_cell_score
78
- nbr_tested_cells = best_cell_score
90
+ best_cell_score = nbr_tested_cells
79
91
  best_cell_x = candidate_cell_x
80
92
  best_cell_y = candidate_cell_y
81
93
  end
@@ -98,30 +110,6 @@ module Modsvaskr
98
110
  tests
99
111
  end
100
112
 
101
- # Get the list of tests to be run in-game for a given list of test names.
102
- # [API] - This method is mandatory for tests needing to be run in-game.
103
- #
104
- # Parameters::
105
- # * *tests* (Array<String>): List of test names
106
- # Result::
107
- # * Hash<Symbol, Array<String> >: List of in-game test names, per in-game tests suite
108
- def in_game_tests_for(tests)
109
- { locations: tests }
110
- end
111
-
112
- # Set statuses based on the result of AutoTest statuses.
113
- # AutoTest names are case insensitive.
114
- # [API] - This method is mandatory for tests needing to be run in-game.
115
- #
116
- # Parameters::
117
- # * *tests* (Array<String>): List of test names
118
- # * *auto_test_statuses* (Hash<Symbol, Array<[String, String]> >): Ordered list of AutoTest [test name, test status], per AutoTest tests suite
119
- # Result::
120
- # * Array<[String, String]>: Corresponding list of [test name, test status]
121
- def parse_auto_tests_statuses_for(tests, auto_test_statuses)
122
- auto_test_statuses.key?(:locations) ? auto_test_statuses[:locations] : []
123
- end
124
-
125
113
  end
126
114
 
127
115
  end
@@ -1,9 +1,21 @@
1
+ require 'modsvaskr/in_game_tests_suite'
2
+
1
3
  module Modsvaskr
2
4
 
3
5
  module TestsSuites
4
6
 
5
7
  class InteriorCell < TestsSuite
6
8
 
9
+ include InGameTestsSuite
10
+
11
+ # Return the in-game tests suite to which we forward the tests to be run
12
+ #
13
+ # Result::
14
+ # * Symbol: In-game tests suite
15
+ def in_game_tests_suite
16
+ :locations
17
+ end
18
+
7
19
  # Discover the list of tests information that could be run.
8
20
  # [API] - This method is mandatory
9
21
  #
@@ -14,7 +26,7 @@ module Modsvaskr
14
26
  # Hash< String, Array<String> >
15
27
  interior_cells = {}
16
28
  @game.xedit.run_script('DumpInfo', only_once: true)
17
- CSV.read("#{@game.xedit.install_path}/Edit Scripts/Modsvaskr_ExportedDumpInfo.csv", encoding: 'windows-1251:utf-8').each do |row|
29
+ @game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
18
30
  esp_name, record_type = row[0..1]
19
31
  if record_type.downcase == 'cell'
20
32
  cell_type, cell_name = row[3..4]
@@ -27,7 +39,7 @@ module Modsvaskr
27
39
  end
28
40
  # Test only interior cells that have been changed by mods
29
41
  vanilla_esps = @game.game_esps
30
- vanilla_interior_cells = vanilla_esps.map { |esp_name| interior_cells[esp_name] }.flatten.sort.uniq
42
+ vanilla_interior_cells = vanilla_esps.map { |esp_name| interior_cells[esp_name] || [] }.flatten.sort.uniq
31
43
  Hash[interior_cells.
32
44
  map { |esp_name, esp_cells| vanilla_esps.include?(esp_name) ? [] : vanilla_interior_cells & esp_cells }.
33
45
  flatten.
@@ -44,30 +56,6 @@ module Modsvaskr
44
56
  ]
45
57
  end
46
58
 
47
- # Get the list of tests to be run in-game for a given list of test names.
48
- # [API] - This method is mandatory for tests needing to be run in-game.
49
- #
50
- # Parameters::
51
- # * *tests* (Array<String>): List of test names
52
- # Result::
53
- # * Hash<Symbol, Array<String> >: List of in-game test names, per in-game tests suite
54
- def in_game_tests_for(tests)
55
- { locations: tests }
56
- end
57
-
58
- # Set statuses based on the result of AutoTest statuses.
59
- # AutoTest names are case insensitive.
60
- # [API] - This method is mandatory for tests needing to be run in-game.
61
- #
62
- # Parameters::
63
- # * *tests* (Array<String>): List of test names
64
- # * *auto_test_statuses* (Hash<Symbol, Array<[String, String]> >): Ordered list of AutoTest [test name, test status], per AutoTest tests suite
65
- # Result::
66
- # * Array<[String, String]>: Corresponding list of [test name, test status]
67
- def parse_auto_tests_statuses_for(tests, auto_test_statuses)
68
- auto_test_statuses.key?(:locations) ? auto_test_statuses[:locations] : []
69
- end
70
-
71
59
  end
72
60
 
73
61
  end
@@ -1,9 +1,21 @@
1
+ require 'modsvaskr/in_game_tests_suite'
2
+
1
3
  module Modsvaskr
2
4
 
3
5
  module TestsSuites
4
6
 
5
7
  class Npc < TestsSuite
6
8
 
9
+ include InGameTestsSuite
10
+
11
+ # Return the in-game tests suite to which we forward the tests to be run
12
+ #
13
+ # Result::
14
+ # * Symbol: In-game tests suite
15
+ def in_game_tests_suite
16
+ :npcs
17
+ end
18
+
7
19
  # Discover the list of tests information that could be run.
8
20
  # [API] - This method is mandatory
9
21
  #
@@ -12,38 +24,24 @@ module Modsvaskr
12
24
  def discover_tests
13
25
  tests = {}
14
26
  @game.xedit.run_script('DumpInfo', only_once: true)
15
- CSV.read("#{@game.xedit.install_path}/Edit Scripts/Modsvaskr_ExportedDumpInfo.csv", encoding: 'windows-1251:utf-8').each do |row|
16
- tests["#{row[0].downcase}/#{row[2].to_i(16)}"] = {
17
- name: "Take screenshot of #{row[0]} - #{row[3]}"
18
- } if row[1].downcase == 'npc_'
27
+ @game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
28
+ if row[1].downcase == 'npc_'
29
+ # Know from which mod this ID comes from
30
+ plugin, base_form_id = @game.decode_form_id(row[2])
31
+ test_name = "#{plugin}/#{base_form_id}"
32
+ if tests.key?(test_name)
33
+ # Add the name of the mod to the description, so that we know which mod modifies which NPC.
34
+ tests[test_name][:name] << "/#{row[0].downcase}"
35
+ else
36
+ tests[test_name] = {
37
+ name: "Take screenshot of #{row[3]} - #{row[0].downcase}"
38
+ }
39
+ end
40
+ end
19
41
  end
20
42
  tests
21
43
  end
22
44
 
23
- # Get the list of tests to be run in-game for a given list of test names.
24
- # [API] - This method is mandatory for tests needing to be run in-game.
25
- #
26
- # Parameters::
27
- # * *tests* (Array<String>): List of test names
28
- # Result::
29
- # * Hash<Symbol, Array<String> >: List of in-game test names, per in-game tests suite
30
- def in_game_tests_for(tests)
31
- { npcs: tests }
32
- end
33
-
34
- # Set statuses based on the result of AutoTest statuses.
35
- # AutoTest names are case insensitive.
36
- # [API] - This method is mandatory for tests needing to be run in-game.
37
- #
38
- # Parameters::
39
- # * *tests* (Array<String>): List of test names
40
- # * *auto_test_statuses* (Hash<Symbol, Array<[String, String]> >): Ordered list of AutoTest [test name, test status], per AutoTest tests suite
41
- # Result::
42
- # * Array<[String, String]>: Corresponding list of [test name, test status]
43
- def parse_auto_tests_statuses_for(tests, auto_test_statuses)
44
- auto_test_statuses.key?(:npcs) ? auto_test_statuses[:npcs] : []
45
- end
46
-
47
45
  end
48
46
 
49
47
  end
@@ -1,9 +1,21 @@
1
+ require 'modsvaskr/in_game_tests_suite'
2
+
1
3
  module Modsvaskr
2
4
 
3
5
  module TestsSuites
4
6
 
5
7
  class NpcHead < TestsSuite
6
8
 
9
+ include InGameTestsSuite
10
+
11
+ # Return the in-game tests suite to which we forward the tests to be run
12
+ #
13
+ # Result::
14
+ # * Symbol: In-game tests suite
15
+ def in_game_tests_suite
16
+ :npcshead
17
+ end
18
+
7
19
  # Discover the list of tests information that could be run.
8
20
  # [API] - This method is mandatory
9
21
  #
@@ -12,38 +24,24 @@ module Modsvaskr
12
24
  def discover_tests
13
25
  tests = {}
14
26
  @game.xedit.run_script('DumpInfo', only_once: true)
15
- CSV.read("#{@game.xedit.install_path}/Edit Scripts/Modsvaskr_ExportedDumpInfo.csv", encoding: 'windows-1251:utf-8').each do |row|
16
- tests["#{row[0].downcase}/#{row[2].to_i(16)}"] = {
17
- name: "Take head screenshot of #{row[0]} - #{row[3]}"
18
- } if row[1].downcase == 'npc_'
27
+ @game.xedit.parse_csv('Modsvaskr_ExportedDumpInfo') do |row|
28
+ if row[1].downcase == 'npc_'
29
+ # Know from which mod this ID comes from
30
+ plugin, base_form_id = @game.decode_form_id(row[2])
31
+ test_name = "#{plugin}/#{base_form_id}"
32
+ if tests.key?(test_name)
33
+ # Add the name of the mod to the description, so that we know which mod modifies which NPC.
34
+ tests[test_name][:name] << "/#{row[0].downcase}"
35
+ else
36
+ tests[test_name] = {
37
+ name: "Take head screenshot of #{row[3]} - #{row[0].downcase}"
38
+ }
39
+ end
40
+ end
19
41
  end
20
42
  tests
21
43
  end
22
44
 
23
- # Get the list of tests to be run in-game for a given list of test names.
24
- # [API] - This method is mandatory for tests needing to be run in-game.
25
- #
26
- # Parameters::
27
- # * *tests* (Array<String>): List of test names
28
- # Result::
29
- # * Hash<Symbol, Array<String> >: List of in-game test names, per in-game tests suite
30
- def in_game_tests_for(tests)
31
- { npcshead: tests }
32
- end
33
-
34
- # Set statuses based on the result of AutoTest statuses.
35
- # AutoTest names are case insensitive.
36
- # [API] - This method is mandatory for tests needing to be run in-game.
37
- #
38
- # Parameters::
39
- # * *tests* (Array<String>): List of test names
40
- # * *auto_test_statuses* (Hash<Symbol, Array<[String, String]> >): Ordered list of AutoTest [test name, test status], per AutoTest tests suite
41
- # Result::
42
- # * Array<[String, String]>: Corresponding list of [test name, test status]
43
- def parse_auto_tests_statuses_for(tests, auto_test_statuses)
44
- auto_test_statuses.key?(:npcshead) ? auto_test_statuses[:npcshead] : []
45
- end
46
-
47
45
  end
48
46
 
49
47
  end
data/lib/modsvaskr/ui.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'csv'
2
1
  require 'curses_menu'
3
2
  require 'modsvaskr/logger'
4
3
  require 'modsvaskr/tests_runner'
@@ -1,5 +1,5 @@
1
1
  module Modsvaskr
2
2
 
3
- VERSION = '0.1.5'
3
+ VERSION = '0.1.10'
4
4
 
5
5
  end
@@ -1,3 +1,5 @@
1
+ require 'csv'
2
+ require 'modsvaskr/encoding'
1
3
  require 'modsvaskr/run_cmd'
2
4
 
3
5
  module Modsvaskr
@@ -47,6 +49,17 @@ module Modsvaskr
47
49
  end
48
50
  end
49
51
 
52
+ # Parse a CSV that has been dumped by a previous run of xEdit
53
+ #
54
+ # Parameters::
55
+ # * *csv* (String): Name of the CSV file (from Edit Scripts), without .csv
56
+ # * *row_block* (Proc): Code called for each CSV row
57
+ # Parameters::
58
+ # * *row* (Array<String>): CSV row
59
+ def parse_csv(csv, &row_block)
60
+ CSV.parse(Encoding.to_utf8(File.read("#{install_path}/Edit Scripts/#{csv}.csv", mode: 'rb'))).each(&row_block)
61
+ end
62
+
50
63
  end
51
64
 
52
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: modsvaskr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muriel Salvan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-01-20 00:00:00.000000000 Z
11
+ date: 2021-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: curses_menu
@@ -104,9 +104,11 @@ extra_rdoc_files: []
104
104
  files:
105
105
  - bin/modsvaskr
106
106
  - lib/modsvaskr/config.rb
107
+ - lib/modsvaskr/encoding.rb
107
108
  - lib/modsvaskr/game.rb
108
109
  - lib/modsvaskr/games/skyrim_se.rb
109
110
  - lib/modsvaskr/in_game_tests_runner.rb
111
+ - lib/modsvaskr/in_game_tests_suite.rb
110
112
  - lib/modsvaskr/logger.rb
111
113
  - lib/modsvaskr/run_cmd.rb
112
114
  - lib/modsvaskr/tests_runner.rb