X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-game%2Fsrc%2Fcodecs%2Fvmd.rs;h=ef1e60a88545f1c86aca58deb0f0232f32177572;hb=5ce3689cb46dd9014cbbc76c773963713272ad72;hp=c6d98aa2d52dcfc1b26967883372e971426f8005;hpb=9895bd7b1732a96f0b3f61bf7c95ba342bd08b1e;p=nihav.git diff --git a/nihav-game/src/codecs/vmd.rs b/nihav-game/src/codecs/vmd.rs index c6d98aa..ef1e60a 100644 --- a/nihav-game/src/codecs/vmd.rs +++ b/nihav-game/src/codecs/vmd.rs @@ -1,6 +1,8 @@ use nihav_core::codecs::*; use nihav_core::io::byteio::*; +use nihav_codec_support::codecs::HAMShuffler; use std::str::FromStr; +use std::sync::Arc; macro_rules! lz_op { (read; $dst:ident, $dpos:expr, $window:ident, $wpos:expr, $br:expr, $dst_size:expr) => { @@ -142,12 +144,12 @@ fn decode_frame_data(br: &mut ByteReader, dst: &mut [u8], mut dpos: usize, strid } Ok(false) }, - _ => return Err(DecoderError::InvalidData), + _ => Err(DecoderError::InvalidData), } } struct VMDVideoDecoder { - info: Rc, + info: NACodecInfoRef, pal: [u8; 768], buf: Vec, width: usize, @@ -158,7 +160,7 @@ struct VMDVideoDecoder { impl VMDVideoDecoder { fn new() -> Self { Self { - info: Rc::new(NACodecInfo::default()), + info: NACodecInfoRef::default(), pal: [0; 768], buf: Vec::new(), width: 0, @@ -169,7 +171,7 @@ impl VMDVideoDecoder { fn decode_frame(&mut self, br: &mut ByteReader, buf: &mut NAVideoBuffer) -> DecoderResult { let paloff = buf.get_offset(1); let stride = buf.get_stride(0); - let mut data = buf.get_data_mut(); + let data = buf.get_data_mut().unwrap(); let dst = data.as_mut_slice(); let frame_x = br.read_u16le()? as usize; @@ -179,6 +181,9 @@ impl VMDVideoDecoder { br.read_skip(1)?; let flags = br.read_byte()?; let has_pal = (flags & 0x02) != 0; + if (frame_x == 0xFFFF) && (frame_y == 0xFFFF) && (frame_l == 0xFFFF) && (frame_d == 0xFFFF) { + return Ok(false); + } validate!(frame_l >= frame_x && frame_d >= frame_y); validate!(frame_l < self.width && frame_d < self.height); @@ -202,7 +207,7 @@ impl VMDVideoDecoder { let method = br.read_byte()?; let is_intra; if (method & 0x80) != 0 { - validate!(self.buf.len() > 0); + validate!(!self.buf.is_empty()); lz_unpack(br, &mut self.buf)?; let mut mr = MemoryReader::new_read(&self.buf); let mut buf_br = ByteReader::new(&mut mr); @@ -215,12 +220,12 @@ impl VMDVideoDecoder { } impl NADecoder for VMDVideoDecoder { - fn init(&mut self, info: Rc) -> DecoderResult<()> { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { self.width = vinfo.get_width(); self.height = vinfo.get_height(); let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, false, PAL8_FORMAT)); - self.info = Rc::new(NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata())); + self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); validate!(info.get_extradata().is_some()); if let Some(ref edata) = info.get_extradata() { @@ -239,7 +244,7 @@ impl NADecoder for VMDVideoDecoder { Err(DecoderError::InvalidData) } } - fn decode(&mut self, pkt: &NAPacket) -> DecoderResult { + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { let src = pkt.get_buffer(); validate!(src.len() >= 10); @@ -251,9 +256,7 @@ impl NADecoder for VMDVideoDecoder { if let Some(bbuf) = bufret { buf = bbuf; } else { - let bufret = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 4); - if let Err(_) = bufret { return Err(DecoderError::InvalidData); } - let bufinfo = bufret.unwrap(); + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 4)?; buf = bufinfo.get_vbuf().unwrap(); self.hams.add_frame(buf); buf = self.hams.get_output_frame().unwrap(); @@ -264,21 +267,36 @@ impl NADecoder for VMDVideoDecoder { let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf)); frm.set_keyframe(is_intra); frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); - Ok(Rc::new(RefCell::new(frm))) + Ok(frm.into_ref()) + } + fn flush(&mut self) { + self.hams.clear(); } } -pub fn get_decoder_video() -> Box { +pub fn get_decoder_video() -> Box { Box::new(VMDVideoDecoder::new()) } +#[derive(Clone,Copy,PartialEq)] +enum VMDAudioMode { + U8, + DPCM, + StereoDPCM, +} + struct VMDAudioDecoder { ainfo: NAAudioInfo, + info: Arc, chmap: NAChannelMap, - is16bit: bool, blk_align: usize, blk_size: usize, + mode: VMDAudioMode, + pred: [i32; 2], + last_byte: Option, + is_odd: bool, + ch: usize, } const SOL_AUD_STEPS16: [i16; 128] = [ @@ -304,10 +322,15 @@ impl VMDAudioDecoder { fn new() -> Self { Self { ainfo: NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0), + info: NACodecInfo::new_dummy(), chmap: NAChannelMap::new(), - is16bit: false, blk_align: 0, blk_size: 0, + mode: VMDAudioMode::U8, + pred: [0; 2], + last_byte: None, + is_odd: false, + ch: 0, } } fn decode_16bit(&self, dst: &mut [i16], off1: usize, br: &mut ByteReader, nblocks: usize, mut mask: u32) -> DecoderResult<()> { @@ -324,19 +347,14 @@ impl VMDAudioDecoder { } else { let mut pred: [i32; 2] = [0; 2]; for ch in 0..channels { - pred[ch] = br.read_u16le()? as i32; + pred[ch] = i32::from(br.read_u16le()?); dst[off[ch]] = pred[ch] as i16; off[ch] += 1; } let mut ch = 0; let flip_ch = if channels == 2 { 1 } else { 0 }; - for _ in 1..self.blk_align { - let b = br.read_byte()? as usize; - if (b & 0x80) != 0 { - pred[ch] -= SOL_AUD_STEPS16[b & 0x7F] as i32; - } else { - pred[ch] += SOL_AUD_STEPS16[b & 0x7F] as i32; - } + for _ in channels..self.blk_align*channels { + pred[ch] = Self::pred16(pred[ch], br.read_byte()?); //pred[ch] = pred[ch].max(-32768).min(32767); dst[off[ch]] = pred[ch] as i16; off[ch] += 1; @@ -348,30 +366,61 @@ impl VMDAudioDecoder { validate!(br.left() == 0); Ok(()) } + fn pred16(pred: i32, val: u8) -> i32 { + if (val & 0x80) != 0 { + pred - i32::from(SOL_AUD_STEPS16[(val & 0x7F) as usize]) + } else { + pred + i32::from(SOL_AUD_STEPS16[(val & 0x7F) as usize]) + } + } + fn cvt_u8(val: u8) -> u8 { + if val < 128 { + 127 - val + } else { + val + } + } } impl NADecoder for VMDAudioDecoder { - fn init(&mut self, info: Rc) -> DecoderResult<()> { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { let fmt; + let channels = ainfo.get_channels() as usize; + let edata = info.get_extradata(); + let flags = if let Some(ref buf) = edata { + validate!(buf.len() >= 2); + (buf[0] as u16) | ((buf[1] as u16) << 8) + } else { + 0 + }; + validate!((channels == 1) ^ ((flags & 0x8200) != 0)); if ainfo.get_format().get_bits() == 8 { - fmt = SND_U8_FORMAT; - self.is16bit = false; self.blk_size = ainfo.get_block_len(); + self.blk_align = ainfo.get_block_len() / channels; + if (flags & 0x8000) == 0 { + fmt = SND_U8_FORMAT; + self.mode = VMDAudioMode::U8; + } else { + fmt = SND_S16_FORMAT; + self.mode = VMDAudioMode::StereoDPCM; + self.is_odd = (channels == 2) && ((self.blk_size & 1) != 0); + } } else { fmt = SND_S16P_FORMAT; - self.is16bit = true; - self.blk_size = (ainfo.get_block_len() + 1) * (ainfo.get_channels() as usize); + self.blk_size = (ainfo.get_block_len() + 1) * channels; + self.blk_align = ainfo.get_block_len(); + self.mode = VMDAudioMode::DPCM; }; - self.blk_align = ainfo.get_block_len() / (ainfo.get_channels() as usize); self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), fmt, ainfo.get_block_len()); - self.chmap = NAChannelMap::from_str(if ainfo.get_channels() == 1 { "C" } else { "L,R" }).unwrap(); + self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo.clone())); + self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); Ok(()) } else { Err(DecoderError::InvalidData) } } - fn decode(&mut self, pkt: &NAPacket) -> DecoderResult { + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { let info = pkt.get_stream().get_info(); if let NACodecTypeInfo::Audio(_) = info.get_properties() { let pktbuf = pkt.get_buffer(); @@ -392,45 +441,111 @@ impl NADecoder for VMDAudioDecoder { mask = 0; nblocks = 1; } - let samples = nblocks * self.blk_align; + let mut samples = nblocks * self.blk_align; + if self.mode == VMDAudioMode::StereoDPCM && self.is_odd { + samples += (nblocks + if self.last_byte.is_some() { 1 } else { 0 }) / 2; + } let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; - if self.is16bit { - let mut adata = abuf.get_abuf_i16().unwrap(); - let off1 = adata.get_offset(1); - let mut dst = adata.get_data_mut(); - self.decode_16bit(&mut dst, off1, &mut br, nblocks, mask)?; - } else { - let mut adata = abuf.get_abuf_u8().unwrap(); - let mut dst = adata.get_data_mut(); - let mut doff = 0; - let mut mask = mask; - let channels = self.chmap.num_channels(); - for _ in 0..nblocks { - if (mask & 1) != 0 { - for i in 0..self.blk_align * channels { - dst[doff + i] = 0; + match self.mode { + VMDAudioMode::DPCM => { + let mut adata = abuf.get_abuf_i16().unwrap(); + let off1 = adata.get_offset(1); + let mut dst = adata.get_data_mut().unwrap(); + self.decode_16bit(&mut dst, off1, &mut br, nblocks, mask)?; + }, + VMDAudioMode::U8 => { + let mut adata = abuf.get_abuf_u8().unwrap(); + let dst = adata.get_data_mut().unwrap(); + let mut doff = 0; + let mut mask = mask; + let channels = self.chmap.num_channels(); + for _ in 0..nblocks { + if (mask & 1) != 0 { + for i in 0..self.blk_align * channels { + dst[doff + i] = 128; + } + } else if channels == 1 { + for i in 0..self.blk_size { + dst[doff + i] = br.read_byte()?; + } + } else { + for i in 0..self.blk_size { + let val = Self::cvt_u8(br.read_byte()?); + dst[doff + i] = val; + } } - } else { - for i in 0..self.blk_align * channels { - dst[doff + i] = br.read_byte()?; + doff += self.blk_align * channels; + mask >>= 1; + } + }, + VMDAudioMode::StereoDPCM => { + let mut adata = abuf.get_abuf_i16().unwrap(); + let dst = adata.get_data_mut().unwrap(); + let mut doff = 0; + let mut mask = mask; + let mut ch = self.ch; + for _ in 0..nblocks { + let put_sample = self.last_byte.is_some(); + if let (true, Some(val)) = (self.is_odd, self.last_byte) { + self.pred[ch] = Self::pred16(self.pred[ch], val); + dst[doff] = self.pred[ch] as i16; + doff += 1; + ch ^= 1; + self.last_byte = None; } + if (mask & 1) != 0 { + for i in 0..self.blk_align { + dst[doff + i * 2 + 0] = self.pred[ch] as i16; + dst[doff + i * 2 + 1] = self.pred[ch ^ 1] as i16; + } + if self.is_odd { + if put_sample { + dst[doff + self.blk_align * 2] = self.pred[ch] as i16; + doff += 1; + ch ^= 1; + } else { + self.last_byte = Some(0); + } + } + } else { + for i in 0..self.blk_align { + self.pred[ch] = Self::pred16(self.pred[ch], br.read_byte()?); + dst[doff + i * 2] = self.pred[ch] as i16; + self.pred[ch ^ 1] = Self::pred16(self.pred[ch ^ 1], br.read_byte()?); + dst[doff + i * 2 + 1] = self.pred[ch ^ 1] as i16; + } + if self.is_odd { + let val = br.read_byte()?; + if put_sample { + self.pred[ch] = Self::pred16(self.pred[ch], val); + dst[doff + self.blk_align * 2] = self.pred[ch] as i16; + doff += 1; + ch ^= 1; + } else { + self.last_byte = Some(val); + } + } + } + doff += self.blk_align * 2; + mask >>= 1; } - doff += self.blk_align; - mask >>= 1; - } - } + self.ch = ch; + }, + }; - let mut frm = NAFrame::new_from_pkt(pkt, info, abuf); + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), abuf); frm.set_duration(Some(samples as u64)); frm.set_keyframe(true); - Ok(Rc::new(RefCell::new(frm))) + Ok(frm.into_ref()) } else { Err(DecoderError::InvalidData) } } + fn flush(&mut self) { + } } -pub fn get_decoder_audio() -> Box { +pub fn get_decoder_audio() -> Box { Box::new(VMDAudioDecoder::new()) } @@ -438,9 +553,9 @@ pub fn get_decoder_audio() -> Box { mod test { use nihav_core::codecs::RegisteredDecoders; use nihav_core::demuxers::RegisteredDemuxers; - use nihav_core::test::dec_video::*; - use crate::codecs::game_register_all_codecs; - use crate::demuxers::game_register_all_demuxers; + use nihav_codec_support::test::dec_video::*; + use crate::game_register_all_codecs; + use crate::game_register_all_demuxers; #[test] fn test_vmd_video() { let mut dmx_reg = RegisteredDemuxers::new(); @@ -448,19 +563,48 @@ mod test { let mut dec_reg = RegisteredDecoders::new(); game_register_all_codecs(&mut dec_reg); -// let file = "assets/1491.VMD"; - let file = "assets/128.vmd"; - test_file_decoding("vmd", file, Some(10), true, false, None/*Some("vmd")*/, &dmx_reg, &dec_reg); + test_decoding("vmd", "vmd-video", "assets/Game/2832.VMD", Some(10), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5Frames(vec![ + [0xd29e0214, 0xf38ad154, 0xccbd381f, 0x3de1109c], + [0x904074eb, 0x202b1d6f, 0xe3f68538, 0xf0db641c], + [0x9c8b1b6c, 0xe205b8dc, 0xbfb07406, 0x993ace41], + [0x71ce4220, 0x8747fd05, 0x854dd86d, 0x2664cde5], + [0x3bc65fa4, 0xebb95292, 0xe0a0fea6, 0x0acfdea1], + [0x33982045, 0x8d11b69b, 0xac254a75, 0x63896a21], + [0xa667db33, 0x90e122d3, 0x2243da15, 0xcc4bffd2], + [0x518621c1, 0xb91412bc, 0x12312869, 0x141ef647], + [0x3069977e, 0x68fd3fa0, 0x2bfdb00d, 0x1e694684], + [0x246c12aa, 0x15137fb0, 0xa4b0fc3e, 0x626a2676], + [0x72cce7e3, 0x98506d04, 0xd4d8bbaf, 0x3cc5e32d]])); + } + #[test] + fn test_vmd_audio_u8() { + let mut dmx_reg = RegisteredDemuxers::new(); + game_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + game_register_all_codecs(&mut dec_reg); + + test_decoding("vmd", "vmd-audio", "assets/Game/1491.VMD", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x75037601, 0xbc7b3976, 0x6e1c948b, 0xf05a3d6c])); + } + #[test] + fn test_vmd_audio_s16_old() { + let mut dmx_reg = RegisteredDemuxers::new(); + game_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + game_register_all_codecs(&mut dec_reg); + + test_decoding("vmd", "vmd-audio", "assets/Game/2832.VMD", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x32dcdf0e, 0xee058684, 0x43ed5bf1, 0x2ff18b5a])); } #[test] - fn test_vmd_audio() { + fn test_vmd_audio_s16_new() { let mut dmx_reg = RegisteredDemuxers::new(); game_register_all_demuxers(&mut dmx_reg); let mut dec_reg = RegisteredDecoders::new(); game_register_all_codecs(&mut dec_reg); -// let file = "assets/1491.VMD"; - let file = "assets/128.vmd"; - test_decode_audio("vmd", file, None, "vmd", &dmx_reg, &dec_reg); + test_decoding("vmd", "vmd-audio", "assets/Game/1000.VMD", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0xc36215d3, 0x96530a80, 0x89f1fa8e, 0x49da302b])); } }