# 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