class NWN::Erf::Erf
This reads and writes NWN::Resources::Container
as valid ERF binary data.
Attributes
day_of_year[RW]
description_str_ref[RW]
file_type[RW]
file_version[RW]
localized_strings[RW]
year[RW]
Public Class Methods
new(io = nil)
click to toggle source
Create a new Erf
object, optionally reading a existing file from io
.
Calls superclass method
NWN::Resources::Container::new
# File lib/nwn/erf.rb, line 20 def initialize io = nil super() @localized_strings = {} @io = io @file_type, @file_version = "ERF", "V1.0" @year = Time.now.year - 1900 @description_str_ref = 0xffffffff @day_of_year = Time.now.yday read_from io if io end
Public Instance Methods
add(o)
click to toggle source
Calls superclass method
NWN::Resources::Container#add
# File lib/nwn/erf.rb, line 38 def add o fnlen = filename_length @file_version raise ArgumentError, "Invalid filename: #{o.filename.inspect}" if o.resref.size == 0 || o.resref.size > fnlen super(o) end
add_file(filename, io = nil)
click to toggle source
Calls superclass method
NWN::Resources::Container#add_file
# File lib/nwn/erf.rb, line 31 def add_file filename, io = nil fnlen = filename_length @file_version raise ArgumentError, "Invalid filename: #{filename.inspect}" if filename.size == 0 || filename.size > (fnlen + 4) super(filename, io) end
write_to(io)
click to toggle source
Writes this Erf
to a io stream.
# File lib/nwn/erf.rb, line 129 def write_to io fnlen = filename_length @file_version locstr = @localized_strings.map {|x| [x[0], x[1].size, x[1]].pack("V V a*") }.join("") keylist = @content.map {|c| NWN.log_debug "truncating filename #{c.resref}, longer than #{fnlen}" if c.resref.size > fnlen [c.resref, @content.index(c), c.res_type, 0].pack("a#{fnlen} V v v") }.join("") offset = 160 + locstr.size + keylist.size + 8 * @content.size reslist = @content.map {|c| r = [offset, c.size].pack("V V") offset += c.size r }.join("") offset_to_locstr = 160 offset_to_keylist = offset_to_locstr + locstr.size offset_to_resourcelist = offset_to_keylist + keylist.size header = [@file_type, @file_version, @localized_strings.size, locstr.size, @content.size, offset_to_locstr, offset_to_keylist, offset_to_resourcelist, @year, @day_of_year, @description_str_ref, ""].pack("A4 A4 VV VV VV VV V a116") io.write(header) io.write(locstr) io.write(keylist) io.write(reslist) @content.each {|c| io.write(c.get) } end
Private Instance Methods
filename_length(version)
click to toggle source
# File lib/nwn/erf.rb, line 47 def filename_length version case version when "V1.0" 16 when "V1.1" 32 else raise IOError, "Invalid ERF version: #{version}" end end
read_from(io)
click to toggle source
# File lib/nwn/erf.rb, line 59 def read_from io @file_type, @file_version, locstr_count, locstr_size, entry_count, offset_to_locstr, offset_to_keys, offset_to_res, @year, @day_of_year, @description_str_ref = @io.read(160, "header").unpack("A4 A4 VV VV VV VV V a116") raise IOError, "Cannot read erf stream: invalid type #{@file_type.inspect}" unless NWN::Erf::ValidTypes.index(@file_type) fnlen = filename_length @file_version @io.seek(offset_to_locstr) locstr = @io.e_read(locstr_size, "locstr_size") for lstr in 0...locstr_count do if locstr.nil? || locstr.size == 0 NWN.log_debug "locstr table: not enough entries (expected: #{locstr_count}, got: #{lstr})" break end if locstr.size < 8 NWN.log_debug "locstr table: not enough entries (expected: #{locstr_count}, got: #{lstr})" + " partial data: #{locstr.inspect}" break end lid, strsz = locstr.unpack("V V") if strsz > locstr.size - 8 NWN.log_debug "locstr table: given strsz is bigger than available data, truncating" strsz = locstr.size - 8 end str = locstr.unpack("x8 a#{strsz}")[0] # This just means that the given locstr size was encoded wrongly - # the old erf.exe is known to do that. NWN.log_debug "Expected locstr size does not match actual " + "string size (want: #{strsz}, got #{str.size} of #{str.inspect})" if strsz != str.size @localized_strings[lid] = str locstr = locstr[8 + str.size .. -1] end keylist_entry_size = fnlen + 4 + 2 + 2 @io.seek(offset_to_keys) keylist = @io.e_read(keylist_entry_size * entry_count, "keylist") keylist = keylist.unpack("A#{fnlen} V v v" * entry_count) resourcelist_entry_size = 4 + 4 @io.seek(offset_to_res) resourcelist = @io.e_read(resourcelist_entry_size * entry_count, "reslist") resourcelist = resourcelist.unpack("I I" * entry_count) _index = 0 keylist.each_slice(4) {|resref, res_id, res_type, unused| co = NWN::Resources::ContentObject.new(resref, res_type, @io) offset, size = resourcelist[_index * 2], resourcelist[_index * 2 + 1] co.offset = offset co.size_override = size add co _index += 1 } end