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,50 @@
1
+ require 'revolt/logger'
2
+
3
+ module ReVolt; module Package; module Archive; end; end; end
4
+
5
+ module ReVolt::Package::Archive::Analyzer
6
+ class Normalized
7
+ include ReVolt::Logger
8
+ def track_files(browser, args = {})
9
+ a = {}
10
+ browser.each do |entry|
11
+ case entry.to_s
12
+ when /levels\/([^\/]+)\/([^\/]+)\.inf$/i then
13
+ id, id2 = $1.downcase, $2.downcase
14
+ if id == id2
15
+ a[:inf] ||= {}
16
+ a[:inf][id] ||= entry
17
+
18
+ a[:level] ||= {}
19
+ a[:level][id] ||= []
20
+ a[:level][id] << entry
21
+ else
22
+ ReVolt::Logger::debug "Differing ids: #{id}, #{id2}"
23
+ end
24
+ when /levels\/([^\/]+)\/([^\/]+)$/i then
25
+ id = $1.downcase
26
+ a[:level] ||= {}
27
+ a[:level][id] ||= []
28
+ a[:level][id] << entry
29
+ when /levels\/([^\/]+)\/reversed\/([^\/]+)$/i then
30
+ id = $1.downcase
31
+ a[:reversed] ||= {}
32
+ a[:reversed][id] ||= []
33
+ a[:reversed][id] << entry
34
+ when /gfx\/(.*)\.bm[pq]/i then
35
+ id = $1.downcase
36
+ a[:gfx] ||= {}
37
+ a[:gfx][id] ||= []
38
+ a[:gfx][id] << entry
39
+ when ReVolt::Package::installer_class_for(entry.to_s)
40
+ a[:possible] ||= []
41
+ a[:possible] << entry
42
+ end
43
+
44
+ ReVolt::Logger::debug "Normalized: #{entry}"
45
+ end
46
+ a
47
+ end
48
+ end # Analyzer::Normalized
49
+
50
+ end # ReVolt::Package::Archive
@@ -0,0 +1,97 @@
1
+ require 'set'
2
+
3
+ require 'revolt/logger'
4
+
5
+ module ReVolt; module Package; module Archive; end; end; end
6
+
7
+ module ReVolt::Package::Archive::Analyzer
8
+ class Troubled
9
+ include ReVolt::Logger
10
+
11
+ def initialize possible_infs
12
+ # Map possible infs to lower case string ids
13
+ @id_set = Set.new
14
+ possible_infs.each do |inf|
15
+ id = File.basename inf.to_s.downcase, '.inf'
16
+ debug "TroubleTrack Inf: " + inf.to_s
17
+ debug "TroubleTrack Inf id: " + id
18
+ @id_set.add id
19
+ end
20
+ end
21
+
22
+ def track_files(browser, args = {})
23
+ a = {}
24
+ browser.each do |entry|
25
+ debug "TroubleTrack #{entry}"
26
+ next unless entry.to_s =~ /([^\/]+)$/i
27
+ file = $1.downcase
28
+ id = @id_set.find do |id|
29
+ # Try replacing the start of the match
30
+ # with the each known id and see if the
31
+ # result is a match
32
+ filetry = file.dup
33
+ filetry[0...id.length] = id
34
+ debug("Comparing #{filetry} to original #{file}")
35
+ filetry == file
36
+ end
37
+
38
+ if id == nil
39
+ if ReVolt::Package::installer_class_for(entry.to_s)
40
+ a[:possible] ||= []
41
+ a[:possible] << entry
42
+ debug "Possible package #{entry}"
43
+ next
44
+ elsif @id_set.size == 1
45
+ # If there is only one .inf file in the directory,
46
+ # put unrecognized files to level category. This
47
+ # way misc files such as "readme.txt" end up somewhere..
48
+ # Of course if the package includes more than one .inf
49
+ # then it is impossible to know where to put the files.
50
+ debug "Unknown entry #{entry}, " +
51
+ "but only one possible track in package"
52
+ id = @id_set.to_a[0]
53
+ else
54
+ debug "Could not use file #{entry}"
55
+ next
56
+ end
57
+ end
58
+
59
+ # If gotten this far, the file belongs to some track
60
+ # category for the id. Simple rules:
61
+ # 1. if reversed found before the name, there it goes
62
+ # 2. if it is id.bmp or id.bmq, to gfx it goes
63
+ # 3. if it is an .inf file, to :inf and :level it goes
64
+ # 3. otherwise to level it goes
65
+
66
+ idrex = Regexp.escape id
67
+ debug "filerex: #{idrex}"
68
+ category = case entry.to_s
69
+ when /reversed\/[^\/]+$/i: :reversed
70
+ when /#{idrex}\.bm[pq]$/i: :gfx
71
+ when /#{idrex}\.inf$/i: :inf
72
+ else :level
73
+ end
74
+
75
+ begin
76
+ debug "TroubleTrack: category #{category} for file #{entry}"
77
+ a[category] ||= {}
78
+
79
+ if category == :inf
80
+ a[category][id] = entry
81
+ else
82
+ a[category][id] ||= []
83
+ a[category][id] << entry
84
+ end
85
+
86
+ begin
87
+ # Redo the loop to add to levels also if it was an inf file
88
+ category = :level
89
+ redo
90
+ end if category == :inf
91
+ end until true
92
+
93
+ end
94
+ a
95
+ end
96
+ end # Analyzer::Troubled
97
+ end # module ReVolt::Package::Archive
@@ -0,0 +1,34 @@
1
+ require 'revolt/package/archive/zip/browser'
2
+ require 'revolt/package/archive/analyzer/normalized'
3
+ require 'revolt/package/archive/analyzer/troubled'
4
+
5
+ module ReVolt; module Package; module Archive; end; end; end
6
+
7
+ module ReVolt::Package::Archive
8
+ module Analyzer
9
+
10
+ module_function
11
+
12
+ def for_track(browser, args = {})
13
+ looks_like = Set.new
14
+ possible_infs = Set.new
15
+
16
+ browser.each do |entry|
17
+ case entry.to_s
18
+ when /levels\//i: looks_like.add :normalized
19
+ when /.inf$/i: possible_infs.add entry
20
+ end
21
+ end
22
+
23
+ case
24
+ when looks_like.member?(:normalized): return Normalized.new
25
+ else
26
+ return Troubled.new(possible_infs.to_a)
27
+ end
28
+
29
+ raise "err"
30
+ nil
31
+ end
32
+
33
+ end # module
34
+ end # module
@@ -0,0 +1,44 @@
1
+ # require 'delegate'
2
+ require 'rubygems'
3
+ require 'zip/zip'
4
+ require 'zip/zipfilesystem'
5
+ require 'set'
6
+ require 'pp'
7
+
8
+ require 'revolt/logger'
9
+
10
+ module ReVolt; module Package; module Archive; end; end; end
11
+
12
+ module ReVolt::Package::Archive::Zip
13
+ class Browser < Zip::ZipFile
14
+ include ReVolt::Logger
15
+
16
+ def self.open(file, &block)
17
+ me = new(file)
18
+ ReVolt::Logger::debug("1Zip::Browser::open me: #{me.class}, #{file}")
19
+
20
+ if block
21
+ begin
22
+ block.call(me)
23
+ rescue
24
+ me.close
25
+ raise
26
+ end
27
+ end
28
+ end
29
+
30
+ def copy(entry, target, args = {})
31
+ debug("Copying from zip #{entry} -> #{target}")
32
+ if File.directory?(target) then
33
+ target = Pathname.new(target) + File.basename(entry.to_s)
34
+ debug("Copying2 from zip #{entry} -> #{target}")
35
+ end
36
+
37
+ # extract throws an error if the target file exists, so remove it
38
+ # if that is so
39
+ File.unlink(target) if File.exist?(target)
40
+ entry.extract(target, &p)
41
+ end
42
+ end # Zip
43
+ end # ReVolt::Package::Zip
44
+
@@ -0,0 +1,35 @@
1
+ require 'revolt/package/archive/zip/browser'
2
+
3
+ module ReVolt; module Package; module Archive; end; end; end
4
+
5
+ module ReVolt::Package
6
+ module Archive
7
+
8
+ module_function
9
+
10
+ def browser_class_for(file, args = {})
11
+ case file.to_s
12
+ when /.zip$/i : ReVolt::Package::Archive::Zip::Browser
13
+ else
14
+ nil
15
+ end
16
+ end
17
+
18
+ =begin
19
+ def browser_for(file, args = {})
20
+ klass = browser_class_for(file, args)
21
+ klass.new if klass
22
+ end
23
+ =end
24
+
25
+ def open(file, args = {}, &block)
26
+ browser = browser_class_for(file, args)
27
+
28
+ raise "Can not handle archive #{file}" unless browser
29
+
30
+ browser.open(file, &block)
31
+ end
32
+ end
33
+
34
+ end
35
+
@@ -0,0 +1,10 @@
1
+ require 'revolt/package/archive/zip/browser'
2
+
3
+ module ReVolt; module Package; module Archive; end; end; end
4
+
5
+ module ReVolt::Package
6
+ module Exe
7
+
8
+ end
9
+ end
10
+
@@ -0,0 +1,105 @@
1
+ require 'pathname'
2
+
3
+ require 'revolt/logger'
4
+ require 'revolt/package/archive'
5
+ require 'revolt/package/archive/analyzer'
6
+ require 'revolt/exceptions'
7
+
8
+ module ReVolt; module Package; module Installer; end; end; end
9
+
10
+ module ReVolt::Package::Installer
11
+ class Archive
12
+ include ReVolt::Logger
13
+
14
+ # Convenience function to create an archive
15
+ # installer for file if can handle it
16
+ def self.create_for(file_name, args = {})
17
+ return new() if handles?(file_name, args)
18
+ end
19
+
20
+ def self.handles?(file_name, args = {})
21
+ if ReVolt::Package::Archive::browser_class_for(file_name, args)
22
+ return true
23
+ end
24
+ false
25
+ end
26
+
27
+ def install_track(source, rv_target, args = {})
28
+ @args = args
29
+ inst_ids = []
30
+ ReVolt::Package::Archive::open(source, args) do |browser|
31
+ analyzer = ReVolt::Package::Archive::Analyzer::for_track(browser, args)
32
+ files = analyzer.track_files(browser, args)
33
+
34
+ if files[:inf] && files[:inf].size > 0 then
35
+ inst_ids = install_analyzed_files(files, rv_target, browser)
36
+ elsif files[:possible] && files[:possible].size > 0
37
+ inst_ids = install_try_possible_files(files, rv_target, browser)
38
+ end
39
+ if inst_ids.size.zero?
40
+ raise ReVolt::PackageUnknownError.new("Can't find track " +
41
+ "files from #{source}")
42
+ end
43
+ end # browser.open
44
+ inst_ids
45
+ end
46
+
47
+ private
48
+ def install_analyzed_files(files, rv_target, browser)
49
+ raise "No inf in install_analyzed_files" unless files[:inf]
50
+
51
+ files[:inf].each do |(id,file)|
52
+ debug("Inf (#{id}): #{file}")
53
+
54
+ install_analyzed_section(files, rv_target, browser, id, :level)
55
+ install_analyzed_section(files, rv_target, browser, id, :gfx)
56
+ install_analyzed_section(files, rv_target, browser, id, :reversed)
57
+ end
58
+
59
+ return files[:inf].keys
60
+ end
61
+
62
+ def install_analyzed_section(files, rv_target, browser, id, section)
63
+ path = Pathname.new(rv_target.to_s)
64
+ path += case section
65
+ when :gfx then 'gfx'
66
+ when :level then "levels/#{id}"
67
+ when :reversed then "levels/#{id}/reversed"
68
+ end
69
+
70
+ debug(" #{section}: to path #{path}")
71
+
72
+ if files[section] && files[section][id]
73
+ Dir.mkdir(path) unless File.directory?(path)
74
+
75
+ files[section][id].each do |file|
76
+ debug(" #{section}: #{file}")
77
+ browser.copy(file, path, @args)
78
+ end
79
+ end
80
+ end
81
+
82
+ def install_try_possible_files(files, rv_target, browser)
83
+ inst_ids = []
84
+ files[:possible].each do |file|
85
+ debug("Trying to install a possible file: #{file}")
86
+ # Copy as a temporary file and try to install it
87
+ tmpfile = ReVolt::Util::tmpdir
88
+ tmpfile += File.basename(file.to_s)
89
+ browser.copy(file, tmpfile)
90
+ begin
91
+ tmp_ids = ReVolt::Package.install_track(tmpfile, rv_target, @args)
92
+ inst_ids += tmp_ids
93
+ rescue ReVolt::PackageUnknownError => e
94
+ debug("The tried file #{tmpfile} did not seem to be track: #{e}")
95
+ ensure
96
+ debug("Deleting temporary file #{tmpfile}")
97
+ tmpfile.delete
98
+ end
99
+ end
100
+ debug("Archive: installed %d from %d possibles" %
101
+ [inst_ids.size, files[:possible].size])
102
+ inst_ids
103
+ end
104
+ end # Archive
105
+ end # ReVolt::Package::Installer
@@ -0,0 +1,10 @@
1
+ module ReVolt; module Package; module Installer; end; end; end
2
+
3
+ module ReVolt::Package::Installer
4
+ class Exe
5
+ def self.handles?(file_name, args = {})
6
+ # No support yet for any executable formats
7
+ false
8
+ end
9
+ end # Archive
10
+ end # ReVolt::Package::Installer
@@ -0,0 +1,64 @@
1
+ require 'revolt/package/archive'
2
+ require 'revolt/package/exe'
3
+ require 'revolt/package/installer/archive'
4
+ require 'revolt/package/installer/exe'
5
+ require 'revolt/exceptions'
6
+
7
+ module ReVolt
8
+ module Package
9
+ # class NoBrowserForError < StandardError; end
10
+
11
+ module_function
12
+
13
+ def installer_class_for(file, args = {})
14
+ inst ||= if args[:installer_class_for] then
15
+ args[:installer_class_for].call(file, args)
16
+ end
17
+ inst ||= Installer::Archive if Installer::Archive::handles?(file, args)
18
+ inst ||= Installer::Exe if Installer::Exe::handles?(file, args)
19
+ inst
20
+ end
21
+
22
+ def installer_for(file, args = {})
23
+ klass = installer_class_for(file, args)
24
+ klass.create_for(file, args) if klass
25
+ end
26
+
27
+ def install_track(source, rv_path, args = {})
28
+ inst = installer_for source
29
+ if inst
30
+ inst.install_track source, rv_path, args
31
+ else
32
+ raise PackageUnknownError.new,"Could not find installer for #{source}"
33
+ end
34
+ end
35
+
36
+ =begin
37
+ # A factory function for creating a filesystem like
38
+ # browser for the given package
39
+ def browser file
40
+ if !block_given?
41
+ raise ArgumentError, "block is required"
42
+ end
43
+
44
+ klass = package_class file
45
+
46
+ raise NoBrowserForError, "No browser for #{file}" unless klass
47
+
48
+ obj = klass.new file
49
+ yield obj
50
+ obj.close
51
+ end
52
+
53
+ def is_package? filename
54
+ browser_class(filename) != nil
55
+ end
56
+
57
+ def browser_class filename
58
+ klass = case filename
59
+ when /.zip$/i : ReVolt::Package::Zip::Browser
60
+ end
61
+ end
62
+ =end
63
+ end # Package
64
+ end # ReVolt
@@ -0,0 +1,30 @@
1
+ require 'revolt/logger'
2
+
3
+ module ReVolt; module Util; end; end
4
+
5
+ module ReVolt::Util
6
+ # FileSystem browser. Provides the given path's contents like other
7
+ # browsers in a format like:
8
+ # path/file.ext
9
+ # path/subdir/file2.ext
10
+ # ...
11
+ class FsBrowser
12
+ include Enumerable
13
+ include ReVolt::Logger
14
+
15
+ attr_reader :files
16
+
17
+ def initialize(path)
18
+ @files = Dir["#{path}/**/*"]
19
+ end
20
+
21
+ def self.open(path, &block)
22
+ me = new(path)
23
+ me.each(block) if block
24
+ end
25
+
26
+ def each(&block)
27
+ @files.each(&block)
28
+ end
29
+ end # class FsBrowser
30
+ end # module
@@ -0,0 +1,20 @@
1
+ require 'tmpdir'
2
+ require 'tempfile'
3
+ require 'pathname'
4
+
5
+ module ReVolt
6
+ module Util
7
+ module_function
8
+
9
+ def tmpdir
10
+ # For now just the same as system tmpdir.
11
+ # Maybe later create a subdirectory there
12
+ # to help debugging
13
+ Pathname.new(Dir.tmpdir)
14
+ end
15
+
16
+ def caseinsensitiveglob(glob)
17
+ glob.downcase.gsub(/[a-z]/) {|m| '['+m.downcase+m.upcase+']' }
18
+ end
19
+ end
20
+ end
data/lib/revolt.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'revolt/levels'
2
+ require 'revolt/level'
3
+ require 'revolt/fetcher'
4
+ require 'revolt/info'
5
+ require 'revolt/package'
6
+ require 'revolt/util'
7
+ require 'revolt/util/fs_browser'
8
+ require 'revolt/args'
9
+
10
+ module ReVolt
11
+ VERSION = '0.5.1'
12
+ end
data/test/common.rb ADDED
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/ruby -w
2
+
3
+ require 'pathname'
4
+
5
+ module ReVolt
6
+ module Test
7
+ ZIPS = {
8
+ :standard => 'standard_track.zip',
9
+ :standard_rev => 'standard_rev_track.zip',
10
+ :standard_multi => 'standard_multi.zip',
11
+ :zips_inside => 'zips_inside.zip',
12
+ :rickyd_style => 'rickyd_track.zip',
13
+ :nodirs_style => 'nodirs_track.zip'
14
+ }
15
+ ZIPS_IDS = {
16
+ :standard => [:levid],
17
+ }
18
+
19
+ module_function
20
+
21
+ def setup
22
+ @output_dir = Pathname.new(File.dirname(__FILE__)) + 'temp_out'
23
+ clean_working_dir
24
+ @output_dir.mkdir
25
+ end
26
+
27
+ def clean_working_dir
28
+ # Clean output dir
29
+ if @output_dir.directory?
30
+ STDERR.puts "Cleaning #{@output_dir}"
31
+ @output_dir.rmtree # FileUtils.rm_r @output_dir
32
+ end
33
+ end
34
+
35
+ def get_test_file filename
36
+ File.join(File.dirname(__FILE__), 'fixtures', 'files', filename)
37
+ end
38
+
39
+ def get_test_output_dir(subdir = nil)
40
+ t = @output_dir.dup
41
+ t.mkdir unless t.directory?
42
+ if subdir
43
+ t += subdir
44
+ t.mkdir unless t.directory?
45
+ end
46
+ t.to_s
47
+ end
48
+
49
+ def stringify_and_order_structure(c)
50
+ operation_and_order_structure(c) {|a|
51
+ a = a.to_s
52
+ a = yield(a) if block_given?
53
+ a
54
+ }
55
+ end
56
+
57
+ def strdowncase_and_order_structure(c)
58
+ stringify_and_order_structure(c) {|a|
59
+ a = a.downcase
60
+ a = yield(a) if block_given?
61
+ a
62
+ }
63
+ end
64
+
65
+ def operation_and_order_structure(c, &blk)
66
+ if c.is_a? Hash
67
+ c.each {|(k,v)| c[k] = operation_and_order_structure(v,&blk)}
68
+ elsif c.is_a? Array
69
+ c.map! {|v| operation_and_order_structure(v,&blk)}
70
+ c.sort!
71
+ else
72
+ c = yield(c) if block_given?
73
+ end
74
+
75
+ c
76
+ end
77
+ end # module Test
78
+ end
@@ -0,0 +1 @@
1
+
Binary file
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,4 @@
1
+ ; Re-Volt test level
2
+ y
3
+ NAME 'Test Level'
4
+ STARTGRID 3