Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS 

Ace2 mod manager

   1  
   2  #!/usr/local/bin/ruby
   3  
   4  require 'open-uri'
   5  require 'fileutils'
   6  
   7  # should be placed inside WoW directory
   8  # usage:
   9  # ace_updater                  - updates all installed mods
  10  # ace_updater update           - updates all installed mods
  11  # ace_updater update Omen Grid - updates selected mods
  12  # ace_updater add Threat-1.0   - install a new mod
  13  # ace_updater delete FuBar     - uninstall an existing mod
  14  # add the -v flag to an update to show changes from the changelog.
  15  
  16  # You may need to adjust zip_extract for your system.
  17  # file is the zip file to be extracted
  18  # target_dir is the existing directory to extract into.
  19  
  20  class Updater
  21  	# requirers some OS customisation, this WFM on Mac OS X
  22  	def zip_extract(file,target_dir)
  23  		system(
  24  			"unzip", "-q", file, "-d", target_dir
  25  		) or raise "Error extracting (#{$?}): unzip #{file.inspect} -d #{target_dir.inspect}"
  26  	end	
  27  
  28  	def initialize(wow_path,opts,listing_url="http://files.wowace.com/")
  29  		@opts=opts
  30  		@wow_path = wow_path
  31  		@addons_dir = File.join(@wow_path,"Interface","Addons")
  32  		@addons_download_dir = File.join(@wow_path,"Interface","ace_updater")
  33  		@listing_url=listing_url
  34  	end
  35  
  36  	def mod_index
  37  		@mod_index ||= begin
  38  			puts "Fetching Ace mod index from #{@listing_url}..."
  39  			m={}
  40  			open(@listing_url) {|f| 
  41  				f.read 
  42  			}.scan(
  43  				%r{<td>\s*<a href="([^"]+)">([^>]+)</a>\s*</td>\s*<td>r(\d+)</td>}
  44  			) {|url,mod,revision|
  45  				m[mod] = [revision.to_i,url]
  46  			} 
  47  			puts "#{m.length} entries found."
  48  			puts
  49  			m
  50  		end
  51  	end
  52  
  53  	def each_installed_mod
  54  		Dir.foreach(@addons_dir) {|addon|
  55  			next if ["..","."].include? addon 
  56  			yield addon,installed_revision(addon)
  57  		}
  58  	end
  59  
  60  	def installed_revision(addon)
  61  		addon_dir = File.join(@addons_dir,addon)
  62  		x = Dir.glob(File.join(addon_dir,"Changelog-#{addon}-r*.xml"))
  63  		return nil if x.empty?
  64  		x[0] =~ /Changelog-#{addon}-r(\d+)\.xml/
  65  		$1.to_i
  66  	end
  67  
  68  	def get_changes(changelog,local_revision) 
  69  		changes=[]
  70  		current_rev = "?????"
  71  		reading_changes = false
  72  		File.open(changelog) {|f|
  73  			f.read
  74  		}.each_line{|l|
  75  			l.strip!
  76  			if l =~ /^r(\d+) \|/
  77  				current_rev = $1.to_i
  78  				break if current_rev <= local_revision
  79  			elsif l=~/^-+$/
  80  				reading_changes = false
  81  			elsif l=~/^$/
  82  				reading_changes = true
  83  			else
  84  				changes << [current_rev,l] if reading_changes
  85  			end
  86  		}
  87  		changes.stable_sort_by {|r,t| r}
  88  	end
  89  
  90  	def update_mods(list=nil)
  91  		list=nil if list.empty?
  92  
  93  		remote_mods = mod_index()
  94  
  95  		puts "Scanning #{list ? 'chosen' : 'all'} mods for updates..."
  96  		jobs = []
  97  		each_installed_mod {|mod,revision|
  98  			next if revision.nil? #non-ace
  99  			next if list and not list.include?(mod) #not on the list
 100  
 101  			remote_revision,url = remote_mods[mod]
 102  			if remote_revision.nil? or remote_revision < revision
 103  				nonfatal("#{mod}: local is newer than remote (r#{revision.inspect} vs r#{remote_revision.inspect}). Skipping.")
 104  			elsif remote_revision > revision
 105  				jobs << [mod,revision,remote_revision,url]
 106  			end
 107  
 108  			list.delete mod if list
 109  		}
 110  		list.each {|x|
 111  			$stderr.puts "WARNING: #{x} is not an installed Ace addon, skipping update."
 112  		} if list
 113  
 114  		if jobs.length == 0
 115  			puts "#{list ? 'Chosen' : 'All'} mods up to date."
 116  		else
 117  			install_mods(jobs)
 118  		end
 119  	end
 120  
 121  	def add_mods(modlist)
 122  		remote_mods = mod_index()
 123  		jobs = modlist.map {|mod|
 124  			if remote_mods.key?(mod)
 125  				rev,url = remote_mods[mod]
 126  				[mod,nil,rev,url]
 127  			else
 128  				$stderr.puts "WARNING: #{mod} not found in mod index, skipping"
 129  			end
 130  		}.compact
 131  		install_mods(jobs)
 132  	end
 133  
 134  	def delete_mods(modlist)
 135  		modlist = modlist.map {|x|
 136  			next if ['.','..'].include?(x) # this would be lame
 137  			rev = installed_revision(x)
 138  			if rev
 139  				[x,rev]
 140  			else
 141  				nonfatal("#{x} is not an installed Ace addon, skipping")
 142  			end
 143  		}.compact
 144  
 145  		modlist.each_with_index {|(mod,rev),i|
 146  			status(i,modlist.length,"delete",mod,rev)
 147  			FileUtils.remove_dir(File.join(@addons_dir,mod))
 148  		}
 149  	end
 150  	
 151  	def install_mods(jobs)
 152  		FileUtils.remove_dir(@addons_download_dir) if File.directory?(@addons_download_dir)
 153  		Dir.mkdir(@addons_download_dir) 
 154  		jobs.each_with_index{|(mod,revision,remote_revision,url),index|
 155  			status(index, jobs.length, (revision ? 'update' : 'add'), mod, *([revision,remote_revision].compact))
 156  
 157  			zipfile = File.join(@addons_download_dir,"#{mod}.zip")
 158  
 159  			File.open(zipfile,'w') {|f|
 160  				open(url) {|data|
 161  					FileUtils.copy_stream(data,f)
 162  				}
 163  			}
 164  
 165  			zip_extract(zipfile,@addons_download_dir)
 166  			expected_output_dir = File.join(@addons_download_dir,mod)
 167  			destination_dir = File.join(@addons_dir,mod)
 168  			backup_dir = "#{destination_dir}.backup"
 169  
 170  			unless File.directory?(expected_output_dir)
 171  				nonfatal("#{expected_output_dir} not created as expected")
 172  				nonfatal("Check #{zipfile}")
 173  				nonfatal("Skipping installation of #{mod}")
 174  			end
 175  
 176  			changelog = File.join(expected_output_dir,"Changelog-#{mod}-r#{remote_revision}.xml")
 177  			# for some reason these often get wrong privs
 178  			FileUtils.chmod 0644,changelog
 179  			if revision and @opts.include?('-v') # show changelog
 180  				changes = get_changes(changelog,revision)
 181  				changes.each {|revision,text|
 182  					puts("   * [r%d] %s" % [revision,text])
 183  				}
 184  			end
 185  
 186  			FileUtils.remove(zipfile)
 187  			FileUtils.mv(destination_dir,backup_dir) if revision
 188  			FileUtils.mv(expected_output_dir,destination_dir)
 189  			FileUtils.remove_dir(backup_dir) if revision
 190  		}
 191  		FileUtils.remove_dir(@addons_download_dir)
 192  	end
 193  
 194  	def status(sequence,total,action,mod,rev1,rev2=nil)
 195  		rev2 = " -> r#{rev2}" if rev2
 196  		puts("[%2d/%2d] %6s %16s r%d%s" % [sequence+1,total,action,mod,rev1,rev2])
 197  	end
 198  
 199  	def nonfatal(s)
 200  		$stderr.puts("WARNING: #{s}")
 201  	end
 202  end
 203  
 204  class Array
 205    def stable_sort_by
 206      n = 0
 207      sort_by {|x| n+= 1; [yield(x), n]}
 208    end
 209  end
 210  
 211  
 212  if __FILE__ == $0
 213  	opts,args = ARGV.partition {|text| text[0] == ?- }
 214  	args << 'update' if args.empty?
 215  	command = args.shift.downcase
 216  
 217  	updater = Updater.new(File.dirname(__FILE__),opts)
 218  
 219  	case command
 220  	when 'update'
 221  		updater.update_mods(args)
 222  	when 'add'
 223  		raise "Must specify mods to add" if args.empty?
 224  		updater.add_mods(args)
 225  	when 'delete'
 226  		raise "Must specify mods to delete" if args.empty?
 227  		updater.delete_mods(args)
 228  	else
 229  		puts "Valid commands: update add delete"
 230  	end
 231  end
 232  
« Newer Snippets
Older Snippets »
Showing 1-1 of 1 total  RSS