revolt 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. data/README +150 -0
  2. data/Rakefile +197 -0
  3. data/bin/rv_find_levels.rb +186 -0
  4. data/bin/rv_install_level_urls.rb +191 -0
  5. data/bin/rv_install_levels.rb +76 -0
  6. data/examples/find_rv_track.rb +17 -0
  7. data/examples/install_rv_track.rb +28 -0
  8. data/lib/revolt/args.rb +46 -0
  9. data/lib/revolt/config.rb +5 -0
  10. data/lib/revolt/exceptions.rb +34 -0
  11. data/lib/revolt/fetcher/file_system.rb +31 -0
  12. data/lib/revolt/fetcher/www.rb +117 -0
  13. data/lib/revolt/fetcher.rb +30 -0
  14. data/lib/revolt/info.rb +40 -0
  15. data/lib/revolt/level.rb +298 -0
  16. data/lib/revolt/levels.rb +362 -0
  17. data/lib/revolt/logger.rb +24 -0
  18. data/lib/revolt/package/archive/analyzer/normalized.rb +50 -0
  19. data/lib/revolt/package/archive/analyzer/troubled.rb +97 -0
  20. data/lib/revolt/package/archive/analyzer.rb +34 -0
  21. data/lib/revolt/package/archive/zip/browser.rb +44 -0
  22. data/lib/revolt/package/archive.rb +35 -0
  23. data/lib/revolt/package/exe.rb +10 -0
  24. data/lib/revolt/package/installer/archive.rb +105 -0
  25. data/lib/revolt/package/installer/exe.rb +10 -0
  26. data/lib/revolt/package.rb +64 -0
  27. data/lib/revolt/util/fs_browser.rb +30 -0
  28. data/lib/revolt/util.rb +20 -0
  29. data/lib/revolt.rb +12 -0
  30. data/test/common.rb +78 -0
  31. data/test/fixtures/files/nodirs_track.zip +0 -0
  32. data/test/fixtures/files/readme.txt +1 -0
  33. data/test/fixtures/files/rickyd_track.zip +0 -0
  34. data/test/fixtures/files/standard_multi.zip +0 -0
  35. data/test/fixtures/files/standard_rev_track.zip +0 -0
  36. data/test/fixtures/files/standard_track.zip +0 -0
  37. data/test/fixtures/files/zips_inside.zip +0 -0
  38. data/test/fixtures/rv/gfx/levid.bmp +1 -0
  39. data/test/fixtures/rv/gfx/levid.bmq +1 -0
  40. data/test/fixtures/rv/gfx/levidrev.bmp +1 -0
  41. data/test/fixtures/rv/gfx/levidrev.bmq +1 -0
  42. data/test/fixtures/rv/gfx/tEsT Level.bmp +1 -0
  43. data/test/fixtures/rv/levels/levid/levid.inf +1 -0
  44. data/test/fixtures/rv/levels/levid/levid.w +1 -0
  45. data/test/fixtures/rv/levels/levid/levida.bmp +1 -0
  46. data/test/fixtures/rv/levels/levid/readme.txt +1 -0
  47. data/test/fixtures/rv/levels/levidrev/levidrev.inf +1 -0
  48. data/test/fixtures/rv/levels/levidrev/levidrev.w +1 -0
  49. data/test/fixtures/rv/levels/levidrev/levidreva.bmp +1 -0
  50. data/test/fixtures/rv/levels/levidrev/readme.txt +1 -0
  51. data/test/fixtures/rv/levels/levidrev/reversed/levidrev.cam +1 -0
  52. data/test/fixtures/rv/levels/levidrev/reversed/levidrev.fan +1 -0
  53. data/test/fixtures/rv/levels/levidrev/reversed/levidrev.fin +1 -0
  54. data/test/fixtures/rv/levels/test lEveL/TEst level.inf +4 -0
  55. data/test/fixtures/rv/levels/test lEveL/TeST level.w +1 -0
  56. data/test/fixtures/rv/levels/test lEveL/readme.txt +1 -0
  57. data/test/fixtures/rv/levels/test lEveL/reversed/TEst level.inf +1 -0
  58. data/test/fixtures/rv/levels/test lEveL/reversed/TeST level.w +1 -0
  59. data/test/fixtures/rv/levels/test lEveL/reversed/readme.txt +1 -0
  60. data/test/fixtures/rv/levels/test lEveL/reversed/test LEVELa.bmp +1 -0
  61. data/test/fixtures/rv/levels/test lEveL/test LEVELa.bmp +1 -0
  62. data/test/tc_archive_analyzer.rb +185 -0
  63. data/test/tc_args.rb +55 -0
  64. data/test/tc_args_external.rb +55 -0
  65. data/test/tc_file_system_fetcher.rb +26 -0
  66. data/test/tc_info.rb +23 -0
  67. data/test/tc_level.rb +182 -0
  68. data/test/tc_level_installer.rb +88 -0
  69. data/test/tc_level_installer_external.rb +124 -0
  70. data/test/tc_levels.rb +68 -0
  71. data/test/tc_package_track_installer.rb +174 -0
  72. data/test/ts_base.rb +14 -0
  73. data/test/ts_external.rb +7 -0
  74. metadata +133 -0
@@ -0,0 +1,298 @@
1
+ require 'pathname'
2
+ require 'set'
3
+ require 'revolt/exceptions'
4
+ require 'revolt/logger'
5
+ require 'revolt/util'
6
+ require 'pp'
7
+
8
+ module ReVolt
9
+ # Level contains information about one Re-Volt level.
10
+ # A unique identifier is used which is a symbol from
11
+ # the directory name of the Level.
12
+ #
13
+ # The operations on Level are dynamic in nature, so that
14
+ # information about the Level are loaded only if it is
15
+ # requested. For example the .inf file is loaded only
16
+ # if information from it is required, such as the level's
17
+ # name.
18
+ #
19
+ # Level's identifier is its directory name in lowercase.
20
+ # The identifier passed to the various functions can
21
+ # be:
22
+ # - a lowercase symbol denoting the level name
23
+ # - a Level object, which own identifier is used to find the instance
24
+ # - a string that is the same as the directory name
25
+ #
26
+ # Internally all above identifiers are turned into a lowercase
27
+ # symbol.
28
+ #
29
+ # Usually Level is associated with a parent object, which is
30
+ # Levels. Many of the functions require this association to exist
31
+ # because otherwise the full path to the level directory, and
32
+ # thus its files, can not be determined
33
+ #
34
+ # The
35
+ class Level
36
+ include ReVolt::Logger
37
+
38
+ attr_accessor :parent
39
+ attr_reader :id
40
+
41
+ @@stock_ids = Set.new [
42
+ :nhood1,
43
+ :market2,
44
+ :muse2,
45
+ :garden1,
46
+ :toylite,
47
+ :wild_west1,
48
+ :toy2,
49
+ :ship1,
50
+ :nhood2,
51
+ :muse1,
52
+ :market1,
53
+ :wild_west2,
54
+ :ship2,
55
+
56
+ # Battle tags
57
+ :nhood1_battle,
58
+ :markar,
59
+ :bot_bat,
60
+ :muse_bat,
61
+
62
+ # Stunt track
63
+ :stunts
64
+ ]
65
+
66
+ # Creates a new Level object that has the given identifier
67
+ def initialize(id, args = {})
68
+ @id = ReVolt::Level::to_level_id id
69
+ @parent = args[:parent]
70
+ @name = args[:name]
71
+ @inf = nil
72
+ @loaded = false
73
+ end
74
+
75
+ # Tries to create a level identifier from the passed
76
+ # object.
77
+ def self.to_level_id(id)
78
+ case
79
+ when id.is_a?(ReVolt::Level) then
80
+ ReVolt::Logger::debug("Level::to_level_id: from id Level #{id}")
81
+ id.id
82
+ when id.is_a?(Symbol) then
83
+ ReVolt::Logger::debug("Level::to_level_id: from id Symbol #{id}")
84
+ id
85
+ else
86
+ ReVolt::Logger::debug("Level::to_level_id: default action from id #{id}")
87
+ id.to_s.downcase.to_sym
88
+ end
89
+ end
90
+
91
+ # Returns true if the Level's .inf file has been loaded
92
+ def loaded?
93
+ @loaded
94
+ end
95
+
96
+ # Returns the name of the level
97
+ def name
98
+ reload unless @name
99
+ @name
100
+ end
101
+
102
+ # Returns true if the Level is a stock level.
103
+ # The comparison is done only on the basis of
104
+ # the Level's identifier.
105
+ def stock?
106
+ @@stock_ids.member?(@id)
107
+ end
108
+
109
+ # Inverse of #stock?
110
+ def custom?
111
+ not stock?
112
+ end
113
+
114
+ # Returns true if there is a reversed version of this
115
+ # level. Basically just checks if the track has a
116
+ # reversed folder
117
+ def reverse?
118
+ ensure_parent
119
+ rpath = path + 'reversed'
120
+ rpath.directory?
121
+ end
122
+
123
+ # Returns the files belonging to this level, relative
124
+ # to the Re-Volt base directory. Pathname objects.
125
+ def files
126
+ self.dirs_and_files[1]
127
+ end
128
+
129
+ # Returns the directories belonging to this level,
130
+ # relative to the Re-Volt base directory
131
+ def dirs
132
+ self.dirs_and_files[0]
133
+ end
134
+
135
+ # Returns the files and directories belonging to this
136
+ # level in an array, in first element array of the directories
137
+ # and in second element array of the files. Each file
138
+ # and directory is a Pathname object.
139
+ def dirs_and_files
140
+ ensure_parent
141
+
142
+ # Level directories, and level files
143
+ d = []
144
+ f = []
145
+ # Level globs, and graphics globs
146
+ globs = [path + '**/*',
147
+ gfx_path + ReVolt::Util::caseinsensitiveglob(id.to_s + '.bm?')
148
+ ]
149
+ debug("Level gfx glob: #{globs[1]}")
150
+
151
+ # Creates relative paths to base directory
152
+ globs.each do |g|
153
+ debug("Globbing: #{g}")
154
+ Pathname::glob(g) do |path|
155
+ debug("Inspecting file #{path}")
156
+ addto = path.directory?() ? d : f
157
+ addto << path.relative_path_from(@parent.base_path)
158
+ end
159
+ end
160
+
161
+ debug("Returning #{d} and #{f}")
162
+ [d, f]
163
+ =begin
164
+ # The gfx files then
165
+ debug("Level path: #{path}")
166
+ debug("Level gfx path: #{gfx_path}")
167
+ debug("files: #{f.join(' ')}")
168
+ (f + d).each do |p|
169
+ debug("Level: relative path to RV base from #{p}")
170
+ p = relative_path_from(@parent.base_path)
171
+ debug("Level: relative path result: #{p}")
172
+ end
173
+ r = f.map do |f|
174
+ f.relative_path_from(@parent.base_path)
175
+ end
176
+
177
+ debug("files relative: #{r.join(' ')}")
178
+
179
+ r
180
+ =end
181
+ end
182
+
183
+ def path
184
+ ensure_parent
185
+ @parent.path + @id.to_s
186
+ end
187
+
188
+ # Returns the full name and path of the .inf file
189
+ # of this level.
190
+ def inf_path
191
+ ensure_parent
192
+ path + (@id.to_s + '.inf')
193
+ end
194
+
195
+ # Returns the path to the graphics files of the Level
196
+ def gfx_path
197
+ ensure_parent
198
+ @parent.gfx_path
199
+ end
200
+
201
+ # Convenience function for moving this Level
202
+ # to given Levels. See Levels::move_level_to
203
+ def move_to(levels, args = {})
204
+ ensure_parent
205
+ raise(ArgumentError,
206
+ "Not ReVolt::Levels") unless levels.is_a? ReVolt::Levels
207
+ @parent.move_level_to(id, levels, args)
208
+ end
209
+
210
+ # Convenience function for copying this Level
211
+ # to given Levels. See Levels::copy_level_to
212
+ def copy_to(levels, args = {})
213
+ ensure_parent
214
+ raise(ArgumentError,
215
+ "Not ReVolt::Levels") unless levels.is_a? ReVolt::Levels
216
+ @parent.copy_level_to(id, levels, args)
217
+ end
218
+
219
+ # Convenience function for deleting this Level
220
+ # from given Levels. See Levels::delete
221
+ def delete(args = {})
222
+ ensure_parent
223
+ @parent.delete(id, args)
224
+ end
225
+
226
+ # Reloads the details of the Level (from .inf file)
227
+ def reload
228
+ raise "ReVolt::Levels instance must be specified before Level can be loaded" unless @parent
229
+
230
+ inf_file = Pathname.new(@parent.path)
231
+ inf_file += id.to_s
232
+ inf_file += id.to_s + ".inf"
233
+ debug "Loading inf file: #{inf_file}"
234
+
235
+ if !inf_file.file?
236
+ raise(InfFileNotFoundError.new(@parent, self, inf_file),
237
+ "Inf file not found from #{inf_file}")
238
+ end
239
+
240
+ @inf = {}
241
+ open(inf_file).each do |line|
242
+ # Remove comments:
243
+ debug "Level::reload .inf line: #{line}"
244
+ line.sub!(/;.*/, '')
245
+ if line =~ /^(\w+)\s+([^\s].*)$/
246
+ key, value = $1, $2
247
+ value.rstrip!
248
+ key.downcase!
249
+ # Replace 'string' values with string
250
+ value.gsub!(/'([^']*)'/, '\1')
251
+ @inf[key.intern] = value
252
+ end
253
+ end
254
+
255
+ @name = @inf[:name]
256
+ @loaded = true
257
+ end
258
+
259
+ # Returns a hash of the name/value pairs from
260
+ # the .inf file. The key of the hash is a lowercase
261
+ # symbol of the corresponding entry in the .inf file
262
+ #
263
+ # For example getting the STARTGRID from .inf file:
264
+ # level.inf[:startgrid]
265
+ def inf
266
+ reload unless loaded?
267
+ @inf
268
+ end
269
+
270
+ def <=>(o)
271
+ id === o.id # .to_s.casecmp(o.id.to_s)
272
+ end
273
+
274
+ # Turns the level's id (and possibly name if it has been loaded)
275
+ # to a human readable string
276
+ def to_s
277
+ if loaded?
278
+ "%s (%s)" % [id.to_s, @name]
279
+ else
280
+ id.to_s
281
+ end
282
+ end
283
+
284
+ def to_sym
285
+ id
286
+ end
287
+
288
+ def to_symbol
289
+ id
290
+ end
291
+
292
+ private
293
+ def ensure_parent
294
+ c = caller.first
295
+ raise "ReVolt::Levels instance must be specified before #{c} can be called" unless @parent
296
+ end
297
+ end # Levels
298
+ end # ReVolt
@@ -0,0 +1,362 @@
1
+ require 'pathname'
2
+ require 'fileutils'
3
+ require 'revolt/info'
4
+ require 'revolt/level'
5
+ require 'revolt/package'
6
+ require 'revolt/logger'
7
+ require 'revolt/logger'
8
+ require 'revolt/args'
9
+
10
+ module ReVolt
11
+ # Is used to handle the levels of Re-Volt installation.
12
+ # Possible uses:
13
+ # - Install a track into it
14
+ # - Delete a track from it
15
+ # - Move a track to another level directory
16
+ # - Copy a track to another level directory
17
+ # - Create a new levels directory structure
18
+ # - Iterate through all installed tracks etc.
19
+ #
20
+ # The Levels and their handling is dynamic. In practice
21
+ # this means that the library only loads as much as is
22
+ # needed about the levels to supply the requested information.
23
+ # As an example, at first when the level is created nothing
24
+ # is loaded. When the user requests a certain Level, then
25
+ # all the directories in the levels are read and made into
26
+ # Level classes. But the actual Level's .inf file and detailed
27
+ # information is loaded only when such detailed information
28
+ # is needed such as requesting the name of the level.
29
+ # This makes the operation very fast.
30
+ #
31
+ # The downside is that the Levels can contain entries
32
+ # that are not really tracks, but some garbage files in the
33
+ # levels folder.
34
+ class Levels
35
+ include ReVolt::Logger
36
+ include Enumerable
37
+
38
+ # Pathname objects
39
+ attr_accessor :path, :gfx_path, :base_path
40
+
41
+ # Creates new Levels instance with given Re-Volt
42
+ # base path. The actual levels directory
43
+ # will be in base_rv_path/levels and the
44
+ # graphics files in base_rv_path/gfx
45
+ def initialize(base_rv_path, args = {})
46
+ @base_path = Pathname.new(base_rv_path)
47
+ @path = @base_path + "levels"
48
+ @gfx_path = @base_path + "gfx"
49
+ @path_checked = false
50
+
51
+ @levels = nil
52
+ end
53
+
54
+ # Just another way of saying new.
55
+ #
56
+ # Example: levels = ReVolt::Levels.at('tmpdir/test')
57
+ def self.at(*a)
58
+ new(*a)
59
+ end
60
+
61
+ # Creates new Levels instance of the levels
62
+ # in the standard Re-Volt installation directory.
63
+ # The directory is detected from registry
64
+ def self.installed(args = {})
65
+ new(ReVolt::Info::path, args)
66
+ end
67
+
68
+ # Iterates through every Level in the directory
69
+ # returning it to the block. Note that this
70
+ # method returns all directories in the levels
71
+ # direcotry as a Level object, so if there is
72
+ # garbage directories the returned Level object
73
+ # might throw an exception if the directory is
74
+ # not an actual level.
75
+ #
76
+ # For a bit slower versions that validate the Level
77
+ # before returning it see each_level method.
78
+ def each
79
+ ensure_levels
80
+
81
+ @levels.each do |key, level|
82
+ yield level
83
+ end
84
+ end
85
+
86
+ # Only returns valid levels (those with .inf file),
87
+ # Slower than each because the inf file will be loaded,
88
+ # but if any information from .inf (such as name) is
89
+ # needed anyway there is no difference
90
+ def each_level
91
+ ensure_levels
92
+ @levels.each do |key, level|
93
+ begin
94
+ level.reload # Throws InfFileNotFoundError if not level
95
+ rescue ReVolt::InfFileNotFoundError => e
96
+ debug "Levels::each_level: folder #{e.level.path} is not track"
97
+ else
98
+ yield level
99
+ end
100
+ end
101
+ end
102
+
103
+ # Like each_level but only returns custom levels
104
+ def each_custom
105
+ each_level do |level|
106
+ if level.custom?
107
+ yield level
108
+ end
109
+ end
110
+ end
111
+
112
+ # Like each_level but only returns stock levels
113
+ def each_stock
114
+ each_level do |level|
115
+ if level.stock?
116
+ yield level
117
+ end
118
+ end
119
+ end
120
+
121
+ # Returns a reference to a Level object matching
122
+ # the given level_id. Returns nil if no matching
123
+ # level.
124
+ def [](level_id)
125
+ ensure_levels
126
+ debug("Levels::[]: #{level_id}")
127
+ @levels[ReVolt::Level::to_level_id(level_id)]
128
+ end
129
+
130
+ # Creates the directory needed for levels
131
+ # if it doesn't exist.
132
+ def create_dir_structure
133
+ @base_path.mkdir if !@base_path.exist?
134
+ @path.mkdir if !@path.exist?
135
+ @gfx_path.mkdir if !@gfx_path.exist?
136
+
137
+ ensure_paths
138
+ end
139
+
140
+ # Installs a track package to the Levels.
141
+ # The package can be on local filesystem or
142
+ # an external one for which a Fetcher can be
143
+ # created. So for example an URL can be given
144
+ # and the track is downloaded and installed.
145
+ def install(src, args = {})
146
+ usesrc = src.chomp if src.is_a? String
147
+ usesrc ||= src
148
+ fetcher = Fetcher.for(usesrc, args)
149
+ fetcher.run(args)
150
+
151
+ ids = ReVolt::Package::install_track(fetcher.target, @base_path, args)
152
+ end
153
+
154
+ # A convenience function for #install. Extracts all urls from
155
+ # a string and calls install on them. Useful for example
156
+ # for installing all track URLs pasted to a chat
157
+ def install_urls(src, args = {})
158
+ # Returns nil if any call to install returns
159
+ # nil as a sign that not all package installers
160
+ # could provide ids of installed tracks
161
+ ids = []
162
+ URI.extract(src, ['http', 'https']) do |url|
163
+ debug "Levels::install_urls: #{url}"
164
+ url = ReVolt::call_changer_arg(ReVolt::ARG_INSTALL_URL, args, url)
165
+ ReVolt::arg_ex_watch(ReVolt::ARG_INSTALL_URL_EX, args, url) {
166
+ debug "Levels::install_urls2: #{url}"
167
+ if url
168
+ id_ret = install(url, args)
169
+ debug "Levels::install_urls: install returned ids #{id_ret}"
170
+ ids = nil unless id_ret
171
+ ids += id_ret if id_ret && ids
172
+ end
173
+ }
174
+ end
175
+ ids
176
+ end
177
+
178
+ # Returns true if the level exists
179
+ def member?(level)
180
+ ensure_levels
181
+
182
+ ret = @levels.has_key?(ReVolt::Level::to_level_id(level))
183
+ end
184
+ def include?(*args); member?(*args); end
185
+
186
+ # Adds a Level instance object. Doesn't copy any files.
187
+ def add(level, args = {})
188
+ ensure_levels
189
+ raise ArgumentError, "not level: #{level}" unless level.is_a? ReVolt::Level
190
+ raise "Already exists #{level}" if @levels.has_key?(level.id)
191
+ @levels[level.id] = level
192
+ level.parent = self
193
+ debug("Levels add: #{level.id}")
194
+ level
195
+ end
196
+
197
+ # Just removes the level from Levels, without deleting the
198
+ # files on filesystem
199
+ def remove(idpar, args = {})
200
+ ensure_levels
201
+
202
+ id = ReVolt::Level::to_level_id(idpar)
203
+ raise "Levels::remove: path #{path} #{idpar} don't exist" unless @levels.has_key?(id)
204
+ level = @levels[id]
205
+
206
+ debug("Level class: #{level.class}")
207
+ @levels.delete(level.id)
208
+ level.parent = nil
209
+ debug("Levels removed: #{level.id}")
210
+ level
211
+ end
212
+
213
+ # Removes from Levels and deletes the files
214
+ def delete(id_orig, args = {})
215
+ level = remove(id_orig)
216
+ begin
217
+ # Remove takes away parent association, so restore it
218
+ # temporarily so that files can be deleted
219
+ level.parent = self
220
+ (dirs, files) = level.dirs_and_files
221
+ files.each do |file|
222
+ src_file = base_path + file
223
+ debug("Deleting file #{src_file}")
224
+ src_file.delete
225
+ end
226
+ dirs.each do |dir|
227
+ src_dir = base_path + dir
228
+ debug("Deleting directory #{dir}")
229
+ src_dir.rmdir
230
+ end
231
+ debug("Deleting dir #{level.path}")
232
+ level.path.rmdir
233
+ ensure
234
+ level.parent = nil
235
+ end
236
+ end
237
+
238
+ # Copies a Level identified by level_id and all its files to
239
+ # another Levels object. If matching Level did not exist, raises
240
+ # InvalidLevelIdError
241
+ def copy_level_to(level_id, levels, args = {})
242
+ ensure_levels
243
+
244
+ level_files_to(level_id, levels, args) do |file, dst_dir|
245
+ debug("Levels: copying file #{file} to #{dst_dir}")
246
+ FileUtils.cp(file, dst_dir)
247
+ end
248
+
249
+ id = ReVolt::Level::to_level_id(level_id)
250
+ levels.add(ReVolt::Level.new(id))
251
+ end
252
+
253
+ # Moves a Level identified by level_id and all its files to
254
+ # another Levels object. If matching Level did not exist, raises
255
+ # InvalidLevelIdError
256
+ def move_level_to(id_orig, levels, args = {})
257
+ ensure_levels
258
+
259
+ level_files_to(id_orig, levels, args) do |file, dst_dir|
260
+ debug("Levels: moving file #{file} to #{dst_dir}")
261
+ FileUtils.mv(file, dst_dir)
262
+ end
263
+
264
+ id = ReVolt::Level::to_level_id(id_orig)
265
+ level = @levels[id]
266
+
267
+ # Remove the source level directory and any other directory
268
+ # possibly remaining
269
+ level.path.rmtree if level.path.exist?
270
+ remove(level)
271
+ levels.add(level)
272
+ end
273
+
274
+ # Reloads all the levels. Essentially discards the Level objects
275
+ # that have been loaded. This means that the references you had
276
+ # before reload to Level objects should not be used anymore
277
+ def reload
278
+ @levels = nil
279
+ end
280
+
281
+ # Number of levels
282
+ def size
283
+ ensure_levels
284
+ @levels.size
285
+ end
286
+ def length
287
+ size
288
+ end
289
+
290
+ def level_files_to(id_orig, levels, args = {}) # :nodoc:
291
+ raise ArgumentError unless levels.respond_to?(:base_path)
292
+ # raise ArgumentError unless levels.respond_to?(:add)
293
+ # raise ArgumentError unless levels.respond_to?(:remove)
294
+
295
+ ensure_levels
296
+
297
+ id = ReVolt::Level::to_level_id(id_orig)
298
+
299
+ # If level already exists in target
300
+ # then remove it to replace with the new instance
301
+ if levels.member?(id)
302
+ levels.remove(id)
303
+ end
304
+
305
+ raise(InvalidLevelIdError,
306
+ "No level for id #{id_orig}") unless id && @levels.has_key?(id)
307
+ level = @levels[id]
308
+
309
+ (dirs, files) = level.dirs_and_files
310
+
311
+ # Prepare needed directories
312
+ dirs.each do |p|
313
+ tp = levels.base_path + p
314
+ debug "Levels: preparing path #{tp}"
315
+ tp.mkpath
316
+ end
317
+
318
+ # The actual level directory is not with the
319
+ # above command, so create it if necessary
320
+ level_dir = levels.path + level.id.to_s
321
+ level_dir.mkdir if !level_dir.directory?
322
+
323
+ dst_path = levels.base_path
324
+ debug("Levels dst path: #{dst_path}")
325
+ level.files.each do |file|
326
+ src_file = base_path + file
327
+ debug("File parent: #{file.parent}")
328
+ dst_dir = dst_path + file.parent
329
+ debug("Levels: dst dir: #{dst_dir}")
330
+ dst_dir.mkpath unless dst_dir.exist?
331
+ yield(src_file, dst_dir)
332
+ end
333
+
334
+ id
335
+ end
336
+
337
+ private
338
+ def ensure_paths
339
+ return if @path_checked
340
+ raise IOError, "#{@path} does not exist" if !@path.exist?
341
+ raise IOError, "#{@path} is not dir" if !@path.directory?
342
+ raise IOError, "#{@gfx_path} does not exist" if !@gfx_path.exist?
343
+ raise IOError, "#{@gfx_path} is not dir" if !@gfx_path.directory?
344
+ @path_checked = true
345
+ end
346
+
347
+ def ensure_levels
348
+ ensure_paths
349
+
350
+ return if @levels
351
+
352
+ debug("Levels: finding all from #{path}")
353
+ @levels = {}
354
+ @path.children.select {|f| f.directory?}.each do |path_level|
355
+ id = path_level.basename
356
+ level = Level.new(id, { :parent => self })
357
+ add(level)
358
+ end
359
+ end
360
+
361
+ end # Levels
362
+ end # ReVolt
@@ -0,0 +1,24 @@
1
+ require 'logger'
2
+ require 'revolt/config'
3
+
4
+ module ReVolt
5
+ module Logger
6
+ # if Config::LOGGING
7
+ # puts "Initializing logging"
8
+ #end
9
+ @@log = nil
10
+ module_function
11
+ def enable
12
+ @@log = ::Logger.new($stderr) unless @@log
13
+ end
14
+ def disable
15
+ @@log = nil
16
+ end
17
+
18
+ def debug(*a)
19
+ @@log.debug(*a) if @@log
20
+ end
21
+ end
22
+ end
23
+
24
+