require 'optparse' load 'poke.list' load 'item.list' # # patch a pokemon diamond/pearl rom to change a pokemon definition # # usage: patch -f rom.nds Slowpoke hp 42 item1 'Razor Fang' # file = 'pk-dm-hx.nds' OptionParser.new {|opt| opt.on('-f file') { |f| file = f } }.parse!(ARGV) poke = ARGV.shift poke = Pokelist.index(poke) || Integer(poke) puts poke # check the file offset using bulbasaur base stats bulb_stats = [45, 49, 49, 45, 65, 65].pack('C*') base_offset = 0x1c021dc caracs = %w[hp att def speed spatt spdef type1 type2 catch xp eff1 eff2 item1 item2 gender hatchrate happy egg1 egg2 spab1 spab2 safari color] packstr = 'CCCCCCCCCCCCSSCCCCCCCCCC' types = %w[normal fight flying poison ground rock bug ghost steel 9 fire water grass electr psy ice dragon 16 dark] File.open(file, 'r+') { |fd| fd.pos = base_offset+44 if fd.read(6) != bulb_stats fd.pos = 0 base_offset = fd.read.index(bulb_stats) raise 'cannot find bulbo stats, aborting' if not base_offset end fd.pos = base_offset + poke*44 data = fd.read(44) puts data.unpack('H*') if ARGV.empty? caracs.zip(data.unpack(packstr)) { |l, v| case l when /^type/: v = types[v] || v when /^item/: v = Itemlist[v] || v end puts l.ljust(10) + " #{v}" } print 'TM ' 92.times { |i| print i+1, ' ' if data[0x1c+i/8][i%8] != 0 } print ' HM ' 8.times { |i| print i+1, ' ' if data[0x1c+(i+92)/8][(i+92)%8] != 0 } puts else while car = ARGV.shift val = ARGV.shift case car when /^type/i: val = types.index(val) || Integer(val) when /^item/i: val = Itemlist.index(val) || Integer(val) when /^[ht]m(\d+)/i ARGV.unshift val nr = $1.to_i - 1 nr += 92 if car.downcase[0] == ?h data[0x1c + (nr/8)] ^= 1 << (nr%8) next end raise 'bad val' if not val raise 'bad car '+car if not i = caracs.index(car) data = data.unpack(packstr) data[i] = Integer(val) data = data.pack(packstr) end fd.pos = base_offset + poke*44 fd.write data end }