Fabio Vitale <fabio / sferaconsulting.it> writes: > Now 3 major questions: > > Q 1: what type must I declare for Filename in the class MRKMessage? Okay, first off I apologize but I lead you astray. Apparently it's not enough to override bit_length in your subclass. When you read the file, you're not getting the stuff lined up properly. Therefore I've decided to make up for it by finishing the rest of your code for you. Note that now I override round_byte_length instead, and we get: require 'bit-struct' class MRKHeader < BitStruct unsigned :version, 32, "Version", :endian => :native unsigned :uid_Validity, 32, "UIDValidity", :endian => :native unsigned :uid_next, 32, "UIDNext", :endian => :native unsigned :last_write_counter, 32, "LastWriteCounter", :endian => :native rest :unused, "Unused" # Override so that it gets padded properly def MRKHeader.round_byte_length super 36 end end # Ideally, I'd construct some sort of "flags" bit-struct field # Or define a boolean field type and make this a series of boolean # fields. # However, for now we can deal with a series of 0s and 1s class MRKMessageFlags < BitStruct unsigned :flagUnused, 2, "Unused" unsigned :flagSeen, 1, "Seen" unsigned :flagAnswered, 1, "Answered" unsigned :flagFlagged, 1, "Flagged" unsigned :flagDeleted, 1, "Deleted" unsigned :flagDraft, 1, "Draft" unsigned :flagRecent, 1, "Recent" end class MRKMessage < BitStruct # Note "text" for nul-terminated strings text :filename, 23*8, "FileName", :endian => :native nest :flags, MRKMessageFlags, "Flags" unsigned :uid, 32, "UID", :endian => :native unsigned :msg_size, 32, "MsgSize", :endian => :native unsigned :date, 32, "Date", :endian => :native # Now we futz with the way that date is set and gotten. # we rename the existing date field to __date, and # then we supply our own meaning for "date" that does # translation into and out of seconds-since-1970 # Again, the ideal solution would be to define a new bit-struct # field type that did this stuff itself. alias_method :__date=, :date= alias_method :__date, :date def date=(time) self.__date= time.to_i end def date Time.at(self.__date) end # we don't need to override the length computation here end File.open("imap.mrk", "rb") {|f| head_string = f.read(MRKHeader.round_byte_length) raise "No header!" unless head_string mrk_header = MRKHeader.new(head_string) puts mrk_header.inspect while msg_string = f.read(MRKMessage.round_byte_length) do puts MRKMessage.new(msg_string).inspect end } __END__ This produces (on the first bit from your file): #<MRKHeader version=1, uid_Validity=1106138982, uid_next=5825, last_write_counter=9872, unused="\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\r\n"> #<MRKMessage filename="md50000004286.msg", flags=#<MRKMessageFlags flagUnused=0, flagSeen=1, flagAnswered=1, flagFlagged=0, flagDeleted=0, flagDraft=0, flagRecent=0>, uid=4150, msg_size=20732, date=Mon Dec 19 12:18:35 Eastern Standard Time 2005> This is more what you expected, right?