Based on the Choice syntax section of the wiki (and some of the tests), I think it would look something like this (caveat emptor: I don't have access to your data so I really have no idea if this works):
class Property < BinData::Record
endian :little
int32 :name_len
string :name, read_length: :name_len
int32 :type_len
string :type, read_length: :type_len
int64 :data_len
choice :data, selection: :type do
# if type is "IntProperty" then the next four bytes (int32) is an int.
int32 "IntProperty"
# if type is "FloatProperty" then the next four bytes (float_32) is a float.
float "FloatProperty"
# if type is "StringProperty" then the next four bytes (int32) is an int (len
# of string) and the next (len * 8) are the string itself.
struct "StringProperty" do
int32 :len
string :data, read_length: :len
end
# if type is "ArrayProperty" then the next four bytes (int32) is an int (len
# of array), then next however many bytes is len of array many Property objects
# (to store in an array).
struct "ArrayProperty" do
int32 :num_properties
array :properties, type: :property, initial_length: :num_items
end
end
end
I think it would be beneficial, however, to split this up into classes. We have int32
/string
pairs in a few places, so let's abstract those out into their own class:
class StringRecord < BinData::Record
endian :little
int32 :len, value: -> { data.length }
string :data, read_length: :len
end
If you want, you could also make that a Primitive.
And one the "PropertyArray"
type:
class ArrayOfProperties < BinData::Record
endian :little
int32 :num_properties, value: -> { properties.size }
array :properties, type: :property, initial_length: :num_items
end
Now our Property class looks a lot cleaner:
class Property < BinData::Record
endian :little
string_record :name
string_record :type
int64 :data_len
choice :data, selection: :type do
int32 "IntProperty"
float "FloatProperty"
string_record "StringProperty"
array_of_properties "ArrayProperty"
end
end
I've specified :value
options for the length fields, but if you're using this for reading and not writing you could skip them. I'm not quite sure how to write the :value
option for :data_len
; perhaps something like value: -> { data.num_bytes }
.
Again, I have no idea if any of the above will work, but hopefully it'll help.