1use binrw::{binrw, helpers::until_eof};
2use flate2::read::ZlibDecoder;
3use std::io::SeekFrom;
4
5mod checksum;
6mod error;
7
8pub use crate::{
9 checksum::{FirmwareChecksum, SECRET_VALUE},
10 error::Error,
11};
12pub type Result<T> = std::result::Result<T, Error>;
13
14#[binrw]
37#[derive(Debug, Default, Clone)]
38#[brw(big, stream = r, map_stream = FirmwareChecksum::new)]
39pub struct FirmwareFile {
40 #[brw(pad_before = 32)]
41 #[br(parse_with = until_eof)]
42 pub resources: Vec<Resource>,
43
44 #[brw(seek_before = SeekFrom::Start(0))]
45 #[br(temp, assert(checksum == r.check(), "bad checksum: {:x?} != {:x?}", checksum, r.check()))]
46 #[bw(calc(r.check()))]
47 pub checksum: [u8; 0x20],
48}
49
50#[binrw]
69#[derive(Default, PartialEq, Clone)]
70#[br(assert(usize::from(header_length) >= Self::MIN_LENGTH))]
71#[brw(big, magic = 0xBDBD0001u32)]
72pub struct Resource {
73 unknown1: u32,
74 #[bw(try_calc(u32::try_from(payload.len() + header.len() + Self::MIN_LENGTH)))]
75 length: u32,
76 #[bw(try_calc(u16::try_from(header.len() + Self::MIN_LENGTH)))]
77 header_length: u16,
78
79 pub compression: u8,
81 pub typ: u8,
82 unpacked_length: u32,
83 unknown7: u32,
84
85 #[br(count = header_length - 24)]
86 pub header: Vec<u8>,
87
88 #[br(count = length - u32::from(header_length))]
89 pub payload: Vec<u8>,
90}
91
92impl Resource {
93 pub const MIN_LENGTH: usize = 24;
94
95 pub fn decompress_payload(&self) -> ZlibDecoder<&[u8]> {
96 ZlibDecoder::new(self.payload.as_ref())
97 }
98}
99
100impl std::fmt::Debug for Resource {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("Resource")
103 .field("unknown1", &self.unknown1)
104 .field("compression", &self.compression)
105 .field("typ", &self.typ)
106 .field("unpacked_length", &self.unpacked_length)
107 .field("unknown7", &self.unknown7)
108 .field("header", &hex::encode(&self.header))
109 .field("payload", &format!("{} bytes", self.payload.len()))
110 .finish()
111 }
112}
113
114#[cfg(test)]
115mod test {
116 use super::*;
117 use binrw::{BinRead, BinWrite};
118 use std::io::Cursor;
119
120 #[test]
121 fn firmware_atem_mini() -> Result<()> {
122 let mut cmd = hex::decode("BDBD000100000000007328300030000000732800F0B8512A0001001800000000000000011EDBBE490000000000000000")?;
123 cmd.resize(cmd.len() + 7546880, 0);
124
125 let expected = Resource {
126 unknown1: 0,
127 compression: 0,
128 typ: 0,
129 unpacked_length: 7546880,
130 unknown7: 0xF0B8512A,
131 header: hex::decode("0001001800000000000000011EDBBE490000000000000000")?,
132 payload: vec![0u8; 7546880],
133 };
134 let r = Resource::read(&mut Cursor::new(&cmd))?;
135 assert_eq!(expected, r);
136
137 let mut out = Cursor::new(Vec::with_capacity(cmd.len() + 32));
138 let fw = FirmwareFile {
139 resources: vec![expected],
140 };
141 fw.write(&mut out)?;
142
143 let out = out.into_inner();
144 assert_eq!(&cmd[..512], &out[32..544]);
146 assert_eq!(
147 hex::decode("05a5716ebe99b9fbbc06b0e9add2f12cc8362f2541ba7ee19b8f5593375ab209")?,
148 &out[..32],
149 );
150
151 let _ = FirmwareFile::read(&mut Cursor::new(&out))?;
153 Ok(())
154 }
155
156 #[test]
157 fn firmware_camera_control_panel() -> Result<()> {
158 let mut cmd = hex::decode("BDBD00010000000000002F4600300100000058D4337D01C40001001800000000000300011EDBBE0E0000000000000000")?;
159 cmd.resize(cmd.len() + 12054, 0);
160
161 let expected = Resource {
162 unknown1: 0,
163 compression: 1,
164 typ: 0,
165 unpacked_length: 22740,
166 unknown7: 0x337D01C4,
167 header: hex::decode("0001001800000000000300011EDBBE0E0000000000000000")?,
168 payload: vec![0u8; 12054],
169 };
170
171 let r = Resource::read(&mut Cursor::new(&cmd))?;
172 assert_eq!(expected, r);
173
174 let mut out = Cursor::new(Vec::with_capacity(cmd.len() + 32));
175 let fw = FirmwareFile {
176 resources: vec![expected],
177 };
178 fw.write(&mut out)?;
179
180 let out = out.into_inner();
181 assert_eq!(&cmd[..512], &out[32..544]);
183 assert_eq!(
184 hex::decode("76d7bb1d94be3022d096a844236797e35575f111b0cd1efe3a8ee2d06096cf60")?,
185 &out[..32],
186 );
187
188 let _ = FirmwareFile::read(&mut Cursor::new(&out))?;
190
191 let mut cmd = hex::decode("BDBD00010000000000006D840030010400040000F2C9BEE80001001800000000FF0000011EDBBE0E0000000000000000")?;
192 cmd.resize(cmd.len() + 27988, 0);
193
194 let expected = Resource {
195 unknown1: 0,
196 compression: 1,
197 typ: 4,
198 unpacked_length: 262144,
199 unknown7: 0xf2c9bee8,
200 header: hex::decode("0001001800000000FF0000011EDBBE0E0000000000000000")?,
201 payload: vec![0u8; 27988],
202 };
203 let r = Resource::read(&mut Cursor::new(&cmd))?;
204 assert_eq!(expected, r);
205
206 let mut out = Cursor::new(Vec::with_capacity(cmd.len() + 32));
207 let fw = FirmwareFile {
208 resources: vec![expected],
209 };
210 fw.write(&mut out)?;
211
212 let out = out.into_inner();
213 assert_eq!(&cmd[..512], &out[32..544]);
215 assert_eq!(
216 hex::decode("af21c869f1396f1fcb24b382002ac800b00e0d2986e6e80bd899f78d1c5e6078")?,
217 &out[..32],
218 );
219
220 let _ = FirmwareFile::read(&mut Cursor::new(&out))?;
222 Ok(())
223 }
224}