revolt 0.5.3 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -13,8 +13,9 @@ track management. This may extend in the future.
13
13
 
14
14
  Using the library users can easily create automated
15
15
  tasks. Some of the major features are:
16
- - Install track packages from WWW server
17
- - Move, copy and delete tracks
16
+ - Install track packages from WWW server or local file
17
+ - Create track packages from locally installed tracks
18
+ - Move, copy, rename and delete tracks
18
19
  - Search tracks with various criteria
19
20
  - Create alternative track repositories
20
21
 
@@ -129,19 +130,81 @@ you can find tracks by any property defined in the tracks .inf file.
129
130
  The easiest way to use it is to just enter the part of the name of
130
131
  the track you want to find. For examples:
131
132
 
132
- rv_find_levels temple
133
+ rv_find_levels.rb temple
133
134
 
134
135
  That would search for all levels whose name contains 'temple'
135
136
  (case insensitive). Another more complex example is to find
136
137
  all custom tracks that can be raced in reverse mode that
137
138
  have FARCLIP more than 10000:
138
139
 
139
- rv_find_levels -m 'custom?,reverse?,farclip>10000'
140
-
140
+ rv_find_levels.rb -m 'custom?,reverse?,farclip>10000'
141
+
142
+ The match rules separated by ',' are AND. To create OR rules
143
+ you can specify several matching rules. For example to find
144
+ all tracks that are raceable in reverse or whose name is
145
+ 'RV Temple':
146
+
147
+ rv_find_levels.rb -m 'custom?,reverse?' -m 'name=RV Temple'
148
+
141
149
  Use the -h option to get more help. The output format is:
142
150
 
143
151
  <level id> (<level name>) [<matched_attribute>=<matched_value>]
144
152
 
145
153
  Trying it in practice should clear the syntax if that seems weird.
146
154
 
155
+ === rv_info_levels.rb
156
+ Displays information about levels, such as if it's a custom
157
+ track, stock track, can be raced in reverse, or is a so called
158
+ "Full Custom" track. Also the contents INF file can be
159
+ output. The same match format can be used as for rv_find_levels.rb
160
+
161
+ For more information and options:
162
+
163
+ rv_info_levels.rb -h
164
+
165
+ === rv_delete_levels.rb
166
+ Deletes levels. Before deleting confirms by default. Match options format
167
+ is similar as rv_find_levels.rb
168
+
169
+ For more information and options:
170
+
171
+ rv_info_levels.rb -h
172
+
173
+
174
+ === rv_package_levels.rb
175
+ Creates packages of levels. The parameters are the
176
+ folder names of the levels to pack. From each level
177
+ a separate package is created. Matching options can
178
+ be used with -m as with rv_find_levels.rb
179
+
180
+ Example:
181
+
182
+ rv_package_levels_level.rb rvt chilled
183
+
184
+ This would create rvt.zip and chilled.zip of corresponding
185
+ levels.
186
+
187
+ For more information and options:
188
+
189
+ rv_rename_level.rb -h
190
+
191
+ === rv_rename_level.rb
192
+ Renames a level. Changes the directory name and
193
+ necessary file names to the one specified. Confirms
194
+ by default before doing the operation.
195
+
196
+ Example:
197
+
198
+ rv_rename_level.rb USER678 global_race
199
+
200
+ The parameters must be level ids, that is, they are
201
+ the folder names of the level. The above command
202
+ would rename USER678 to global_race.
203
+
204
+ The matching parameters can be used with -m option
205
+ for the level to be renamed, but only one level
206
+ can be matched or error is produced.
207
+
208
+ For more information and options:
147
209
 
210
+ rv_rename_level.rb -h
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ require 'optparse'
4
+ require 'pp'
5
+ require 'revolt'
6
+ require 'set'
7
+
8
+ def main
9
+ del_levs = []
10
+ cmd = CmdArguments.new(ARGV)
11
+ ReVolt::Logger.enable if cmd[:debug]
12
+ levels = ReVolt::Levels.at(cmd[:base])
13
+ levels.each_level do |level|
14
+ all_matches = {}
15
+ nmatches = ReVolt::Util::Matcher.multi(level, cmd[:match], all_matches)
16
+ if nmatches > 0
17
+ keyvaluestr = all_matches.map{|(k,v)|"#{k}=#{v}"}.join(', ')
18
+ del_levs << level
19
+ puts "%s [%s]" % [level.inspect, keyvaluestr]
20
+ end
21
+ end
22
+
23
+ if del_levs.size > 0
24
+ unless cmd[:force]
25
+ puts ""
26
+ print "Delete these levels? (y/N) "
27
+ answer = STDIN.gets.chomp
28
+ if answer.downcase != 'y'
29
+ puts "Deleting not done"
30
+ exit(0)
31
+ end
32
+ end
33
+ del_levs.each do |l, match|
34
+ puts "Deleting %s (%s)" % [l.name, l.id.to_s]
35
+ l.delete
36
+ end
37
+ else
38
+ puts "No matching levels"
39
+ end
40
+ end
41
+
42
+
43
+ class CmdArguments < Hash
44
+ include ReVolt::Util::CommonCmdArgs
45
+
46
+ def initialize(cmd)
47
+ super()
48
+
49
+ self[:base] = ReVolt::Info::path
50
+ self[:force] = false
51
+
52
+ matchstrs = []
53
+ opts = OptionParser.new do |opts|
54
+ opts.banner = "Usage: #$0 [options] [name_match ...]\n"
55
+ opts.separator ""
56
+ opts.separator "Deletes levels. By default confirms deletion of matched levels"
57
+ opts.separator ""
58
+ opts.separator "Examples:"
59
+ opts.separator " Deletes for all tracks which name contains temple:"
60
+ opts.separator " #$0 temple"
61
+ opts.separator " Deletes tracks which farclip is more than 31000 and startgrid is 3"
62
+ opts.separator " #$0 -m 'farclip>31000,startgrid=3'"
63
+
64
+ opts.separator ""
65
+ match_help(opts)
66
+ opts.separator ""
67
+
68
+ opts.separator "Options:"
69
+
70
+ on_match(opts) do |value|
71
+ matchstrs << value
72
+ end
73
+ opts.on('-d', '--debug',
74
+ 'Outputs debug information') do
75
+ self[:debug] = true
76
+ end
77
+ opts.on('-f', '--force',
78
+ 'Force deletion without confirmation') do
79
+ self[:force] = true
80
+ end
81
+
82
+ on_base_path(opts) do |value|
83
+ self[:base] = value
84
+ end
85
+
86
+ opts.on_tail('-h', '--help',
87
+ 'Display full help and exit') do
88
+ puts opts
89
+
90
+ exit(0)
91
+ end
92
+ end
93
+
94
+ opts.parse!(cmd)
95
+
96
+ cmd.each do |n|
97
+ matchstrs << '%s=~%s' % ['name', n]
98
+ end
99
+
100
+ # If input file not specified, at least start date is required
101
+ if matchstrs.size == 0
102
+ puts "Error: no matches specified\n"
103
+ STDERR.puts opts.banner
104
+ exit 1
105
+ end
106
+
107
+ begin
108
+ # Create the matcher objects
109
+ self[:match] = []
110
+ for m in matchstrs
111
+ self[:match] << ReVolt::Util::Matcher.parse(m)
112
+ end
113
+ rescue ReVolt::Util::Matcher::ParseError => e
114
+ STDERR.puts e.errstr
115
+ exit(1)
116
+ end
117
+ end
118
+ end
119
+
120
+
121
+ main
@@ -10,80 +10,26 @@ def main
10
10
  ReVolt::Logger.enable if cmd[:debug]
11
11
  levels = ReVolt::Levels.at(cmd[:base])
12
12
  levels.each_level do |level|
13
- matches = {}
14
- nmatches = 0
15
- cmd[:match].each do |op|
16
- value = level_cmp_match(op, level)
17
- if value != nil
18
- # level.inf[key].send(:=~, ''/#{value}/i')) # =~ /#{value}/i
19
- matches[op[:name]] = value # level.inf[key]
20
- nmatches += 1
21
- end
22
- end
23
- if cmd[:match].size == nmatches
24
- keyvaluestr = matches.map{|(k,v)|"#{k}=#{v}"}.join(', ')
25
- puts "%s [%s]" % [level.to_s, keyvaluestr]
13
+ all_matches = {}
14
+ nmatches = ReVolt::Util::Matcher.multi(level, cmd[:match], all_matches)
15
+ if nmatches > 0
16
+ keyvaluestr = all_matches.map{|(k,v)|"#{k}=#{v}"}.join(', ')
17
+ puts "%s [%s]" % [level.inspect, keyvaluestr]
26
18
  end
27
19
  end
28
20
  end
29
21
 
30
- def level_cmp_match(op, level)
31
- return false if !level || !op
32
- args1 = [op[:method]]
33
- args2 = [op[:cmp], op[:expect]]
34
- obj = level
35
- ret_value = true
36
- if op[:method] == :inf
37
- lvlinfval = level.inf[op[:infkey]]
38
- return false if !lvlinfval
39
- obj = lvlinfval
40
- # op[:method] = [op[:infcmp], op[:infval]]
41
- ret_value = lvlinfval
42
-
43
- # Check if comparing to a number (if non digits, not a number)
44
- #ReVolt::Logger.debug("Second class: #{s.class} value: #{s}")
45
- case
46
- when op[:infcmp] == :=~:
47
- ReVolt::Logger.debug("Its a regexp")
48
- # If a regular expression, do the comparison to reg exp match's return value
49
- obj = lvlinfval =~ /#{op[:infval]}/i
50
- # If the return value is integer, a match was found
51
- args1 = [:is_a?, Integer]
52
- when !(op[:infval] =~ /[^\d]/)
53
- ReVolt::Logger.debug("Its a number")
54
- # If comparing to an integer
55
- # Return false unless both comparisons to integer
56
- return false if lvlinfval =~ /[^\d]/
57
- # Do the comparison as integers
58
- obj = lvlinfval.to_i
59
- args1 = [op[:infcmp], op[:infval].to_i]
60
- else
61
- # obj = lvlinfval
62
- args1 = [op[:infcmp], op[:infval]]
63
- end
64
- end
65
-
66
- ReVolt::Logger.debug("Sending operation #{args1} to object #{obj.class} with value #{obj}")
67
- ReVolt::Logger.debug("Args2: #{args2}") # Sending operation #{args} to object #{obj.class}")
68
- # ret = obj.send(infcmp, infval) # *args)
69
- # ret = (obj =~ infval)
70
- # ReVolt::Logger.debug("Return value: #{ret}, class: #{ret.class}")
71
- if (obj.send(*args1)).send(*args2)
72
- ReVolt::Logger.debug("Returning value #{ret_value}")
73
- return ret_value
74
- end
75
- nil
76
- end
77
22
 
78
23
  class CmdArguments < Hash
24
+ include ReVolt::Util::CommonCmdArgs
25
+
79
26
  def initialize(cmd)
80
27
  super()
81
28
 
82
- inf_matches = []
83
29
  self[:base] = ReVolt::Info::path
84
-
30
+ matchstrs = []
85
31
  opts = OptionParser.new do |opts|
86
- opts.banner = "Usage: #$0 [options] [name_match]\n"
32
+ opts.banner = "Usage: #$0 [options] [name_match ...]\n"
87
33
 
88
34
  opts.separator "Examples:"
89
35
  opts.separator " Searches for all tracks which name contains temple:"
@@ -92,92 +38,58 @@ class CmdArguments < Hash
92
38
  opts.separator " #$0 -m 'farclip>31000,startgrid=3'"
93
39
  opts.separator " All custom tracks that can be raced in reversed mode"
94
40
  opts.separator " #$0 -m 'reverse?,custom?'"
41
+ opts.separator " All full custom tracks or custom tracks that can be raced in reverse"
42
+ opts.separator " #$0 -m 'full_custom?' -m 'reverse?,custom?'"
95
43
 
96
44
  opts.separator ""
97
- opts.separator "Operators:"
98
- opts.separator " <op> in the options can be replaced with:"
99
- opts.separator " = equality"
100
- opts.separator " < smaller than"
101
- opts.separator " > larger than"
102
- opts.separator " =~ regular expression match"
103
- opts.separator ""
104
- opts.separator " The match can include also the following toggles:"
105
- opts.separator " reversed?"
106
- opts.separator " stock?"
107
- opts.separator " custom?"
108
-
45
+ match_help(opts)
109
46
  opts.separator ""
110
47
 
111
48
  opts.separator "Options:"
112
49
 
113
- opts.on('-m', '--match [key<op>match[,key2<op>match2[,...]]]', Array,
114
- 'Matches values in the .inf file with operator') do |value|
115
- # 'Match to values in level inf files') do |value|
116
- inf_matches = value
50
+ on_match(opts) do |value|
51
+ matchstrs << value
117
52
  end
53
+
118
54
  opts.on('-d', '--debug',
119
55
  'Outputs debug information') do
120
56
  self[:debug] = true
121
57
  end
122
- opts.on('-b', '--base [DIR]',
123
- 'RV base path. Defaults to RV installation') do |value|
58
+
59
+ on_base_path(opts) do |value|
124
60
  self[:base] = value
125
61
  end
126
62
 
127
63
  opts.on_tail('-h', '--help',
128
- 'Display this help and exit') do
64
+ 'Display full help and exit') do
129
65
  puts opts
130
- exit(1)
66
+
67
+ exit(0)
131
68
  end
132
69
  end
133
70
 
134
71
  opts.parse!(cmd)
135
72
 
136
- if cmd.size != 0
137
- inf_matches << '%s=~%s' % ['name', cmd[0]]
73
+ cmd.each do |n|
74
+ matchstrs << '%s=~%s' % ['name', n]
138
75
  end
139
76
 
140
77
  # If input file not specified, at least start date is required
141
- if inf_matches.size == 0
142
- puts "Error: no matches specified"
143
- STDERR.puts opts
78
+ if matchstrs.size == 0
79
+ puts "Error: no matches specified\n"
80
+ STDERR.puts opts.banner
144
81
  exit 1
145
82
  end
146
83
 
147
- self[:match] = []
148
- inf_matches.each do |keyvalue|
149
- op = {
150
- :cmp => :==,
151
- :expect => true
152
- }
153
- case keyvalue
154
- when 'reverse?','stock?','custom?'
155
- op.merge!({
156
- :method => keyvalue.to_sym,
157
- :name => keyvalue
158
- })
159
- else
160
- ((key,cmp,value)) = keyvalue.scan(/(\w+)(=~|<|>|=)(.+)/)
161
- # (key, value) = keyvalue.split('=')
162
- if key.nil?
163
- STDERR.puts "Error: invalid match '#{keyvalue}', should be key<cmp>value"
164
- STDERR.puts " where cmp is comparison type, one of: =,<,> or =~"
165
- STDERR.puts " Example: name=~temple"
166
- STDERR.puts opts
167
- exit 1
168
- end
169
-
170
- cmp = '==' if cmp == '='
171
- op.merge!({
172
- :method => :inf,
173
- :infkey => key.downcase.to_sym,
174
- :infcmp => cmp.to_sym,
175
- :infval => value,
176
- :name => key,
177
- })
178
-
84
+ begin
85
+ # Create the matcher objects
86
+ self[:match] = []
87
+ for m in matchstrs
88
+ self[:match] << ReVolt::Util::Matcher.parse(m)
179
89
  end
180
- self[:match] << op
90
+ rescue ReVolt::Util::Matcher::ParseError => e
91
+ STDERR.puts e.errstr
92
+ exit(1)
181
93
  end
182
94
  end
183
95
  end
@@ -0,0 +1,118 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ require 'optparse'
4
+ require 'pp'
5
+ require 'revolt'
6
+ require 'set'
7
+
8
+ def main
9
+ @cmd = CmdArguments.new(ARGV)
10
+ ReVolt::Logger.enable if @cmd[:debug]
11
+ levels = ReVolt::Levels.at(@cmd[:base])
12
+ levels.each_level do |level|
13
+ nmatches = ReVolt::Util::Matcher.multi(level, @cmd[:match])
14
+ if nmatches > 0
15
+ output_info(level)
16
+ end
17
+ end
18
+ end
19
+
20
+ def output_info(l)
21
+ puts "%s (%s)" % [l.name, l.id]
22
+ [
23
+ [:stock?, ' - This is a stock level'],
24
+ [:custom?, ' - This is a custom level'],
25
+ [:reverse?, ' - Can be played in reverse'],
26
+ [:full_custom?,' - Can be played in Full Custom mode']
27
+ ].each do |(question, msg)|
28
+ puts msg if l.send(question)
29
+ end
30
+
31
+ if @cmd[:all]
32
+ puts "Elements from info file:"
33
+ l.inf.sort{|a,b| a.to_s <=> b.to_s}.each do |key, value|
34
+ value = l.inf[key]
35
+ puts "%-20s %s" % [key.to_s.upcase, value]
36
+ end
37
+ end
38
+
39
+ puts ""
40
+ end
41
+
42
+ class CmdArguments < Hash
43
+ include ReVolt::Util::CommonCmdArgs
44
+
45
+ def initialize(cmd)
46
+ super()
47
+
48
+ self[:base] = ReVolt::Info::path
49
+ self[:all] = false
50
+
51
+ matchstrs = []
52
+ opts = OptionParser.new do |opts|
53
+ opts.banner = "Usage: #$0 [options] [name_match ...]\n"
54
+ opts.separator ""
55
+ opts.separator "Outputs information about matching levels from their inf file."
56
+ opts.separator ""
57
+ opts.separator "Examples:"
58
+ opts.separator " Information about levels whose name includes temple:"
59
+ opts.separator " #$0 temple"
60
+
61
+ opts.separator ""
62
+ match_help(opts)
63
+ opts.separator ""
64
+
65
+ opts.separator "Options:"
66
+
67
+ on_match(opts) do |value|
68
+ matchstrs << value
69
+ end
70
+ opts.on('-d', '--debug',
71
+ 'Outputs debug information') do
72
+ self[:debug] = true
73
+ end
74
+ opts.on('-a', '--all',
75
+ 'All information from the level output') do
76
+ self[:all] = true
77
+ end
78
+
79
+ on_base_path(opts) do |value|
80
+ self[:base] = value
81
+ end
82
+
83
+ opts.on_tail('-h', '--help',
84
+ 'Display full help and exit') do
85
+ puts opts
86
+
87
+ exit(0)
88
+ end
89
+ end
90
+
91
+ opts.parse!(cmd)
92
+
93
+ cmd.each do |n|
94
+ matchstrs << '%s=~%s' % ['name', n]
95
+ end
96
+
97
+ # If input file not specified, at least start date is required
98
+ if matchstrs.size == 0
99
+ puts "Error: no matches specified\n"
100
+ STDERR.puts opts.banner
101
+ exit 1
102
+ end
103
+
104
+ begin
105
+ # Create the matcher objects
106
+ self[:match] = []
107
+ for m in matchstrs
108
+ self[:match] << ReVolt::Util::Matcher.parse(m)
109
+ end
110
+ rescue ReVolt::Util::Matcher::ParseError => e
111
+ STDERR.puts e.errstr
112
+ exit(1)
113
+ end
114
+ end
115
+ end
116
+
117
+
118
+ main
@@ -40,10 +40,10 @@ def main
40
40
  tmplevels.each do |level|
41
41
  # Install only levels that do not exist already
42
42
  if !@cmd[:force] && @levels.member?(level)
43
- puts "Skipping installing of " + level.to_s + " because it exists"
43
+ puts "Skipping installing of " + level.inspect + " because it exists"
44
44
  @summary[:exists] << level
45
45
  else
46
- puts "Installing level " + level.to_s
46
+ puts "Installing level " + level.inspect
47
47
  level.move_to @levels
48
48
  @summary[:installed] << level
49
49
  end
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ require 'optparse'
4
+ require 'pp'
5
+ require 'revolt'
6
+ require 'set'
7
+
8
+ def main
9
+ pkg_levs = []
10
+ cmd = CmdArguments.new(ARGV)
11
+ ReVolt::Logger.enable if cmd[:debug]
12
+ levels = ReVolt::Levels.at(cmd[:base])
13
+ levels.each_level do |level|
14
+ all_matches = {}
15
+ nmatches = ReVolt::Util::Matcher.multi(level, cmd[:match], all_matches)
16
+ if nmatches > 0
17
+ keyvaluestr = all_matches.map{|(k,v)|"#{k}=#{v}"}.join(', ')
18
+ pkg_levs << level
19
+ puts "%s [%s]" % [level.inspect, keyvaluestr]
20
+ end
21
+ end
22
+
23
+ if pkg_levs.size > 0
24
+ puts ''
25
+ puts "Creating packages of #{pkg_levs.size} matching levels"
26
+ pkg_levs.each do |l, match|
27
+ pkgfname = "%s.%s" % [l.id.to_s, 'zip']
28
+ puts "Creating package %s from %s" % [pkgfname, l.inspect]
29
+ l.package(pkgfname)
30
+ end
31
+ else
32
+ puts "No matching levels"
33
+ end
34
+ end
35
+
36
+
37
+ class CmdArguments < Hash
38
+ include ReVolt::Util::CommonCmdArgs
39
+
40
+ def initialize(cmd)
41
+ super()
42
+
43
+ self[:base] = ReVolt::Info::path
44
+ self[:force] = false
45
+
46
+ matchstrs = []
47
+ opts = OptionParser.new do |opts|
48
+ opts.banner = "Usage: #$0 [options] [levelid ...]\n"
49
+ opts.separator ""
50
+ opts.separator "Creates packages of levels. Each packaged level will be"
51
+ opts.separator "named by its id"
52
+ opts.separator ""
53
+ opts.separator "Examples:"
54
+ opts.separator " Package a level that is in RV levels sub-directory temple:"
55
+ opts.separator " #$0 temple"
56
+ opts.separator " Packages all custom tracks that can be raced in reverse:"
57
+ opts.separator " #$0 -m 'custom?,reverse?'"
58
+
59
+ opts.separator ""
60
+ match_help(opts)
61
+ opts.separator ""
62
+
63
+ opts.separator "Options:"
64
+
65
+ on_match(opts) do |value|
66
+ matchstrs << value
67
+ end
68
+ opts.on('-d', '--debug',
69
+ 'Outputs debug information') do
70
+ self[:debug] = true
71
+ end
72
+
73
+ on_base_path(opts) do |value|
74
+ self[:base] = value
75
+ end
76
+
77
+ opts.on_tail('-h', '--help',
78
+ 'Display full help and exit') do
79
+ puts opts
80
+
81
+ exit(0)
82
+ end
83
+ end
84
+
85
+ opts.parse!(cmd)
86
+
87
+ cmd.each do |pkgid|
88
+ matchstrs << 'id=%s' % ['name', cmd[0]]
89
+ end
90
+
91
+ # If input file not specified, at least start date is required
92
+ if matchstrs.size == 0
93
+ puts "Error: no matches specified\n"
94
+ STDERR.puts opts.banner
95
+ exit 1
96
+ end
97
+
98
+ begin
99
+ # Create the matcher objects
100
+ self[:match] = []
101
+ for m in matchstrs
102
+ self[:match] << ReVolt::Util::Matcher.parse(m)
103
+ end
104
+ rescue ReVolt::Util::Matcher::ParseError => e
105
+ STDERR.puts e.errstr
106
+ exit(1)
107
+ end
108
+ end
109
+ end
110
+
111
+
112
+ main