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