# require 'image_popup' # Methods to look into Sonyericsson mobile phone theme (THM) files. # Author:: Peter Kofler, www.code-cop.org # Copyright (c) 2009, Peter Kofler, licensed under BSD License. module SonyEricssonDesign EXTENSION = '.thm' # Standby_image = main image # Desktop_image = the menu selection # Popup_image = little image MAIN_IMAGE = 'Standby_image' attr_reader :items # Helper class to hold the data from the thm file structure. class ThmItem < Struct.new(:name, :numbers, :stuff, :body) # Length of the content in bytes. def body_len numbers[3] end # Full name of the the content file. alias to_s name # Extension of the content file. def extension name.scan(/\.[^.]+$/)[0] end def save_with_basename(basename) save(basename + extension) end def save(filename=name) File.open(filename, 'wb') { |io| io.write body } end def delete(filename=name) File.delete(filename) end end # Scan folder _dirname_ for all thm files. Do not check subfolders. def scan_for_thms(dirname) current = Dir.getwd Dir.chdir dirname files = Dir["*#{EXTENSION}"].collect { |name| "#{dirname}/#{name}" } Dir.chdir current files end # If the given block evaluates to +true+ print the _msg_ and return evaluation result. def warn?(msg) if yield puts "WARNING: #{msg}" true else false end end # Read thm _file_ and return the list of +ThmItem+ items. Stop at broken or unknown items. def read_thm(file) items = [] data = File.open(file, 'rb') { |io| io.read } max = data.size ofs = 0 while ofs < max do d = ThmItem.new #0000h ... null terminated string with name of file break if warn?("short data - no name") { ofs + 0x63 >= max } d.name = data[ofs..ofs+0x63].strip # p d.name break if warn?("invalid data - empty name") { d.name.empty? } #0064h ... some numbers in octal separated by blanks and nulls, lengths = [7, 7, 7, 11, 11, 7, 1] break if warn?("short data in #{d.name} - no numbers") { ofs + 0x100 >= max } # p [ofs.to_s , ':'] + data[ofs+0x64..ofs+0x100].scan(/[0-7]+/) d.numbers = data[ofs+0x64..ofs+0x100].scan(/[0-7]+/).collect { |n| n.strip.to_i(8) } # p d.numbers break if warn?("invalid data in #{d.name} - no length #{d.numbers.join('/')}") { !d.body_len || d.body_len == 0 } #0101h ... ustar 0x00 00W850 #0129h ... nogroup break if warn?("short data in #{d.name} - no stuff") { ofs + 0x1ff >= max } d.stuff = data[ofs+0x101..ofs+0x1ff].strip.scan(/[^\000]+/) # p d.stuff #0200h ... data break if warn?("short data in #{d.name} - no content") { ofs + 0x200 + d.body_len > max } d.body = data[ofs + 0x200..ofs + 0x200 + d.body_len - 1] # puts d.body items << d ofs += 0x200 + d.body_len while ofs/)[0][0] raise "main image (#{tag}) link not found" unless main_image_name main_image = @items.find { |i| i.name == main_image_name } raise "main image #{main_image_name} content not found" unless main_image main_image end # # Open a popup and show the main image. # def show_main_image # main_image = find_main_image # begin # main_image.save # ImagePopup.new(main_image.name).display # ensure # main_image.delete # end # end # Save the main image with the same name as the thm _thmname_ to disk. def save_main_images(thmname) n = MAIN_IMAGE main_image = find_main_image(n) main_image.save_with_basename(thmname.scan(/^(.+)\./)[0][0] + '_' + n) end end if __FILE__ == $0 include SonyEricssonDesign path = ARGV[0] if path.nil? print "#{File.basename $0} \n\n" print "extracts the #{MAIN_IMAGE} out of all Ericsson Theme files in the given for preview" exit(1) end scan_for_thms(path).each do |thm| begin puts "working on #{thm}" read_thm(thm) #show_main_image save_main_images(thm) rescue puts $! end end end