Lorsque vous apportez des modifications à un fichier binaire, il peut être intéressant de stocker celles-ci pour les réutiliser :
Binaire Original + Patch = Binaire modifié
RubyPatchMaker permet de créer un patch en ruby, utilisable sur le Binaire original. Il existe en deux versions: ligne de commande, et GUI.
- ruby-gtk2 (ruby-gtk2-0.16.0-1-i386-msvcrt-1.8.zip , également pour la version GUI)

2) Un patch est alors créé. Il enregistre:
- l'empreinte MD5 de l'original
- l'ensemble des différences entre le binaire original et celui modifié.

3) Pour patcher ultérieurement, placez le binaire et le patch dans le même répertoire, et lancez le patch. Une copie de l'orignal est sauvegardée.

******************************************************************************
# Ruby Patch Maker Tool 1.0
# created by t0ka7a 2009
# this program stores differences between two files:
# - original file: file before patch
# - new file: file patched
# Then, it returns a patch in ruby.
require 'gtk2'
require 'digest/md5'
class Modifications
# differences between 2 files
def initialize
@data=[]
@counters=[]
@modif_text = ""
end
def push(obj1,obj2)
@data.push(obj1)
@counters.push(obj2)
end
def data
@data
end
def counters
@counters
end
def extract_modifications(name_f_old,name_f_new)
#returns an array with differences between original and new file.
#open original and patched files
f_old = File.open(name_f_old.to_s,'r').binmode
f_new = File.open(name_f_new.to_s,'r').binmode
# test if f_new and f_old have same size
return nil if test_error(File.size(name_f_old)!=File.size(name_f_new),"Original and patched files don't have same size")
#declaration
i=0
a,b=""
# compares byte number i of original file to byte number i of patched file. Returns an array with differences found
while b=f_old.getc do
a=f_new.getc
if a!=b then
push(a,i)
end
i+=1
end
#close files
f_old.close
f_new.close
# puts lines of program used to load modifications in @modif
i=0
while @counters[i] do
@modif_text << " @data.push(#{@data[i]})\n@counters.push(#{@counters[i]})\n"
i+=1
end
end
def write_program_to_file(name_f_save,name_f_old,md5_f_old)
# creates a file named xxx_patch.rb with the program of the patch
file = File.open("#{name_f_save}.rb",'w')
# creates a string with lines of program for the patch
program = "
#!/usr/bin/env ruby
# created with Ruby Patch Maker Tool by t0ka7a 2009
require 'gtk2'
require 'digest/md5'
require 'ftools'
#----------------------------------------------------------------------------------------------------------------------------------------
# differences between 2 files
class Modifications
def initialize
@data=[]
@counters=[]
@name_p=#{name_f_save.dump}
@name_f_to_patch=#{name_f_old.dump}
@md5_f_old=#{md5_f_old.dump}
@md5_f_new=Digest::MD5.file(@name_f_to_patch).hexdigest if @name_f_to_patch.to_s != \"\"
#{@modif_text}
#-----------------------------------------------------------------------
#compare md5 of file to patch with md5 calculated when patch was created.
if @md5_f_old != @md5_f_new then
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::WARNING,
Gtk::MessageDialog::BUTTONS_CLOSE,
\"File to patch is not valid. Good version?\"
)
dialog.run
dialog.destroy
return nil
end
#-----------------------------------------------------------------------
# copy original_file to original_file.old
File.copy(@name_f_to_patch.to_s,@name_f_to_patch.to_s+\".old\")
#-----------------------------------------------------------------------
#patch the file
f = File.open(@name_f_to_patch.to_s,\'r+\').binmode
for i in 0...@counters.length
f.pos=@counters[i].to_i
f.write(@data[i].chr.to_s)
end
f.close
#-----------------------------------------------------------------------
#show dialog success
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
\"file \#{@name_f_to_patch} patched!\"
)
dialog.run
dialog.destroy
end
end
#----------------------------------------------------------------------------------------------------------------------------------------
def messagebox2
# shows a dialog of confirmation of patch
window = Gtk::Window.new
table = Gtk::Table.new(3,2,true)
label1 = Gtk::Label.new(\"Patch for \#{#{name_f_save.dump}}\")
label2 = Gtk::Label.new(\"generated by t0ka7a's Patcher Tool\")
button_cancel = Gtk::Button.new(\"cancel\")
button_ok = Gtk::Button.new(\"Patch\")
table.attach_defaults(label1,0,2,0,1)
table.attach_defaults(label2,0,2,1,2)
table.attach_defaults(button_cancel,0,1,2,3)
table.attach_defaults(button_ok,1,2,2,3)
button_cancel.signal_connect(\"clicked\") { Gtk.main_quit }
button_ok.signal_connect(\"clicked\") { modif=Modifications.new;Gtk.main_quit}
window.add(table)
window.show_all
Gtk.main
end
#----------------------------------------------------------------------------------------------------------------------------------------
# main
messagebox2
"
file.write(program)
file.close
end
end
def md5(file)
# returns md5 of file
digest = Digest::MD5.file(file).hexdigest if file.to_s != ""
return digest
end
def dialog_bravo(message)
# dialog bravo
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
message
)
dialog.run
dialog.destroy
end
def test_error(condition,message)
# shows an error dialog with message if condition false
if condition then
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::WARNING,
Gtk::MessageDialog::BUTTONS_CLOSE,
message
)
dialog.run
dialog.destroy
return true
else
return false
end
end
def next_function(name_f_old,name_f_new,name_f_save)
md5_f_old=md5(name_f_old)
modif=Modifications.new
modif.extract_modifications(name_f_old,name_f_new)
modif.write_program_to_file(name_f_save,name_f_old,md5_f_old)
dialog_bravo("patch successfully created: #{name_f_save}")
Gtk.main_quit
end
def messagebox1
# show main dialog
window = Gtk::Window.new
window.set_window_position Gtk::Window::POS_CENTER # center window
filled = [false,false,false] # = true when all dialog filled by user
modif = [] # array with differences between original and patched files
table = Gtk::Table.new(7,3,true) #table for position of the buttons and labels #rows, columns, homogenous
options = Gtk::EXPAND|Gtk::FILL
# line 7
button_cancel = Gtk::Button.new("cancel")
button_ok = Gtk::Button.new("OK")
# line 0
label = Gtk::Label.new("Ruby Patch Maker Tool by t0ka7a")
# line 1
label2 = Gtk::Label.new("original file: ")
button_file2 = Gtk::FileChooserButton.new("original file", Gtk::FileChooser::ACTION_OPEN)
# line 2
label_file3 = Gtk::Label.new("")
# line 3
label4 = Gtk::Label.new("patched file: ")
button_file4 = Gtk::FileChooserButton.new("patched file",Gtk::FileChooser::ACTION_OPEN)
# line 4
label_file5 = Gtk::Label.new("")
#line 5
label_folder = Gtk::Label.new("")
# line 6
button_folder = Gtk::Button.new("destinatation for patch")
# child, left_attach, right_attach, top_attach, bottom_attach, x-opt, y-opt, xpad, ypad
table.attach(label,0,3,0,1,options,options,0,0) #line 0
table.attach(label2,0,1,1,2,options,options,0,0) # line 1
table.attach(button_file2,1,3,1,2,options,options,0,0)
table.attach(label_file3,0,3,2,3,options,options,0,0) # line 2
table.attach(label4,0,1,3,4,options,options,0,0) # line 3
table.attach(button_file4,1,3,3,4,options,options,0,0)
table.attach(label_file5,0,3,4,5,options,options,0,0) # line 4
table.attach(button_folder,0,3,5,6,options,options,0,0) # line 5
table.attach(label_folder,0,3,6,7,options,options,0,0) # line 6
table.attach(button_cancel,0,1,7,8,options,options,0,0) # line 7
table.attach(button_ok,2,3,7,8,options,options,0,0)
window.add(table)
window.border_width = 10
window.title = "Ruby Patch Maker Tool 1.0 by t0ka7a"
#events
button_file2.signal_connect('selection_changed') do # old file
label_file3.text = button_file2.filename
filled[0]=true
end
button_file4.signal_connect('selection_changed') do #patched file
label_file5.text = button_file4.filename
filled[1]=true
end
button_folder.signal_connect('clicked') do #destination for patch
filled[2]=true
dialog = Gtk::FileChooserDialog.new(
"Save Patch As ...",
window,
Gtk::FileChooser::ACTION_SAVE,
nil,
[ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
[ Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT ]
)
dialog.run do |response|
if response == Gtk::Dialog::RESPONSE_ACCEPT then
label_folder.text = dialog.filename
dialog.destroy
end
end
end
button_cancel.signal_connect("clicked") { Gtk.main_quit }
button_ok.signal_connect("clicked") { next_function(label_file3.text,label_file5.text,label_folder.text) if filled[0] and filled[1] and filled[2]}
window.signal_connect("destroy") { Gtk.main_quit }
window.show_all
Gtk.main
end
def compil
end
#main
messagebox1
******************************************************************************
# Ruby Patch Maker Tool 1.0
# created by t0ka7a 2009
# this program stores differences between two files:
# - original file: file before patch
# - new file: file patched
# Then, it returns a patch in ruby.
# ***********************************
# * *
# Ruby Patch Maker Tool by t0ka7a
# * *
# ***********************************"
#
# How to use:"
# Install ruby from http://www.ruby-lang.org/
# put original file, patched file, and this program in the same directory."
# In Windows, go to \"Start->Execute\""
# Write \"cmd\"."
# Write \"ruby RubyPatchMaker1.0.rb\". Enjoy!"require 'digest/md5'
require 'ftools'
class Modifications
# differences between 2 files
def initialize
@data=[]
@counters=[]
@modif_text = ""
end
def push(obj1,obj2)
@data.push(obj1)
@counters.push(obj2)
end
def data
@data
end
def counters
@counters
end
def extract_modifications(name_f_old,name_f_new)
#returns an array with differences between original and new file.
#open original and patched files
f_old = File.open(name_f_old.to_s,'r').binmode
f_new = File.open(name_f_new.to_s,'r').binmode
# test if f_new and f_old have same size
if File.size(name_f_old)!=File.size(name_f_new) then
puts "Original and patched files don't have same size. Quit!"
exit
end
#declaration
i=0
a,b=""
# compares byte number i of original file to byte number i of patched file. Returns an array with differences found
while b=f_old.getc do
a=f_new.getc
if a!=b then
push(a,i)
end
i+=1
end
#close files
f_old.close
f_new.close
# puts lines of program used to load modifications in @modif
i=0
while @counters[i] do
@modif_text << " @data.push(#{@data[i]})\n@counters.push(#{@counters[i]})\n"
i+=1
end
end
def write_program_to_file(name_f_save,name_f_old,md5_f_old)
# creates a file named xxx_patch.rb with the program of the patch
file = File.open("#{name_f_save}.rb",'w')
# creates a string with lines of program for the patch
program = "
#!/usr/bin/env ruby
# created with Ruby Patch Maker Tool by t0ka7a 2009
require 'digest/md5'
require 'ftools'
#----------------------------------------------------------------------------------------------------------------------------------------
# differences between 2 files
class Modifications
def initialize
@data=[]
@counters=[]
@name_p=#{name_f_save.dump}
@name_f_to_patch=#{name_f_old.dump}
@md5_f_old=#{md5_f_old.dump}
@md5_f_new=Digest::MD5.file(@name_f_to_patch).hexdigest if @name_f_to_patch.to_s != \"\"
#{@modif_text}
#-----------------------------------------------------------------------
#compare md5 of file to patch with md5 calculated when patch was created.
if @md5_f_old != @md5_f_new then
puts \"File to patch is not valid. Good version?\"
return nil
end
#-----------------------------------------------------------------------
# copy original_file to original_file.old
File.copy(@name_f_to_patch.to_s,@name_f_to_patch.to_s+\".old\")
#-----------------------------------------------------------------------
#patch the file
f = File.open(@name_f_to_patch.to_s,\'r+\').binmode
for i in 0...@counters.length
f.pos=@counters[i].to_i
f.write(@data[i].chr.to_s)
end
f.close
puts \"File Patched successfully. Enjoy!\"
end
#----------------------------------------------------------------------------------------------------------------------------------------
# main
puts \"\"
puts \"\"
puts \"\"
puts \"\"
puts \"\"
puts \"*************************************************\"
puts \"* *\"
puts \" Patch for \#{#{name_f_save.dump}}\ \"
puts \"* *\"
puts \"*************************************************\"
puts \"\"
puts \"\"
puts \"\"
puts \"\"
puts \"How to use:\"
puts \" put patch in the folder of \#{#{name_f_save.dump}}\.\"
puts \" In Windows, go to \\\"Start->Execute\\\"\"
puts \" Write \\\"cmd\\\".\"
puts \" Write ruby \\\"\#{#{name_f_save.dump}}\.rb\\\". Enjoy!\"
puts \"\"
puts \"ready (Y/n)?\"
exit if gets.chomp != \"Y\"
modif=Modifications.new
end"
file.write(program)
file.close
end
end
def md5(file)
# returns md5 of file
digest = Digest::MD5.file(file).hexdigest if file.to_s != ""
return digest
end
def main
puts ""
puts ""
puts ""
puts ""
puts ""
puts "***********************************"
puts "* *"
puts "* Ruby Patch Maker Tool by t0ka7a *"
puts "* *"
puts "***********************************"
puts ""
puts ""
puts ""
puts ""
puts "How to use:"
puts " put original file, patched file, and this program in the same directory."
puts " In Windows, go to \"Start->Execute\""
puts " Write \"cmd\"."
puts " Write \"ruby RubyPatchMaker1.0.rb\". Enjoy!"
puts ""
puts "ready (Y/n)?"
exit if gets.chomp != "Y"
puts ""
puts "original file name?"
name_f_old=gets.chomp
if !File.exist?(name_f_old) then
puts "File doesn't exist. Quit!"
exit
end
puts ""
puts "patched file name?"
name_f_new=gets.chomp
puts ""
if !File.exist?(name_f_new) then
puts "File doesn't exist. Quit!"
return nil
end
name_f_save = name_f_old + "_patch"
md5_f_old=md5(name_f_old)
modif=Modifications.new
modif.extract_modifications(name_f_old,name_f_new)
modif.write_program_to_file(name_f_save,name_f_old,md5_f_old)
puts "patch successfully created: #{name_f_save}.rb."
end
main
Binaire Original + Patch = Binaire modifié
RubyPatchMaker permet de créer un patch en ruby, utilisable sur le Binaire original. Il existe en deux versions: ligne de commande, et GUI.
- Prérequis:
- ruby-gtk2 (ruby-gtk2-0.16.0-1-i386-msvcrt-1.8.zip , également pour la version GUI)
- Mode d'emploi:
2) Un patch est alors créé. Il enregistre:
- l'empreinte MD5 de l'original
- l'ensemble des différences entre le binaire original et celui modifié.
3) Pour patcher ultérieurement, placez le binaire et le patch dans le même répertoire, et lancez le patch. Une copie de l'orignal est sauvegardée.
******************************************************************************
- Code source version GUI:
# Ruby Patch Maker Tool 1.0
# created by t0ka7a 2009
# this program stores differences between two files:
# - original file: file before patch
# - new file: file patched
# Then, it returns a patch in ruby.
require 'gtk2'
require 'digest/md5'
class Modifications
# differences between 2 files
def initialize
@data=[]
@counters=[]
@modif_text = ""
end
def push(obj1,obj2)
@data.push(obj1)
@counters.push(obj2)
end
def data
@data
end
def counters
@counters
end
def extract_modifications(name_f_old,name_f_new)
#returns an array with differences between original and new file.
#open original and patched files
f_old = File.open(name_f_old.to_s,'r').binmode
f_new = File.open(name_f_new.to_s,'r').binmode
# test if f_new and f_old have same size
return nil if test_error(File.size(name_f_old)!=File.size(name_f_new),"Original and patched files don't have same size")
#declaration
i=0
a,b=""
# compares byte number i of original file to byte number i of patched file. Returns an array with differences found
while b=f_old.getc do
a=f_new.getc
if a!=b then
push(a,i)
end
i+=1
end
#close files
f_old.close
f_new.close
# puts lines of program used to load modifications in @modif
i=0
while @counters[i] do
@modif_text << " @data.push(#{@data[i]})\n@counters.push(#{@counters[i]})\n"
i+=1
end
end
def write_program_to_file(name_f_save,name_f_old,md5_f_old)
# creates a file named xxx_patch.rb with the program of the patch
file = File.open("#{name_f_save}.rb",'w')
# creates a string with lines of program for the patch
program = "
#!/usr/bin/env ruby
# created with Ruby Patch Maker Tool by t0ka7a 2009
require 'gtk2'
require 'digest/md5'
require 'ftools'
#----------------------------------------------------------------------------------------------------------------------------------------
# differences between 2 files
class Modifications
def initialize
@data=[]
@counters=[]
@name_p=#{name_f_save.dump}
@name_f_to_patch=#{name_f_old.dump}
@md5_f_old=#{md5_f_old.dump}
@md5_f_new=Digest::MD5.file(@name_f_to_patch).hexdigest if @name_f_to_patch.to_s != \"\"
#{@modif_text}
#-----------------------------------------------------------------------
#compare md5 of file to patch with md5 calculated when patch was created.
if @md5_f_old != @md5_f_new then
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::WARNING,
Gtk::MessageDialog::BUTTONS_CLOSE,
\"File to patch is not valid. Good version?\"
)
dialog.run
dialog.destroy
return nil
end
#-----------------------------------------------------------------------
# copy original_file to original_file.old
File.copy(@name_f_to_patch.to_s,@name_f_to_patch.to_s+\".old\")
#-----------------------------------------------------------------------
#patch the file
f = File.open(@name_f_to_patch.to_s,\'r+\').binmode
for i in 0...@counters.length
f.pos=@counters[i].to_i
f.write(@data[i].chr.to_s)
end
f.close
#-----------------------------------------------------------------------
#show dialog success
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
\"file \#{@name_f_to_patch} patched!\"
)
dialog.run
dialog.destroy
end
end
#----------------------------------------------------------------------------------------------------------------------------------------
def messagebox2
# shows a dialog of confirmation of patch
window = Gtk::Window.new
table = Gtk::Table.new(3,2,true)
label1 = Gtk::Label.new(\"Patch for \#{#{name_f_save.dump}}\")
label2 = Gtk::Label.new(\"generated by t0ka7a's Patcher Tool\")
button_cancel = Gtk::Button.new(\"cancel\")
button_ok = Gtk::Button.new(\"Patch\")
table.attach_defaults(label1,0,2,0,1)
table.attach_defaults(label2,0,2,1,2)
table.attach_defaults(button_cancel,0,1,2,3)
table.attach_defaults(button_ok,1,2,2,3)
button_cancel.signal_connect(\"clicked\") { Gtk.main_quit }
button_ok.signal_connect(\"clicked\") { modif=Modifications.new;Gtk.main_quit}
window.add(table)
window.show_all
Gtk.main
end
#----------------------------------------------------------------------------------------------------------------------------------------
# main
messagebox2
"
file.write(program)
file.close
end
end
def md5(file)
# returns md5 of file
digest = Digest::MD5.file(file).hexdigest if file.to_s != ""
return digest
end
def dialog_bravo(message)
# dialog bravo
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
message
)
dialog.run
dialog.destroy
end
def test_error(condition,message)
# shows an error dialog with message if condition false
if condition then
dialog = Gtk::MessageDialog.new(
nil,
Gtk::Dialog::DESTROY_WITH_PARENT,
Gtk::MessageDialog::WARNING,
Gtk::MessageDialog::BUTTONS_CLOSE,
message
)
dialog.run
dialog.destroy
return true
else
return false
end
end
def next_function(name_f_old,name_f_new,name_f_save)
md5_f_old=md5(name_f_old)
modif=Modifications.new
modif.extract_modifications(name_f_old,name_f_new)
modif.write_program_to_file(name_f_save,name_f_old,md5_f_old)
dialog_bravo("patch successfully created: #{name_f_save}")
Gtk.main_quit
end
def messagebox1
# show main dialog
window = Gtk::Window.new
window.set_window_position Gtk::Window::POS_CENTER # center window
filled = [false,false,false] # = true when all dialog filled by user
modif = [] # array with differences between original and patched files
table = Gtk::Table.new(7,3,true) #table for position of the buttons and labels #rows, columns, homogenous
options = Gtk::EXPAND|Gtk::FILL
# line 7
button_cancel = Gtk::Button.new("cancel")
button_ok = Gtk::Button.new("OK")
# line 0
label = Gtk::Label.new("Ruby Patch Maker Tool by t0ka7a")
# line 1
label2 = Gtk::Label.new("original file: ")
button_file2 = Gtk::FileChooserButton.new("original file", Gtk::FileChooser::ACTION_OPEN)
# line 2
label_file3 = Gtk::Label.new("")
# line 3
label4 = Gtk::Label.new("patched file: ")
button_file4 = Gtk::FileChooserButton.new("patched file",Gtk::FileChooser::ACTION_OPEN)
# line 4
label_file5 = Gtk::Label.new("")
#line 5
label_folder = Gtk::Label.new("")
# line 6
button_folder = Gtk::Button.new("destinatation for patch")
# child, left_attach, right_attach, top_attach, bottom_attach, x-opt, y-opt, xpad, ypad
table.attach(label,0,3,0,1,options,options,0,0) #line 0
table.attach(label2,0,1,1,2,options,options,0,0) # line 1
table.attach(button_file2,1,3,1,2,options,options,0,0)
table.attach(label_file3,0,3,2,3,options,options,0,0) # line 2
table.attach(label4,0,1,3,4,options,options,0,0) # line 3
table.attach(button_file4,1,3,3,4,options,options,0,0)
table.attach(label_file5,0,3,4,5,options,options,0,0) # line 4
table.attach(button_folder,0,3,5,6,options,options,0,0) # line 5
table.attach(label_folder,0,3,6,7,options,options,0,0) # line 6
table.attach(button_cancel,0,1,7,8,options,options,0,0) # line 7
table.attach(button_ok,2,3,7,8,options,options,0,0)
window.add(table)
window.border_width = 10
window.title = "Ruby Patch Maker Tool 1.0 by t0ka7a"
#events
button_file2.signal_connect('selection_changed') do # old file
label_file3.text = button_file2.filename
filled[0]=true
end
button_file4.signal_connect('selection_changed') do #patched file
label_file5.text = button_file4.filename
filled[1]=true
end
button_folder.signal_connect('clicked') do #destination for patch
filled[2]=true
dialog = Gtk::FileChooserDialog.new(
"Save Patch As ...",
window,
Gtk::FileChooser::ACTION_SAVE,
nil,
[ Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL ],
[ Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT ]
)
dialog.run do |response|
if response == Gtk::Dialog::RESPONSE_ACCEPT then
label_folder.text = dialog.filename
dialog.destroy
end
end
end
button_cancel.signal_connect("clicked") { Gtk.main_quit }
button_ok.signal_connect("clicked") { next_function(label_file3.text,label_file5.text,label_folder.text) if filled[0] and filled[1] and filled[2]}
window.signal_connect("destroy") { Gtk.main_quit }
window.show_all
Gtk.main
end
def compil
end
#main
messagebox1
******************************************************************************
- Code source version ligne de commandes
# Ruby Patch Maker Tool 1.0
# created by t0ka7a 2009
# this program stores differences between two files:
# - original file: file before patch
# - new file: file patched
# Then, it returns a patch in ruby.
# ***********************************
# * *
# Ruby Patch Maker Tool by t0ka7a
# * *
# ***********************************"
#
# How to use:"
# Install ruby from http://www.ruby-lang.org/
# put original file, patched file, and this program in the same directory."
# In Windows, go to \"Start->Execute\""
# Write \"cmd\"."
# Write \"ruby RubyPatchMaker1.0.rb\". Enjoy!"require 'digest/md5'
require 'ftools'
class Modifications
# differences between 2 files
def initialize
@data=[]
@counters=[]
@modif_text = ""
end
def push(obj1,obj2)
@data.push(obj1)
@counters.push(obj2)
end
def data
@data
end
def counters
@counters
end
def extract_modifications(name_f_old,name_f_new)
#returns an array with differences between original and new file.
#open original and patched files
f_old = File.open(name_f_old.to_s,'r').binmode
f_new = File.open(name_f_new.to_s,'r').binmode
# test if f_new and f_old have same size
if File.size(name_f_old)!=File.size(name_f_new) then
puts "Original and patched files don't have same size. Quit!"
exit
end
#declaration
i=0
a,b=""
# compares byte number i of original file to byte number i of patched file. Returns an array with differences found
while b=f_old.getc do
a=f_new.getc
if a!=b then
push(a,i)
end
i+=1
end
#close files
f_old.close
f_new.close
# puts lines of program used to load modifications in @modif
i=0
while @counters[i] do
@modif_text << " @data.push(#{@data[i]})\n@counters.push(#{@counters[i]})\n"
i+=1
end
end
def write_program_to_file(name_f_save,name_f_old,md5_f_old)
# creates a file named xxx_patch.rb with the program of the patch
file = File.open("#{name_f_save}.rb",'w')
# creates a string with lines of program for the patch
program = "
#!/usr/bin/env ruby
# created with Ruby Patch Maker Tool by t0ka7a 2009
require 'digest/md5'
require 'ftools'
#----------------------------------------------------------------------------------------------------------------------------------------
# differences between 2 files
class Modifications
def initialize
@data=[]
@counters=[]
@name_p=#{name_f_save.dump}
@name_f_to_patch=#{name_f_old.dump}
@md5_f_old=#{md5_f_old.dump}
@md5_f_new=Digest::MD5.file(@name_f_to_patch).hexdigest if @name_f_to_patch.to_s != \"\"
#{@modif_text}
#-----------------------------------------------------------------------
#compare md5 of file to patch with md5 calculated when patch was created.
if @md5_f_old != @md5_f_new then
puts \"File to patch is not valid. Good version?\"
return nil
end
#-----------------------------------------------------------------------
# copy original_file to original_file.old
File.copy(@name_f_to_patch.to_s,@name_f_to_patch.to_s+\".old\")
#-----------------------------------------------------------------------
#patch the file
f = File.open(@name_f_to_patch.to_s,\'r+\').binmode
for i in 0...@counters.length
f.pos=@counters[i].to_i
f.write(@data[i].chr.to_s)
end
f.close
puts \"File Patched successfully. Enjoy!\"
end
#----------------------------------------------------------------------------------------------------------------------------------------
# main
puts \"\"
puts \"\"
puts \"\"
puts \"\"
puts \"\"
puts \"*************************************************\"
puts \"* *\"
puts \" Patch for \#{#{name_f_save.dump}}\ \"
puts \"* *\"
puts \"*************************************************\"
puts \"\"
puts \"\"
puts \"\"
puts \"\"
puts \"How to use:\"
puts \" put patch in the folder of \#{#{name_f_save.dump}}\.\"
puts \" In Windows, go to \\\"Start->Execute\\\"\"
puts \" Write \\\"cmd\\\".\"
puts \" Write ruby \\\"\#{#{name_f_save.dump}}\.rb\\\". Enjoy!\"
puts \"\"
puts \"ready (Y/n)?\"
exit if gets.chomp != \"Y\"
modif=Modifications.new
end"
file.write(program)
file.close
end
end
def md5(file)
# returns md5 of file
digest = Digest::MD5.file(file).hexdigest if file.to_s != ""
return digest
end
def main
puts ""
puts ""
puts ""
puts ""
puts ""
puts "***********************************"
puts "* *"
puts "* Ruby Patch Maker Tool by t0ka7a *"
puts "* *"
puts "***********************************"
puts ""
puts ""
puts ""
puts ""
puts "How to use:"
puts " put original file, patched file, and this program in the same directory."
puts " In Windows, go to \"Start->Execute\""
puts " Write \"cmd\"."
puts " Write \"ruby RubyPatchMaker1.0.rb\". Enjoy!"
puts ""
puts "ready (Y/n)?"
exit if gets.chomp != "Y"
puts ""
puts "original file name?"
name_f_old=gets.chomp
if !File.exist?(name_f_old) then
puts "File doesn't exist. Quit!"
exit
end
puts ""
puts "patched file name?"
name_f_new=gets.chomp
puts ""
if !File.exist?(name_f_new) then
puts "File doesn't exist. Quit!"
return nil
end
name_f_save = name_f_old + "_patch"
md5_f_old=md5(name_f_old)
modif=Modifications.new
modif.extract_modifications(name_f_old,name_f_new)
modif.write_program_to_file(name_f_save,name_f_old,md5_f_old)
puts "patch successfully created: #{name_f_save}.rb."
end
main
Aucun commentaire:
Enregistrer un commentaire