How do I parse a binary format consisting of a hierarchy of chunks with different types? #122
-
Hey there! I'm looking into loading the map from GTA: Vice City in Bevy, and I'm interested in using binrw as a solution for my soon-to-be file format reading woes. The primary format I'd have to read is the RenderWare binary stream, detailed here: https://gtamods.com/wiki/RenderWare_binary_stream_file#File_Format This format consists of hierarchical chunks; each chunk has a header (type, size in bytes including children, library stamp) (three u32s), and is followed by its children (also chunks), and then followed by the data for the chunk itself. What's the best way to represent this in binrw - specifically, how do I choose the variant automatically based on the type? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 2 replies
-
This (sections/chunks in a file with a chunk length in bytes) is a usecase I want to make more ergonomic, maybe some way to use the take io::Read adapter.... (see #121) Anyways, how to deal with it in existing binrw, a few options:
In either case you should have a fallthrough/default "unknown chunk type" variant that either reads in the raw bytes or skips them Example 1enum Chunk {
// ...
#[br(magic = 14u32)]
FrameList {
#[br(temp)]
size: u32,
version: u32,
#[br(temp)]
frame_count: u32,
#[br(count = frame_count)]
frames: Vec<Frame>,
},
// ...
Unknown {
chunk_type: u32,
chunk_size: u32,
version: u32,
#[br(count = chunk_size)]
data: Vec<u8>,
},
} if you do something like the above, then if you find a chunk you don't know how to parse yet then it handles it gracefully Example 2if you're more interesting in doing the other way (a bit less repetition w/ header fields).... #[binread]
struct Chunk {
kind: ChunkType,
size: u32,
version: u32,
#[br(pad_size_to = size)]
#[br(args(kind, size, version))]
body: ChunkData,
}
#[binread]
#[derive(Debug, PartialEq)]
#[br(repr(u32))]
enum ChunkType {
// ...
FrameList = 14,
// ...
}
use ChunkType::*;
#[binread]
#[br(import(kind: ChunkType, size: u32, version: u32))]
enum ChunkData {
#[br(pre_assert(kind == FrameList))]
FrameList {
#[br(temp)]
frame_count: u32,
#[br(count = frame_count)]
frames: Vec<Frame>
},
} |
Beta Was this translation helpful? Give feedback.
This (sections/chunks in a file with a chunk length in bytes) is a usecase I want to make more ergonomic, maybe some way to use the take io::Read adapter.... (see #121)
Anyways, how to deal with it in existing binrw, a few options:
br(repr(u32))
enum for type, then when you want to do per-type parsing you can use pre_assert on each variant of the underlying chunk contents parser to select which variant should be used for parsingIn either case you should have a fallthrough/default "unknown chunk type" variant that either reads in the raw bytes or skips them
Example 1