X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-game%2Fsrc%2Fcodecs%2Fgremlinvideo.rs;h=ad637968abda653b9aa6771ef5534e9e2c539b32;hp=19021ee3db9c1e70f47d1dcd4b68469da7f38816;hb=HEAD;hpb=6011e20199143f519881660144a4ca95ba77fd2d diff --git a/nihav-game/src/codecs/gremlinvideo.rs b/nihav-game/src/codecs/gremlinvideo.rs index 19021ee..ad63796 100644 --- a/nihav-game/src/codecs/gremlinvideo.rs +++ b/nihav-game/src/codecs/gremlinvideo.rs @@ -8,8 +8,10 @@ struct GremlinVideoDecoder { info: NACodecInfoRef, pal: [u8; 768], frame: Vec, + frame16: Vec, scale_v: bool, scale_h: bool, + is_16bit: bool, } struct Bits8 { @@ -50,7 +52,7 @@ impl Bits32 { self.queue >>= nbits; self.fill -= nbits; if self.fill <= 16 { - self.queue |= (br.read_u16le()? as u32) << self.fill; + self.queue |= u32::from(br.read_u16le()?) << self.fill; self.fill += 16; } Ok(res) @@ -60,8 +62,9 @@ impl Bits32 { impl GremlinVideoDecoder { fn new() -> Self { GremlinVideoDecoder { - info: NACodecInfoRef::default(), pal: [0; 768], frame: Vec::new(), - scale_v: false, scale_h: false + info: NACodecInfoRef::default(), pal: [0; 768], + frame: Vec::new(), frame16: Vec::new(), + scale_v: false, scale_h: false, is_16bit: false, } } @@ -81,6 +84,22 @@ impl GremlinVideoDecoder { Ok(()) } + fn lz_copy16(&mut self, idx: usize, offset: isize, len: usize) -> DecoderResult<()> { + if idx + len > self.frame16.len() { return Err(DecoderError::InvalidData); } + if offset == -1 { + let c = self.frame16[idx - 1]; + for i in 0..len { self.frame16[idx + i] = c; } + } else if offset < 0 { + let start = idx - (-offset as usize); + for i in 0..len { self.frame16[idx + i] = self.frame16[start + i]; } + } else { + if idx + (offset as usize) + len > self.frame16.len() { return Err(DecoderError::InvalidData); } + let start = idx + (offset as usize); + for i in 0..len { self.frame16[idx + i] = self.frame16[start + i]; } + } + Ok(()) + } + fn rescale(&mut self, w: usize, h: usize, scale_v: bool, scale_h: bool) { if (self.scale_v == scale_v) && (self.scale_h == scale_h) { return; } @@ -133,6 +152,58 @@ impl GremlinVideoDecoder { self.scale_h = scale_h; } + fn rescale16(&mut self, w: usize, h: usize, scale_v: bool, scale_h: bool) { + if (self.scale_v == scale_v) && (self.scale_h == scale_h) { return; } + + if self.scale_h && self.scale_v { + for j in 0..h { + let y = h - j - 1; + for i in 0..w { + let x = w - i - 1; + self.frame16[PREAMBLE_SIZE + x + y * w] = self.frame16[PREAMBLE_SIZE + x/2 + (y/2) * (w/2)]; + } + } + } else if self.scale_h { + for j in 0..h { + let y = h - j - 1; + for x in 0..w { + self.frame16[PREAMBLE_SIZE + x + y * w] = self.frame16[PREAMBLE_SIZE + x + (y/2) * w]; + } + } + } else if self.scale_v { + for j in 0..h { + let y = h - j - 1; + for i in 0..w { + let x = w - i - 1; + self.frame16[PREAMBLE_SIZE + x + y * w] = self.frame16[PREAMBLE_SIZE + x/2 + y * (w/2)]; + } + } + } + + if scale_h && scale_v { + for y in 0..h/2 { + for x in 0..w/2 { + self.frame16[PREAMBLE_SIZE + x + y * (w/2)] = self.frame16[PREAMBLE_SIZE + x*2 + y*2 * w]; + } + } + } else if scale_h { + for y in 0..h/2 { + for x in 0..w { + self.frame16[PREAMBLE_SIZE + x + y * w] = self.frame16[PREAMBLE_SIZE + x + y*2 * w]; + } + } + } else if scale_v { + for y in 0..h { + for x in 0..w/2 { + self.frame16[PREAMBLE_SIZE + x + y * w] = self.frame16[PREAMBLE_SIZE + x*2 + y * w]; + } + } + } + + self.scale_v = scale_v; + self.scale_h = scale_h; + } + fn output_frame(&mut self, bufinfo: &mut NABufferType, w: usize, h: usize) { let bufo = bufinfo.get_vbuf(); let mut buf = bufo.unwrap(); @@ -143,17 +214,17 @@ impl GremlinVideoDecoder { let mut sidx = PREAMBLE_SIZE; let mut didx = 0; - for i in 0..768 { dst[paloff + i] = self.pal[i]; } + dst[paloff..][..768].copy_from_slice(&self.pal); if !self.scale_v && !self.scale_h { for _ in 0..h { - for x in 0..w { dst[didx + x] = self.frame[sidx + x]; } + dst[didx..][..w].copy_from_slice(&self.frame[sidx..][..w]); sidx += w; didx += stride; } } else { for y in 0..h { if !self.scale_v { - for x in 0..w { dst[didx + x] = self.frame[sidx + x]; } + dst[didx..][..w].copy_from_slice(&self.frame[sidx..][..w]); } else { for x in 0..w { dst[didx + x] = self.frame[sidx + x/2]; } } @@ -165,6 +236,41 @@ impl GremlinVideoDecoder { } } + fn output_frame16(&mut self, bufinfo: &mut NABufferType, w: usize, h: usize) { + let bufo = bufinfo.get_vbuf16(); + let mut buf = bufo.unwrap(); + let stride = buf.get_stride(0); + let data = buf.get_data_mut().unwrap(); + let dst = data.as_mut_slice(); + + if !self.scale_v && !self.scale_h { + for (dline, sline) in dst.chunks_mut(stride).zip(self.frame16[PREAMBLE_SIZE..].chunks(w)).take(h) { + dline[..w].copy_from_slice(sline); + } + } else if !self.scale_h { + for (dline, sline) in dst.chunks_mut(stride).zip(self.frame16[PREAMBLE_SIZE..].chunks(w / 2)).take(h) { + for (dst, &src) in dline.chunks_mut(2).zip(sline.iter()) { + dst[0] = src; + dst[1] = src; + } + } + } else if !self.scale_v { + for (dline, sline) in dst.chunks_mut(stride * 2).zip(self.frame16[PREAMBLE_SIZE..].chunks(w)).take(h) { + dline[..w].copy_from_slice(sline); + dline[stride..][..w].copy_from_slice(sline); + } + } else { + for (dline, sline) in dst.chunks_mut(stride).zip(self.frame16[PREAMBLE_SIZE..].chunks(w / 2)).take(h) { + for x in 0..w/2 { + dline[x * 2] = sline[x]; + dline[x * 2 + 1] = sline[x]; + dline[x * 2 + stride] = sline[x]; + dline[x * 2 + 1 + stride] = sline[x]; + } + } + } + } + fn decode_method2(&mut self, br: &mut ByteReader) -> DecoderResult<()> { let mut bits = Bits8::new(); @@ -203,6 +309,48 @@ impl GremlinVideoDecoder { Ok(()) } + fn decode_method2_16bit(&mut self, br: &mut ByteReader) -> DecoderResult<()> { + let mut bits = Bits8::new(); + + if self.frame16[8] != 64 { + for (i, dst) in self.frame16[..PREAMBLE_SIZE].chunks_mut(4).enumerate() { + let val = (i as u16) << 5; + for el in dst.iter_mut() { + *el = val; + } + } + } + + let mut size = self.info.get_properties().get_video_info().unwrap().get_width() * + self.info.get_properties().get_video_info().unwrap().get_height(); + let mut idx = PREAMBLE_SIZE; + while size > 0 { + let tag = bits.read_2bits(br)?; + if tag == 0 { + self.frame16[idx] = br.read_u16le()?; + size -= 1; + idx += 1; + } else if tag == 1 { + let b = br.read_byte()?; + let len = ((b & 0xF) as usize) + 3; + let bot = (b >> 4) as isize; + let off = ((br.read_byte()? as isize) << 4) + bot - 4096; + validate!(len <= size); + size -= len; + self.lz_copy16(idx, off, len)?; + idx += len; + } else if tag == 2 { + let len = (br.read_byte()? as usize) + 2; + validate!(len <= size); + size -= len; + idx += len; + } else { + break; + } + } + Ok(()) + } + fn decode_method5(&mut self, br: &mut ByteReader, skip: usize) -> DecoderResult<()> { let mut bits = Bits8::new(); @@ -247,6 +395,7 @@ impl GremlinVideoDecoder { Ok(()) } + #[allow(clippy::identity_op)] fn decode_method68(&mut self, br: &mut ByteReader, skip: usize, use8: bool) -> DecoderResult<()> { let mut bits = Bits32::new(); @@ -304,7 +453,7 @@ impl GremlinVideoDecoder { let top = (bits.read_bits(br, 4)? as usize) << 8; let offs = top + (br.read_byte()? as usize); if (subtag != 0) || (offs <= 0xF80) { - let len = (subtag as usize) + 3; + let len = subtag + 3; self.lz_copy(idx, (offs as isize) - 4096, len)?; idx += len; } else { @@ -370,31 +519,43 @@ impl GremlinVideoDecoder { } } +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 GremlinVideoDecoder { fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { let w = vinfo.get_width(); let h = vinfo.get_height(); - if !vinfo.get_format().is_paletted() { return Err(DecoderError::NotImplemented); } - let fmt = PAL8_FORMAT; + self.is_16bit = !vinfo.get_format().is_paletted(); + let fmt = if !self.is_16bit { PAL8_FORMAT } else { RGB555_FORMAT }; let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, fmt)); self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); - self.frame.resize(PREAMBLE_SIZE + w * h, 0); - for i in 0..2 { - for j in 0..256 { - for k in 0..8 { - self.frame[i * 2048 + j * 8 + k] = j as u8; + if !self.is_16bit { + self.frame.resize(PREAMBLE_SIZE + w * h, 0); + for i in 0..2 { + for j in 0..256 { + for k in 0..8 { + self.frame[i * 2048 + j * 8 + k] = j as u8; + } } } - } - let edata = info.get_extradata().unwrap(); - validate!(edata.len() == 768); - for c in 0..256 { - for i in 0..3 { - let cc = edata[c * 3 + i]; - self.pal[c * 3 + i] = (cc << 2) | (cc >> 4); + let edata = info.get_extradata().unwrap(); + validate!(edata.len() == 768); + for c in 0..256 { + for i in 0..3 { + let cc = edata[c * 3 + i]; + self.pal[c * 3 + i] = (cc << 2) | (cc >> 4); + } } + } else { + self.frame16.resize(PREAMBLE_SIZE + w * h, 0); } Ok(()) } else { @@ -414,9 +575,17 @@ impl NADecoder for GremlinVideoDecoder { let scale_v = (flags & 0x10) != 0; let scale_h = (flags & 0x20) != 0; - self.rescale(w, h, scale_v, scale_h); + if !self.is_16bit { + self.rescale(w, h, scale_v, scale_h); + } else { + self.rescale16(w, h, scale_v, scale_h); + } + if self.is_16bit && (cmethod != 2) && (cmethod != 3) { + return Err(DecoderError::NotImplemented); + } if (cmethod == 0) || (cmethod == 1) { + validate!(!self.is_16bit); for c in 0..256 { for i in 0..3 { let b = br.read_byte()?; @@ -436,7 +605,11 @@ impl NADecoder for GremlinVideoDecoder { frm.set_frame_type(FrameType::Skip); return Ok(frm.into_ref()) } else if cmethod == 2 { - self.decode_method2(&mut br)?; + if !self.is_16bit { + self.decode_method2(&mut br)?; + } else { + self.decode_method2_16bit(&mut br)?; + } } else if cmethod == 5 { self.decode_method5(&mut br, (flags >> 8) as usize)?; } else if cmethod == 6 { @@ -447,20 +620,30 @@ impl NADecoder for GremlinVideoDecoder { return Err(DecoderError::NotImplemented); } - let bufret = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0); - if let Err(_) = bufret { return Err(DecoderError::InvalidData); } - let mut bufinfo = bufret.unwrap(); + let mut bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; - self.output_frame(&mut bufinfo, w, h); + if !self.is_16bit { + self.output_frame(&mut bufinfo, w, h); + } else { + self.output_frame16(&mut bufinfo, w, h); + } let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); 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) { + } +} + +impl NAOptionHandler for GremlinVideoDecoder { + 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 { +pub fn get_decoder_video() -> Box { Box::new(GremlinVideoDecoder::new()) } @@ -550,9 +733,17 @@ impl NADecoder for GremlinAudioDecoder { Err(DecoderError::InvalidData) } } + fn flush(&mut self) { + } } -pub fn get_decoder_audio() -> Box { +impl NAOptionHandler for GremlinAudioDecoder { + 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(GremlinAudioDecoder::new()) } @@ -560,16 +751,39 @@ pub fn get_decoder_audio() -> Box { mod test { use nihav_core::codecs::RegisteredDecoders; use nihav_core::demuxers::RegisteredDemuxers; - use nihav_core::test::dec_video::test_file_decoding; - 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_decoders; + use crate::game_register_all_demuxers; + + // samples: intro1.gdv from Normality, SHELI_S.GDV from Jungle Strike + #[test] + fn test_gdv_video() { + 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("gdv", "gdv-video", "assets/Game/intro1.gdv", Some(10), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x7ea302bf, 0xc3e210cf, 0x6e341376, 0x9e976056])); + } + #[test] + fn test_gdv_video16() { + 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("gdv", "gdv-video", "assets/Game/SHELI_S.GDV", Some(10), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x9acfdefd, 0x12687f8c, 0x9ef1cd56, 0x019240bb])); + } #[test] - fn test_gdv() { + fn test_gdv_audio() { 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_file_decoding("gdv", "assets/Game/intro1.gdv", Some(10), true, false, None, &dmx_reg, &dec_reg); + test_decoding("gdv", "gdv-audio", "assets/Game/intro1.gdv", None, &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0xd41d8cd9, 0x8f00b204, 0xe9800998, 0xecf8427e])); } }