Beam Software SIFF format support
[nihav.git] / nihav-game / src / demuxers / siff.rs
CommitLineData
561d0f79
KS
1use nihav_core::frame::*;
2use nihav_core::demuxers::*;
3
4const DEFAULT_FCP_DELAY: u64 = 100;
5const DEFAULT_VBV_DELAY: u64 = 80;
6
7#[derive(Clone,Copy,Debug,PartialEq)]
8enum SIFFType {
9 None,
10 FCP,
11 VBV,
12 Sound,
13}
14
15struct SIFFDemuxer<'a> {
16 src: &'a mut ByteReader<'a>,
17 subtype: SIFFType,
18 size: u32,
19 ablock: usize,
20 nframes: usize,
21 cframe: usize,
22 vpts: u64,
23 abuf: Vec<u8>,
24 apts: u64,
25 ver: u8,
26}
27
28impl<'a> SIFFDemuxer<'a> {
29 fn new(io: &'a mut ByteReader<'a>) -> Self {
30 Self {
31 src: io,
32 subtype: SIFFType::None,
33 size: 0,
34 ablock: 0,
35 nframes: 0,
36 cframe: 0,
37 vpts: 0,
38 abuf: Vec::new(),
39 apts: 0,
40 ver: 0,
41 }
42 }
43
44 fn parse_fcp_header(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
45 let tag = self.src.read_tag()?;
46 validate!(&tag == b"FCHD");
47 let hdr_size = self.src.read_u32be()? as usize;
48 validate!(hdr_size >= 16);
49 let mut flags = vec![0; 2];
50 self.src.read_buf(&mut flags)?;
51 let width = self.src.read_u16le()? as usize;
52 let height = self.src.read_u16le()? as usize;
53 validate!(width > 0 && height > 0);
54 self.nframes = self.src.read_u16le()? as usize;
55 self.src.read_skip(8)?;
56 self.src.read_skip(hdr_size - 16)?;
57
58 let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT);
59 let vci = NACodecTypeInfo::Video(vhdr);
60 let vinfo = NACodecInfo::new("beam-fcp", vci, Some(flags));
61 if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 1000, self.nframes as u64 * DEFAULT_FCP_DELAY)).is_none() {
62 return Err(DemuxerError::MemoryError);
63 }
64
65 self.ablock = 1;
66 let srate = 22050;
67 let ahdr = NAAudioInfo::new(srate, 1, SND_U8_FORMAT, 1);
68 let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None);
69 if strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate, 0)).is_none() {
70 return Err(DemuxerError::MemoryError);
71 }
72
73 self.vpts = 0;
74 self.apts = 0;
75 self.cframe = 0;
76
77 Ok(())
78 }
79 fn get_fcp_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
80 if self.cframe >= self.nframes {
81 return Err(DemuxerError::EOF);
82 }
83 let size = self.src.read_u16le()? as usize;
84 validate!(size > 8);
85 let stream = strmgr.get_stream(0).unwrap();
86 let (tb_num, tb_den) = stream.get_timebase();
87 let ts = NATimeInfo::new(Some(self.vpts), None, None, tb_num, tb_den);
88 let kframe = self.vpts == 0;
89 self.cframe += 1;
90 let pkt = self.src.read_packet(stream, ts, kframe, size - 2)?;
91 let buf = pkt.get_buffer();
92
93 let mut mr = MemoryReader::new_read(buf.as_slice());
94 let mut br = ByteReader::new(&mut mr);
95 let asize = br.read_u16le()? as usize;
96 let duration = br.read_u16le()? as u64;
97 validate!(asize < buf.len());
98 if asize > 0 {
99 let nclrs = br.read_u16le()? as usize;
100 if nclrs > 0 {
101 br.read_skip(nclrs * 3 + 2)?;
102 }
103 self.abuf.resize(asize, 0);
104 br.read_buf(&mut self.abuf)?;
105 }
106
107 if duration > 0 {
108 self.vpts += duration;
109 } else {
110 self.vpts += DEFAULT_FCP_DELAY;
111 }
112
113 Ok(pkt)
114 }
115
116 fn parse_vbv_header(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
117 let tag = self.src.read_tag()?;
118 validate!(&tag == b"VBHD");
119 let hdr_size = self.src.read_u32be()? as usize;
120 validate!(hdr_size >= 32);
121 let version = self.src.read_u16le()?;
122 validate!(version == 1 || version == 2);
123 self.ver = version as u8;
124 let width = self.src.read_u16le()? as usize;
125 let height = self.src.read_u16le()? as usize;
126 validate!(width > 0 && height > 0);
127 self.src.read_skip(4)?;
128 self.nframes = self.src.read_u16le()? as usize;
129 let flags = self.src.read_u16le()?;
130 let bits = flags as u8;
131 let channels = if (flags & 0x100) != 0 { 2 } else { 1 };
132 validate!(bits == 0 || bits >= 8);
133 let srate = self.src.read_u16le()? as u32;
134 self.ablock = (bits as usize) * (channels as usize) / 8;
135 self.src.read_skip(16)?;
136 self.src.read_skip(hdr_size - 32)?;
137
138 let mut vhdr = NAVideoInfo::new(width, height, false, if version == 1 { PAL8_FORMAT } else { RGB565_FORMAT });
139 vhdr.bits = version as u8 * 8;
140 let vci = NACodecTypeInfo::Video(vhdr);
141 let vinfo = NACodecInfo::new("beam-video", vci, None);
142 if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 1000, self.nframes as u64 * DEFAULT_VBV_DELAY)).is_none() {
143 return Err(DemuxerError::MemoryError);
144 }
145
146 if srate > 0 && bits > 0 {
147 let ahdr = NAAudioInfo::new(srate, channels, if bits == 8 { SND_U8_FORMAT } else { SND_S16_FORMAT }, self.ablock);
148 let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None);
149 if strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate, 0)).is_none() {
150 return Err(DemuxerError::MemoryError);
151 }
152 }
153
154 self.vpts = 0;
155 self.apts = 0;
156 self.cframe = 0;
157
158 Ok(())
159 }
160 fn get_vbv_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
161 if self.cframe >= self.nframes {
162 return Err(DemuxerError::EOF);
163 }
164 let size = self.src.read_u32le()? as usize;
165 validate!(size > 6);
166 let stream = strmgr.get_stream(0).unwrap();
167 let (tb_num, tb_den) = stream.get_timebase();
168 let ts = NATimeInfo::new(Some(self.vpts), None, None, tb_num, tb_den);
169 let kframe = self.vpts == 0;
170 self.cframe += 1;
171 let pkt = self.src.read_packet(stream, ts, kframe, size - 4)?;
172 let buf = pkt.get_buffer();
173
174 let mut mr = MemoryReader::new_read(buf.as_slice());
175 let mut br = ByteReader::new(&mut mr);
176 let flags = br.read_u16le()?;
177 if (flags & 0x01) != 0 {
178 br.read_skip(4)?;
179 }
180 if (flags & 0x04) != 0 {
181 let asize = br.read_u32le()? as usize;
182 validate!((asize > 4) && asize < (buf.len() - (br.tell() as usize)));
183 self.abuf.resize(asize - 4, 0);
184 br.read_buf(&mut self.abuf)?;
185 }
186 if (flags & 0x08) != 0 {
187 let vsize = br.read_u32le()? as usize;
188 validate!(vsize > 4);
189 br.read_skip(vsize - 4)?;
190 }
191 if (flags & 0x10) != 0 {
192 let psize = br.read_u32le()? as usize;
193 validate!(psize > 4);
194 br.read_skip(psize - 4)?;
195 }
196 let delay = if (flags & 0x20) != 0 {
197 br.read_u16le()? as u64
198 } else { 0 };
199 self.vpts += if delay > 0 { delay } else { DEFAULT_VBV_DELAY };
200
201 Ok(pkt)
202 }
203
204 fn parse_snd_header(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
205 let tag = self.src.read_tag()?;
206 validate!(&tag == b"SHDR");
207 let hdr_size = self.src.read_u32be()? as usize;
208 validate!(hdr_size >= 8);
209 let snd_size = self.src.read_u32le()?;
210 let srate = self.src.read_u16le()? as u32;
211 let flags = self.src.read_u16le()?;
212 let bits = flags as u8;
213 validate!(bits >= 8);
214 let channels = if (flags & 0x100) != 0 { 2 } else { 1 };
215 self.ablock = (bits as usize) * (channels as usize);
216 self.src.read_skip(hdr_size - 8)?;
217
218 let duration = u64::from(snd_size) / u64::from(channels) * 8 / u64::from(bits);
219
220 let fmt = match bits {
221 8 => SND_U8_FORMAT,
222 16 => SND_S16_FORMAT,
223 12 => NASoniton::new(12, SONITON_FLAG_PACKED | SONITON_FLAG_SIGNED),
224 _ => return Err(DemuxerError::NotImplemented),
225 };
226 let ahdr = NAAudioInfo::new(srate, channels, fmt, self.ablock);
227 let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None);
228 if strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, srate, duration)).is_none() {
229 return Err(DemuxerError::MemoryError);
230 }
231
232 Ok(())
233 }
234 fn get_snd_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
235 if self.size == 0 {
236 return Err(DemuxerError::EOF);
237 }
238 let cur_size = self.size.min(1024 * (self.ablock as u32));
239
240 let stream = strmgr.get_stream(0).unwrap();
241 let (tb_num, tb_den) = stream.get_timebase();
242 let ts = NATimeInfo::new(None, None, None, tb_num, tb_den);
243 let pkt = self.src.read_packet(stream, ts, true, cur_size as usize)?;
244 self.size -= cur_size;
245
246 Ok(pkt)
247 }
248}
249
250impl<'a> DemuxCore<'a> for SIFFDemuxer<'a> {
251 fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
252 let magic = self.src.read_tag()?;
253 validate!(&magic == b"SIFF");
254 self.size = self.src.read_u32be()?;
255 let tag = self.src.read_tag()?;
256 self.subtype = match &tag {
257 b"FCPK" => SIFFType::FCP,
258 b"VBV1" => SIFFType::VBV,
259 b"SOUN" => SIFFType::Sound,
260 _ => return Err(DemuxerError::NotImplemented),
261 };
262
263 match self.subtype {
264 SIFFType::FCP => self.parse_fcp_header(strmgr)?,
265 SIFFType::VBV => self.parse_vbv_header(strmgr)?,
266 SIFFType::Sound => self.parse_snd_header(strmgr)?,
267 _ => unreachable!(),
268 };
269
270 let tag = self.src.read_tag()?;
271 validate!(&tag == b"BODY");
272 let body_size = self.src.read_u32be()?;
273 validate!(self.src.tell() + u64::from(body_size) <= u64::from(self.size) + 8);
274 self.size = body_size;
275
276 Ok(())
277 }
278
279 fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
280 if !self.abuf.is_empty() {
281 let mut buf = Vec::new();
282 std::mem::swap(&mut buf, &mut self.abuf);
283
284 if let Some(stream) = strmgr.get_stream(1) {
285 let (tb_num, tb_den) = stream.get_timebase();
286 let ts = NATimeInfo::new(Some(self.apts), None, None, tb_num, tb_den);
287 self.apts += (buf.len() / self.ablock) as u64;
288 return Ok(NAPacket::new(stream, ts, true, buf));
289 }
290 }
291 match self.subtype {
292 SIFFType::FCP => self.get_fcp_frame(strmgr),
293 SIFFType::VBV => self.get_vbv_frame(strmgr),
294 SIFFType::Sound => self.get_snd_frame(strmgr),
295 _ => unreachable!(),
296 }
297 }
298
299 fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
300 Err(DemuxerError::NotPossible)
301 }
302 fn get_duration(&self) -> u64 { 0 }
303}
304
305impl<'a> NAOptionHandler for SIFFDemuxer<'a> {
306 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
307 fn set_options(&mut self, _options: &[NAOption]) { }
308 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
309}
310
311pub struct SIFFDemuxerCreator { }
312
313impl DemuxerCreator for SIFFDemuxerCreator {
314 fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
315 Box::new(SIFFDemuxer::new(br))
316 }
317 fn get_name(&self) -> &'static str { "siff" }
318}
319
320#[cfg(test)]
321mod test {
322 use super::*;
323 use std::fs::File;
324
325 fn test_siff_demux(name: &str) {
326 let mut file = File::open(name).unwrap();
327 let mut fr = FileReader::new_read(&mut file);
328 let mut br = ByteReader::new(&mut fr);
329 let mut dmx = SIFFDemuxer::new(&mut br);
330 let mut sm = StreamManager::new();
331 let mut si = SeekIndex::new();
332 dmx.open(&mut sm, &mut si).unwrap();
333 loop {
334 let pktres = dmx.get_frame(&mut sm);
335 if let Err(e) = pktres {
336 if (e as i32) == (DemuxerError::EOF as i32) { break; }
337 panic!("error");
338 }
339 let pkt = pktres.unwrap();
340 println!("Got {}", pkt);
341 }
342 }
343
344 #[test]
345 fn test_siff_demux_fcp() {
346 // sample from The Dame was Loaded game
347 test_siff_demux("assets/Game/siff/BEAM.FCP");
348 }
349 #[test]
350 fn test_siff_demux_anim_8bit() {
351 // sample from Lost Vikings 2 game
352 test_siff_demux("assets/Game/siff/BEAM.VB");
353 }
354 #[test]
355 fn test_siff_demux_anim_16bit() {
356 // sample from Alien Earth game
357 test_siff_demux("assets/Game/siff/beamlogo.vbc");
358 }
359 #[test]
360 fn test_siff_demux_snd() {
361 // sample from The Dame was Loaded game
362 test_siff_demux("assets/Game/siff/01AFIRST.SON");
363 // sample from Lost Vikings 2 game
364 test_siff_demux("assets/Game/siff/01THEME.SON");
365 }
366}