X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-game%2Fsrc%2Fcodecs%2Fvmd.rs;h=40d8c5e9b8413ac37eecaafb9c9c12aa4aecb4e4;hp=b81b4f5f1cf19d4ae41744d0da50702968a56448;hb=78fb6560c73965d834b215fb0b49505ae5443288;hpb=ed6c9f8a7b4c723b02361bf75fdccdc40eb427cc diff --git a/nihav-game/src/codecs/vmd.rs b/nihav-game/src/codecs/vmd.rs index b81b4f5..40d8c5e 100644 --- a/nihav-game/src/codecs/vmd.rs +++ b/nihav-game/src/codecs/vmd.rs @@ -1,6 +1,6 @@ use nihav_core::codecs::*; use nihav_core::io::byteio::*; -use nihav_codec_support::codecs::HAMShuffler; +use nihav_codec_support::codecs::imaadpcm::*; use std::str::FromStr; use std::sync::Arc; @@ -85,9 +85,11 @@ fn rle_unpack(br: &mut ByteReader, len: usize, dst: &mut [u8]) -> DecoderResult< let dst = &mut dst[dpos..][..len]; br.read_buf(dst)?; } else { - let val = br.read_byte()?; - for i in 0..len { - dst[dpos + i] = val; + let val1 = br.read_byte()?; + let val2 = br.read_byte()?; + for i in (0..len).step_by(2) { + dst[dpos + i] = val1; + dst[dpos + i + 1] = val2; } } dpos += len; @@ -152,11 +154,15 @@ struct VMDVideoDecoder { info: NACodecInfoRef, pal: [u8; 768], buf: Vec, + framebuf: Vec, width: usize, height: usize, xoff: usize, yoff: usize, - hams: HAMShuffler, + is_16bit: bool, + is_24bit: bool, + ver1: u8, + ver2: u8, } impl VMDVideoDecoder { @@ -165,32 +171,31 @@ impl VMDVideoDecoder { info: NACodecInfoRef::default(), pal: [0; 768], buf: Vec::new(), + framebuf: Vec::new(), width: 0, height: 0, xoff: 0, yoff: 0, - hams: HAMShuffler::default(), + is_16bit: false, + is_24bit: false, + ver1: 0, + ver2: 0, } } - 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 data = buf.get_data_mut().unwrap(); - let dst = data.as_mut_slice(); - + fn decode_frame(&mut self, br: &mut ByteReader) -> DecoderResult { let frame_x = br.read_u16le()? as usize; let frame_y = br.read_u16le()? as usize; - let frame_l = br.read_u16le()? as usize; + let frame_r = br.read_u16le()? as usize; let frame_d = br.read_u16le()? as usize; 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) { + let has_pal = (flags & 0x02) != 0 && !self.is_16bit && !self.is_24bit; + if (frame_x == 0xFFFF) && (frame_y == 0xFFFF) && (frame_r == 0xFFFF) && (frame_d == 0xFFFF) { return Ok(false); } validate!(frame_x >= self.xoff && frame_y >= self.yoff); - validate!(frame_l >= frame_x && frame_d >= frame_y); - validate!(frame_l - self.xoff < self.width && frame_d - self.yoff < self.height); + validate!(frame_r >= frame_x && frame_d >= frame_y); + validate!(frame_r - self.xoff < self.width && frame_d - self.yoff < self.height); if has_pal { br.read_skip(2)?; @@ -200,14 +205,19 @@ impl VMDVideoDecoder { } } - let dpal = &mut dst[paloff..][..768]; - dpal.copy_from_slice(&self.pal[0..]); - if br.left() == 0 { return Ok(false); } - let w = frame_l + 1 - frame_x; + let bpp = if (!self.is_16bit && !self.is_24bit) || self.ver1 < 2 { + 1 + } else if self.is_16bit { + 2 + } else { + 3 + }; + let w = (frame_r + 1 - frame_x) * bpp; let h = frame_d + 1 - frame_y; - let dpos = frame_x - self.xoff + (frame_y - self.yoff) * stride; + let stride = self.width; + let dpos = (frame_x - self.xoff) * bpp + (frame_y - self.yoff) * stride; let method = br.read_byte()?; let is_intra; @@ -216,21 +226,27 @@ impl VMDVideoDecoder { lz_unpack(br, &mut self.buf)?; let mut mr = MemoryReader::new_read(&self.buf); let mut buf_br = ByteReader::new(&mut mr); - is_intra = decode_frame_data(&mut buf_br, dst, dpos, stride, w, h, method & 0x7F)?; + is_intra = decode_frame_data(&mut buf_br, &mut self.framebuf, dpos, stride, w, h, method & 0x7F)?; } else { - is_intra = decode_frame_data(br, dst, dpos, stride, w, h, method & 0x7F)?; + is_intra = decode_frame_data(br, &mut self.framebuf, dpos, stride, w, h, method & 0x7F)?; } Ok(is_intra && frame_x == 0 && frame_y == 0 && w == self.width && h == self.height) } } +const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, + comp_info: [ + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 1, next_elem: 2 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 2, next_elem: 2 }), + None, None], + elem_size: 2, be: false, alpha: false, palette: false }; + impl NADecoder for VMDVideoDecoder { 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 = 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() { @@ -244,7 +260,31 @@ impl NADecoder for VMDVideoDecoder { } self.xoff = read_u16le(&edata[8..])? as usize; self.yoff = read_u16le(&edata[10..])? as usize; + self.ver1 = edata[2]; + self.ver2 = edata[4]; + } else { + unreachable!(); } + let (disp_width, fmt) = if self.ver2 < 5 { + (self.width, PAL8_FORMAT) + } else if self.ver2 < 13 { + self.is_24bit = true; + if self.ver1 >= 2 { + self.width *= 3; + } + validate!(self.width % 3 == 0); + (self.width / 3, RGB24_FORMAT) + } else { + self.is_16bit = true; + if self.ver1 >= 2 { + self.width *= 2; + } + (self.width / 2, RGB555_FORMAT) + }; + self.framebuf = vec!(0; self.width * self.height); + + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(disp_width, self.height, false, fmt)); + self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); Ok(()) } else { @@ -258,29 +298,49 @@ impl NADecoder for VMDVideoDecoder { let mut mr = MemoryReader::new_read(&src); let mut br = ByteReader::new(&mut mr); - let mut buf; - let bufret = self.hams.clone_ref(); - if let Some(bbuf) = bufret { - buf = bbuf; + let is_intra = self.decode_frame(&mut br)?; + + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 4)?; + let videobuf; + if !self.is_16bit { + let mut buf = bufinfo.get_vbuf().unwrap(); + let stride = buf.get_stride(0); + let paloff = buf.get_offset(1); + let data = buf.get_data_mut().unwrap(); + for (inrow, outrow) in self.framebuf.chunks(self.width).zip(data.chunks_mut(stride)) { + (&mut outrow[..self.width]).copy_from_slice(inrow); + } + if !self.is_24bit { + (&mut data[paloff..][..768]).copy_from_slice(&self.pal); + } + videobuf = if !self.is_24bit { NABufferType::Video(buf) } else { NABufferType::VideoPacked(buf) }; } else { - 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(); + let mut buf = bufinfo.get_vbuf16().unwrap(); + let stride = buf.get_stride(0); + let data = buf.get_data_mut().unwrap(); + for (inrow, outrow) in self.framebuf.chunks(self.width).zip(data.chunks_mut(stride)) { + for i in (0..self.width).step_by(2) { + outrow[i >> 1] = read_u16le(&inrow[i..])?; + } + } + videobuf = NABufferType::Video16(buf); } - let is_intra = self.decode_frame(&mut br, &mut buf)?; - - let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf)); + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), videobuf); frm.set_keyframe(is_intra); frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); Ok(frm.into_ref()) } fn flush(&mut self) { - self.hams.clear(); } } +impl NAOptionHandler for VMDVideoDecoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + pub fn get_decoder_video() -> Box { Box::new(VMDVideoDecoder::new()) @@ -291,6 +351,7 @@ enum VMDAudioMode { U8, DPCM, StereoDPCM, + ADPCM, } struct VMDAudioDecoder { @@ -380,9 +441,6 @@ impl VMDAudioDecoder { pred + i32::from(SOL_AUD_STEPS16[(val & 0x7F) as usize]) } } - fn cvt_u8(val: u8) -> u8 { - val ^ 0x80 - } } impl NADecoder for VMDAudioDecoder { @@ -393,7 +451,7 @@ impl NADecoder for VMDAudioDecoder { 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) + u16::from(buf[0]) | (u16::from(buf[1]) << 8) } else { 0 }; @@ -410,19 +468,27 @@ impl NADecoder for VMDAudioDecoder { self.is_odd = (channels == 2) && ((self.blk_size & 1) != 0); } } else { - fmt = SND_S16P_FORMAT; - self.blk_size = (ainfo.get_block_len() + 1) * channels; self.blk_align = ainfo.get_block_len(); - self.mode = VMDAudioMode::DPCM; + if (flags & 0x10) == 0 { + fmt = SND_S16P_FORMAT; + self.blk_size = (ainfo.get_block_len() + 1) * channels; + self.mode = VMDAudioMode::DPCM; + } else { + fmt = SND_S16_FORMAT; + self.blk_size = (ainfo.get_block_len() * channels + 1) / 2 + 3 * channels; + self.mode = VMDAudioMode::ADPCM; + } }; self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), fmt, ainfo.get_block_len()); - self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo.clone())); + self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo)); self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap(); Ok(()) } else { Err(DecoderError::InvalidData) } } + #[allow(clippy::identity_op)] + #[allow(clippy::cyclomatic_complexity)] fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { let info = pkt.get_stream().get_info(); if let NACodecTypeInfo::Audio(_) = info.get_properties() { @@ -472,10 +538,7 @@ impl NADecoder for VMDAudioDecoder { 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; - } + unreachable!(); } doff += self.blk_align * channels; mask >>= 1; @@ -534,6 +597,70 @@ impl NADecoder for VMDAudioDecoder { } self.ch = ch; }, + VMDAudioMode::ADPCM => { + let mut adata = abuf.get_abuf_i16().unwrap(); + let dst = adata.get_data_mut().unwrap(); + let mut doff = 0; + if self.chmap.num_channels() == 1 { + let mut mask = mask; + let mut ima = IMAState::new(); + for _ in 0..nblocks { + if (mask & 1) != 0 { + for i in 0..self.blk_align { + dst[doff + i] = 0; + } + doff += self.blk_align; + mask >>= 1; + continue; + } + let pred = br.read_u16le()? as i16; + let step = br.read_byte()?; + validate!((step as usize) < IMA_STEP_TABLE.len()); + ima.reset(pred, step); + let mut b = 0; + for i in 0..self.blk_align { + if (i & 1) == 0 { + b = br.read_byte()?; + dst[doff] = ima.expand_sample(b >> 4); + } else { + dst[doff] = ima.expand_sample(b & 0xF); + } + doff += 1; + } + mask >>= 1; + } + } else { + let mut mask = mask; + let mut ima1 = IMAState::new(); + let mut ima2 = IMAState::new(); + for _ in 0..nblocks { + if (mask & 1) != 0 { + for i in 0..self.blk_align * 2 { + dst[doff + i] = 0; + } + doff += self.blk_align * 2; + mask >>= 1; + continue; + } + let pred1 = br.read_u16le()? as i16; + let pred2 = br.read_u16le()? as i16; + let step1 = br.read_byte()?; + let step2 = br.read_byte()?; + validate!((step1 as usize) < IMA_STEP_TABLE.len()); + validate!((step2 as usize) < IMA_STEP_TABLE.len()); + ima1.reset(pred1, step1); + ima2.reset(pred2, step2); + for _ in 0..self.blk_align { + let b = br.read_byte()?; + dst[doff] = ima1.expand_sample(b >> 4); + doff += 1; + dst[doff] = ima2.expand_sample(b & 0xF); + doff += 1; + } + mask >>= 1; + } + } + }, }; let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), abuf); @@ -548,6 +675,12 @@ impl NADecoder for VMDAudioDecoder { } } +impl NAOptionHandler for VMDAudioDecoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + pub fn get_decoder_audio() -> Box { Box::new(VMDAudioDecoder::new()) } @@ -557,14 +690,14 @@ mod test { use nihav_core::codecs::RegisteredDecoders; use nihav_core::demuxers::RegisteredDemuxers; use nihav_codec_support::test::dec_video::*; - use crate::game_register_all_codecs; + use crate::game_register_all_decoders; use crate::game_register_all_demuxers; #[test] fn test_vmd_video() { 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); + game_register_all_decoders(&mut dec_reg); test_decoding("vmd", "vmd-video", "assets/Game/2832.VMD", Some(10), &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![ @@ -581,11 +714,37 @@ mod test { [0x72cce7e3, 0x98506d04, 0xd4d8bbaf, 0x3cc5e32d]])); } #[test] + fn test_vmd_video_16bpp() { + let mut dmx_reg = RegisteredDemuxers::new(); + game_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + game_register_all_decoders(&mut dec_reg); + + test_decoding("vmd", "vmd-video", "assets/Game/HLP1000.VMD", Some(10), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5Frames(vec![ + [0x03d77d51, 0x8670ae24, 0x86184cc8, 0x9c928700], + [0xf4796f1b, 0x0f75a120, 0x62056509, 0xc83f1a2c], + [0xd9e6db4d, 0x7af82082, 0xac6a335c, 0x19b8438f], + [0x03d77d51, 0x8670ae24, 0x86184cc8, 0x9c928700], + [0xd9e6db4d, 0x7af82082, 0xac6a335c, 0x19b8438f], + [0xf4796f1b, 0x0f75a120, 0x62056509, 0xc83f1a2c]])); + } + #[test] + fn test_vmd_video_24bpp() { + let mut dmx_reg = RegisteredDemuxers::new(); + game_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + game_register_all_decoders(&mut dec_reg); + + test_decoding("vmd", "vmd-video", "assets/Game/02C.VMD", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0xb580782c, 0xd7fb98c0, 0xaf9b83cc, 0xaea0846b])); + } + #[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); + game_register_all_decoders(&mut dec_reg); test_decoding("vmd", "vmd-audio", "assets/Game/1491.VMD", None, &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x75037601, 0xbc7b3976, 0x6e1c948b, 0xf05a3d6c])); @@ -595,7 +754,7 @@ mod test { 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); + game_register_all_decoders(&mut dec_reg); test_decoding("vmd", "vmd-audio", "assets/Game/2832.VMD", None, &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0x32dcdf0e, 0xee058684, 0x43ed5bf1, 0x2ff18b5a])); @@ -605,9 +764,19 @@ mod test { 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); + game_register_all_decoders(&mut dec_reg); test_decoding("vmd", "vmd-audio", "assets/Game/1000.VMD", None, &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0xc36215d3, 0x96530a80, 0x89f1fa8e, 0x49da302b])); } + #[test] + fn test_vmd_audio_ima_adpcm() { + let mut dmx_reg = RegisteredDemuxers::new(); + game_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + game_register_all_decoders(&mut dec_reg); + + test_decoding("vmd", "vmd-audio", "assets/Game/HLP1000.VMD", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x76a00405, 0xe4e5378d, 0x495b2a68, 0x4dffe042])); + } }