X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-game%2Fsrc%2Fcodecs%2Fvmd.rs;h=ef1e60a88545f1c86aca58deb0f0232f32177572;hb=5ce3689cb46dd9014cbbc76c773963713272ad72;hp=1f4964e7d2d9f487ce2faf514fbec8f51ad2a5c3;hpb=e64739f87a35f29be0bbbce366876180ba3eb57e;p=nihav.git diff --git a/nihav-game/src/codecs/vmd.rs b/nihav-game/src/codecs/vmd.rs index 1f4964e..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) => { @@ -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); @@ -274,12 +279,24 @@ 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] = [ @@ -305,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<()> { @@ -332,12 +354,7 @@ impl VMDAudioDecoder { let mut ch = 0; let flip_ch = if channels == 2 { 1 } else { 0 }; for _ in channels..self.blk_align*channels { - let b = br.read_byte()? as usize; - if (b & 0x80) != 0 { - pred[ch] -= i32::from(SOL_AUD_STEPS16[b & 0x7F]); - } else { - pred[ch] += i32::from(SOL_AUD_STEPS16[b & 0x7F]); - } + 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; @@ -349,25 +366,55 @@ 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, _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() / (ainfo.get_channels() as usize); + 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.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) @@ -394,44 +441,99 @@ 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().unwrap(); - self.decode_16bit(&mut dst, off1, &mut br, nblocks, mask)?; - } else { - 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] = 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 if channels == 1 { - for i in 0..self.blk_size { - 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; } - } else { - for i in 0..self.blk_size { - let val = br.read_byte()?; - if val < 128 { - dst[doff + i] = 127 - val; - } else { - dst[doff + i] = val; + 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 * channels; - 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(frm.into_ref()) @@ -451,7 +553,7 @@ 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 nihav_codec_support::test::dec_video::*; use crate::game_register_all_codecs; use crate::game_register_all_demuxers; #[test] @@ -461,21 +563,48 @@ mod test { let mut dec_reg = RegisteredDecoders::new(); game_register_all_codecs(&mut dec_reg); -// let file = "assets/Game/1491.VMD"; - let file = "assets/Game/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/Game/1491.VMD"; - let file = "assets/Game/128.vmd"; -// let file = "assets/Game/1000.VMD"; -// let file = "assets/Game/235.VMD"; - test_decode_audio("vmd", file, None, None/*Some("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])); } }