use binrw::{binrw, helpers::until_eof};
use flate2::read::ZlibDecoder;
use std::io::SeekFrom;
mod checksum;
mod error;
pub use crate::{
checksum::{FirmwareChecksum, SECRET_VALUE},
error::Error,
};
pub type Result<T> = std::result::Result<T, Error>;
#[binrw]
#[derive(Debug, Default, Clone)]
#[brw(big, stream = r, map_stream = FirmwareChecksum::new)]
pub struct FirmwareFile {
#[brw(pad_before = 32)]
#[br(parse_with = until_eof)]
pub resources: Vec<Resource>,
#[brw(seek_before = SeekFrom::Start(0))]
#[br(temp, assert(checksum == r.check(), "bad checksum: {:x?} != {:x?}", checksum, r.check()))]
#[bw(calc(r.check()))]
pub checksum: [u8; 0x20],
}
#[binrw]
#[derive(Default, PartialEq, Clone)]
#[br(assert(usize::from(header_length) >= Self::MIN_LENGTH))]
#[brw(big, magic = 0xBDBD0001u32)]
pub struct Resource {
unknown1: u32,
#[bw(try_calc(u32::try_from(payload.len() + header.len() + Self::MIN_LENGTH)))]
length: u32,
#[bw(try_calc(u16::try_from(header.len() + Self::MIN_LENGTH)))]
header_length: u16,
pub compression: u8,
pub typ: u8,
unpacked_length: u32,
unknown7: u32,
#[br(count = header_length - 24)]
pub header: Vec<u8>,
#[br(count = length - u32::from(header_length))]
pub payload: Vec<u8>,
}
impl Resource {
pub const MIN_LENGTH: usize = 24;
pub fn decompress_payload(&self) -> ZlibDecoder<&[u8]> {
ZlibDecoder::new(self.payload.as_ref())
}
}
impl std::fmt::Debug for Resource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Resource")
.field("unknown1", &self.unknown1)
.field("compression", &self.compression)
.field("typ", &self.typ)
.field("unpacked_length", &self.unpacked_length)
.field("unknown7", &self.unknown7)
.field("header", &hex::encode(&self.header))
.field("payload", &format!("{} bytes", self.payload.len()))
.finish()
}
}
#[cfg(test)]
mod test {
use super::*;
use binrw::{BinRead, BinWrite};
use std::io::Cursor;
#[test]
fn firmware_atem_mini() -> Result<()> {
let mut cmd = hex::decode("BDBD000100000000007328300030000000732800F0B8512A0001001800000000000000011EDBBE490000000000000000")?;
cmd.resize(cmd.len() + 7546880, 0);
let expected = Resource {
unknown1: 0,
compression: 0,
typ: 0,
unpacked_length: 7546880,
unknown7: 0xF0B8512A,
header: hex::decode("0001001800000000000000011EDBBE490000000000000000")?,
payload: vec![0u8; 7546880],
};
let r = Resource::read(&mut Cursor::new(&cmd))?;
assert_eq!(expected, r);
let mut out = Cursor::new(Vec::with_capacity(cmd.len() + 32));
let fw = FirmwareFile {
resources: vec![expected],
};
fw.write(&mut out)?;
let out = out.into_inner();
assert_eq!(&cmd[..512], &out[32..544]);
assert_eq!(
hex::decode("05a5716ebe99b9fbbc06b0e9add2f12cc8362f2541ba7ee19b8f5593375ab209")?,
&out[..32],
);
let _ = FirmwareFile::read(&mut Cursor::new(&out))?;
Ok(())
}
#[test]
fn firmware_camera_control_panel() -> Result<()> {
let mut cmd = hex::decode("BDBD00010000000000002F4600300100000058D4337D01C40001001800000000000300011EDBBE0E0000000000000000")?;
cmd.resize(cmd.len() + 12054, 0);
let expected = Resource {
unknown1: 0,
compression: 1,
typ: 0,
unpacked_length: 22740,
unknown7: 0x337D01C4,
header: hex::decode("0001001800000000000300011EDBBE0E0000000000000000")?,
payload: vec![0u8; 12054],
};
let r = Resource::read(&mut Cursor::new(&cmd))?;
assert_eq!(expected, r);
let mut out = Cursor::new(Vec::with_capacity(cmd.len() + 32));
let fw = FirmwareFile {
resources: vec![expected],
};
fw.write(&mut out)?;
let out = out.into_inner();
assert_eq!(&cmd[..512], &out[32..544]);
assert_eq!(
hex::decode("76d7bb1d94be3022d096a844236797e35575f111b0cd1efe3a8ee2d06096cf60")?,
&out[..32],
);
let _ = FirmwareFile::read(&mut Cursor::new(&out))?;
let mut cmd = hex::decode("BDBD00010000000000006D840030010400040000F2C9BEE80001001800000000FF0000011EDBBE0E0000000000000000")?;
cmd.resize(cmd.len() + 27988, 0);
let expected = Resource {
unknown1: 0,
compression: 1,
typ: 4,
unpacked_length: 262144,
unknown7: 0xf2c9bee8,
header: hex::decode("0001001800000000FF0000011EDBBE0E0000000000000000")?,
payload: vec![0u8; 27988],
};
let r = Resource::read(&mut Cursor::new(&cmd))?;
assert_eq!(expected, r);
let mut out = Cursor::new(Vec::with_capacity(cmd.len() + 32));
let fw = FirmwareFile {
resources: vec![expected],
};
fw.write(&mut out)?;
let out = out.into_inner();
assert_eq!(&cmd[..512], &out[32..544]);
assert_eq!(
hex::decode("af21c869f1396f1fcb24b382002ac800b00e0d2986e6e80bd899f78d1c5e6078")?,
&out[..32],
);
let _ = FirmwareFile::read(&mut Cursor::new(&out))?;
Ok(())
}
}