1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
|
module TTFunk
class ResourceFile
attr_reader :map
def self.open(path)
::File.open(path, "rb") do |io|
file = new(io)
yield file
end
end
def initialize(io)
@io = io
data_offset, map_offset, data_length, map_length = @io.read(16).unpack("N*")
@map = {}
@io.pos = map_offset + 24 # skip header copy, next map handle, file reference, and attrs
type_list_offset, name_list_offset = @io.read(4).unpack("n*")
type_list_offset += map_offset
name_list_offset += map_offset
@io.pos = type_list_offset
max_index = @io.read(2).unpack("n").first
0.upto(max_index) do
type, max_type_index, ref_list_offset = @io.read(8).unpack("A4nn")
@map[type] = { :list => [], :named => {} }
parse_from(type_list_offset + ref_list_offset) do
0.upto(max_type_index) do
id, name_ofs, attr = @io.read(5).unpack("nnC")
data_ofs = @io.read(3)
data_ofs = data_offset + [0, data_ofs].pack("CA*").unpack("N").first
handle = @io.read(4).unpack("N").first
entry = { :id => id, :attributes => attr, :offset => data_ofs, :handle => handle }
if name_list_offset + name_ofs < map_offset + map_length
parse_from(name_ofs + name_list_offset) do
len = @io.read(1).unpack("C").first
entry[:name] = @io.read(len)
end
end
@map[type][:list] << entry
@map[type][:named][entry[:name]] = entry if entry[:name]
end
end
end
end
def [](type, index=0)
if @map[type]
collection = index.is_a?(Fixnum) ? :list : :named
if @map[type][collection][index]
parse_from(@map[type][collection][index][:offset]) do
length = @io.read(4).unpack("N").first
return @io.read(length)
end
end
end
end
def resources_for(type)
(@map[type] && @map[type][:named] || {}).keys
end
private
def parse_from(offset)
saved, @io.pos = @io.pos, offset
yield
ensure
@io.pos = saved
end
end
end
|