use anyhow::{bail, Result};
use std::fs::File;
use std::io::{Seek, SeekFrom, Write};
pub struct BinFileWriter {
file: File,
n_sections: u32,
sections_written: u32,
section_start: Option<u64>,
}
impl BinFileWriter {
pub fn new(path: &str, file_type: &str, version: u32, n_sections: u32) -> Result<Self> {
assert!(file_type.len() == 4, "File type must be exactly 4 bytes");
let mut file = File::create(path)?;
file.write_all(file_type.as_bytes())?;
file.write_all(&version.to_le_bytes())?;
file.write_all(&n_sections.to_le_bytes())?;
Ok(BinFileWriter { file, n_sections, sections_written: 0, section_start: None })
}
pub fn start_write_section(&mut self, section_id: u32) -> Result<()> {
if self.section_start.is_some() {
bail!("Already writing a section");
}
let pos = self.file.stream_position()?;
self.section_start = Some(pos);
self.write_u32(section_id)?;
self.write_u64(0)?;
Ok(())
}
pub fn end_write_section(&mut self) -> Result<()> {
let start = match self.section_start {
Some(s) => s,
None => bail!("Not writing a section"),
};
let current_pos = self.file.stream_position()?;
let section_size = current_pos - start - 12;
self.file.seek(SeekFrom::Start(start + 4))?;
self.file.write_all(§ion_size.to_le_bytes())?;
self.file.seek(SeekFrom::Start(current_pos))?;
self.section_start = None;
self.sections_written += 1;
Ok(())
}
pub fn write_u8(&mut self, value: u8) -> Result<()> {
self.file.write_all(&[value])?;
Ok(())
}
pub fn write_u16(&mut self, value: u16) -> Result<()> {
self.file.write_all(&value.to_le_bytes())?;
Ok(())
}
pub fn write_u32(&mut self, value: u32) -> Result<()> {
self.file.write_all(&value.to_le_bytes())?;
Ok(())
}
pub fn write_u64(&mut self, value: u64) -> Result<()> {
self.file.write_all(&value.to_le_bytes())?;
Ok(())
}
pub fn write_string(&mut self, s: &str) -> Result<()> {
self.file.write_all(s.as_bytes())?;
self.file.write_all(&[0u8])?;
Ok(())
}
pub fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
self.file.write_all(data)?;
Ok(())
}
pub fn close(self) -> Result<()> {
if self.sections_written != self.n_sections {
eprintln!("Warning: expected {} sections but only {} were written", self.n_sections, self.sections_written);
}
Ok(())
}
}