X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-game%2Fsrc%2Fcodecs%2Fhl_fmv.rs;fp=nihav-game%2Fsrc%2Fcodecs%2Fhl_fmv.rs;h=426d1444d89420fd5c7862b2f07bd75a70082c61;hp=0000000000000000000000000000000000000000;hb=ff6a914f5b07a9c62d7624fb6dd9f1fc093e82ca;hpb=bf30ce1bcf061cf59d0dbd149997892ad4fdf7ed diff --git a/nihav-game/src/codecs/hl_fmv.rs b/nihav-game/src/codecs/hl_fmv.rs new file mode 100644 index 0000000..426d144 --- /dev/null +++ b/nihav-game/src/codecs/hl_fmv.rs @@ -0,0 +1,516 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; + +const FRAME_W: usize = 320; +const FRAME_H: usize = 240; +const HIST_SIZE: usize = 32768; + +struct Pattern { + len: u8, + pattern: [u8; 16], +} + +struct HighlanderDecoder { + info: NACodecInfoRef, + hist: [u8; HIST_SIZE], + tmp1: [u8; FRAME_W * FRAME_H], + tmp2: [u8; FRAME_W * FRAME_H * 17 / 16], +} + +fn unpack(src: &[u8], dst: &mut [u8], hist: &mut [u8; HIST_SIZE]) -> DecoderResult { + let mut mr = MemoryReader::new_read(src); + let mut br = ByteReader::new(&mut mr); + + let mut mw = MemoryWriter::new_write(dst); + let mut bw = ByteWriter::new(&mut mw); + + *hist = [0; HIST_SIZE]; + + let mut pprev = 0; + let mut prev = 0; + while br.left() > 0 { + let mut flags = br.read_byte()?; + for _ in 0..8 { + let idx = (usize::from(pprev) << 7) ^ usize::from(prev); + if (flags & 1) == 0 { + if br.left() == 0 { + break; + } + hist[idx] = br.read_byte()?; + } + let val = hist[idx]; + bw.write_byte(val)?; + + flags >>= 1; + pprev = prev; + prev = val; + } + } + + Ok(bw.tell() as usize) +} + +fn paint_frame(dst: &mut [u8], stride: usize, src: &[u8]) -> DecoderResult<()> { + let mut mr = MemoryReader::new_read(src); + let mut br = ByteReader::new(&mut mr); + + let mut blk_offs = [0; 16]; + for (y, offs) in blk_offs.chunks_mut(4).enumerate() { + offs[0] = stride * y; + offs[1] = stride * y + 1; + offs[2] = stride * y + 2; + offs[3] = stride * y + 3; + } + + for row in dst.chunks_mut(stride * 4).take(FRAME_H / 4) { + for xoff in (0..FRAME_W).step_by(4) { + let idx = br.read_byte()? as usize; + validate!(idx < PAINT_MODE.len()); + let mode = &PAINT_MODE[idx]; + validate!(i64::from(mode.len) <= br.left()); + + for (&blk_off, &idx) in blk_offs.iter().zip(mode.pattern.iter()) { + if idx == 0xFF { + row[xoff + blk_off] = br.read_byte()?; + } + } + for (&blk_off, &idx) in blk_offs.iter().zip(mode.pattern.iter()) { + if idx != 0xFF { + row[xoff + blk_off] = row[xoff + blk_offs[idx as usize]]; + } + } + } + } + Ok(()) +} + +impl HighlanderDecoder { + fn new() -> Self { + Self { + info: NACodecInfoRef::default(), + hist: [0; HIST_SIZE], + tmp1: [0; FRAME_W * FRAME_H], + tmp2: [0; FRAME_W * FRAME_H * 17 / 16], + } + } +} + +impl NADecoder for HighlanderDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() { + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(FRAME_W, FRAME_H, false, PAL8_FORMAT)); + self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); + + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { + let src = pkt.get_buffer(); + validate!(src.len() > 4); + + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; + let bufo = bufinfo.get_vbuf(); + let mut buf = bufo.unwrap(); + 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(); + + validate!(src.len() > 4); + + let size = read_u32le(&src)? as usize; + validate!(size <= src.len() - 4); + + let size2 = unpack(&src[4..][..size], &mut self.tmp1, &mut self.hist)?; + let size3 = unpack(&self.tmp1[..size2], &mut self.tmp2, &mut self.hist)?; + paint_frame(dst, stride, &self.tmp2[..size3])?; + + let dpal = &mut dst[paloff..][..768]; + for (dst, &src) in dpal.iter_mut().zip(DEFAULT_PAL.iter()) { + *dst = (src << 2) | (src >> 4); + } + + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); + frm.set_keyframe(true); + frm.set_frame_type(FrameType::I); + Ok(frm.into_ref()) + } + fn flush(&mut self) { + } +} + +impl NAOptionHandler for HighlanderDecoder { + 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() -> Box { + Box::new(HighlanderDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_codec_support::test::dec_video::*; + use crate::game_register_all_decoders; + use crate::game_register_all_demuxers; + // sample extracted from Highlander: The Last of the MacLeods unpublished game + #[test] + fn test_hl_fmv_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("hl-fmv", "hl-fmv-video", "assets/Game/0260.fmv", Some(10), &dmx_reg, &dec_reg, + ExpectedTestResult::MD5([0x369659f0, 0x417ad3a7, 0xc62dfc6f, 0x6e5fe871])); + } +} + +const PAINT_MODE: [Pattern; 9] = [ + Pattern { + len: 1, + pattern: [ + 0xFF, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + ], + }, + Pattern { + len: 2, + pattern: [ + 0x05, 0x09, 0x05, 0x09, + 0x09, 0xFF, 0x09, 0x05, + 0x05, 0xFF, 0x05, 0x09, + 0x09, 0x05, 0x09, 0x05 + ], + }, + Pattern { + len: 2, + pattern: [ + 0xFF, 0xFF, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, + 0x00, 0x01, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01 + ] + }, + Pattern { + len: 2, + pattern: [ + 0xFF, 0xFF, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00 + ], + }, + Pattern { + len: 2, + pattern: [ + 0x0E, 0x0E, 0x0E, 0x0E, + 0x0E, 0x0F, 0x0E, 0x0F, + 0x0E, 0x0E, 0x0E, 0x0E, + 0x0E, 0x0F, 0xFF, 0xFF + ], + }, + Pattern { + len: 2, + pattern: [ + 0x0F, 0x0F, 0x0F, 0x0F, + 0x0E, 0x0F, 0x0E, 0x0F, + 0x0F, 0x0F, 0x0F, 0x0F, + 0x0E, 0x0F, 0xFF, 0xFF + ], + }, + Pattern { + len: 5, + pattern: [ + 0xFF, 0xFF, 0x00, 0xFF, + 0x01, 0xFF, 0x01, 0xFF, + 0x00, 0x01, 0x00, 0x03, + 0x03, 0x07, 0x03, 0x07 + ], + }, + Pattern { + len: 8, + pattern: [ + 0xFF, 0xFF, 0xFF, 0xFF, + 0x01, 0x00, 0x03, 0x02, + 0xFF, 0xFF, 0xFF, 0xFF, + 0x09, 0x08, 0x0B, 0x0A + ], + }, + Pattern { + len: 16, + pattern: [ + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF + ], + } +]; + +const DEFAULT_PAL: [u8; 768] = [ + 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, + 0x00, 0x20, 0x00, + 0x00, 0x20, 0x20, + 0x20, 0x00, 0x00, + 0x20, 0x00, 0x20, + 0x20, 0x20, 0x00, + 0x30, 0x30, 0x30, + 0x30, 0x37, 0x30, + 0x3C, 0x32, 0x29, + 0x01, 0x01, 0x01, + 0x02, 0x02, 0x02, + 0x03, 0x03, 0x03, + 0x04, 0x04, 0x04, + 0x05, 0x05, 0x05, + 0x07, 0x07, 0x07, + 0x08, 0x08, 0x08, + 0x0A, 0x0A, 0x0A, + 0x15, 0x15, 0x15, + 0x13, 0x13, 0x13, + 0x10, 0x10, 0x10, + 0x0E, 0x0E, 0x0E, + 0x20, 0x20, 0x20, + 0x00, 0x00, 0x20, + 0x00, 0x20, 0x00, + 0x00, 0x20, 0x20, + 0x20, 0x00, 0x00, + 0x20, 0x00, 0x20, + 0x20, 0x20, 0x00, + 0x00, 0x00, 0x0C, + 0x00, 0x00, 0x19, + 0x00, 0x00, 0x26, + 0x00, 0x00, 0x33, + 0x00, 0x0C, 0x00, + 0x00, 0x0C, 0x0C, + 0x00, 0x0C, 0x19, + 0x00, 0x0C, 0x26, + 0x00, 0x0C, 0x33, + 0x00, 0x0C, 0x3F, + 0x00, 0x19, 0x00, + 0x00, 0x19, 0x0C, + 0x00, 0x19, 0x19, + 0x00, 0x19, 0x26, + 0x00, 0x19, 0x33, + 0x00, 0x19, 0x3F, + 0x00, 0x26, 0x00, + 0x00, 0x26, 0x0C, + 0x00, 0x26, 0x19, + 0x00, 0x26, 0x26, + 0x00, 0x26, 0x33, + 0x00, 0x26, 0x3F, + 0x00, 0x33, 0x00, + 0x00, 0x33, 0x0C, + 0x00, 0x33, 0x19, + 0x00, 0x33, 0x26, + 0x00, 0x33, 0x33, + 0x00, 0x33, 0x3F, + 0x00, 0x3F, 0x19, + 0x00, 0x3F, 0x26, + 0x00, 0x3F, 0x33, + 0x0C, 0x00, 0x00, + 0x0C, 0x00, 0x0C, + 0x0C, 0x00, 0x19, + 0x0C, 0x00, 0x26, + 0x0C, 0x00, 0x33, + 0x0C, 0x00, 0x3F, + 0x0C, 0x0C, 0x00, + 0x0C, 0x0C, 0x0C, + 0x0C, 0x0C, 0x19, + 0x0C, 0x0C, 0x26, + 0x0C, 0x0C, 0x33, + 0x0C, 0x0C, 0x3F, + 0x0C, 0x19, 0x00, + 0x0C, 0x19, 0x0C, + 0x0C, 0x19, 0x19, + 0x0C, 0x19, 0x26, + 0x0C, 0x19, 0x33, + 0x0C, 0x19, 0x3F, + 0x0C, 0x26, 0x00, + 0x0C, 0x26, 0x0C, + 0x0C, 0x26, 0x19, + 0x0C, 0x26, 0x26, + 0x0C, 0x26, 0x33, + 0x0C, 0x26, 0x3F, + 0x0C, 0x33, 0x00, + 0x0C, 0x33, 0x0C, + 0x0C, 0x33, 0x19, + 0x0C, 0x33, 0x26, + 0x0C, 0x33, 0x33, + 0x0C, 0x33, 0x3F, + 0x0C, 0x3F, 0x0C, + 0x0C, 0x3F, 0x19, + 0x0C, 0x3F, 0x26, + 0x0C, 0x3F, 0x33, + 0x0C, 0x3F, 0x3F, + 0x19, 0x00, 0x00, + 0x19, 0x00, 0x0C, + 0x19, 0x00, 0x19, + 0x19, 0x00, 0x26, + 0x19, 0x00, 0x33, + 0x19, 0x00, 0x3F, + 0x19, 0x0C, 0x00, + 0x19, 0x0C, 0x0C, + 0x19, 0x0C, 0x19, + 0x19, 0x0C, 0x26, + 0x19, 0x0C, 0x33, + 0x19, 0x0C, 0x3F, + 0x19, 0x19, 0x00, + 0x19, 0x19, 0x0C, + 0x19, 0x19, 0x19, + 0x19, 0x19, 0x26, + 0x19, 0x19, 0x33, + 0x19, 0x26, 0x00, + 0x19, 0x26, 0x0C, + 0x19, 0x26, 0x19, + 0x19, 0x26, 0x26, + 0x19, 0x26, 0x33, + 0x19, 0x26, 0x3F, + 0x19, 0x33, 0x00, + 0x19, 0x33, 0x0C, + 0x19, 0x33, 0x26, + 0x19, 0x33, 0x33, + 0x19, 0x33, 0x3F, + 0x19, 0x3F, 0x00, + 0x19, 0x3F, 0x0C, + 0x19, 0x3F, 0x26, + 0x19, 0x3F, 0x33, + 0x33, 0x00, 0x3F, + 0x3F, 0x00, 0x33, + 0x26, 0x26, 0x00, + 0x26, 0x0C, 0x26, + 0x26, 0x00, 0x26, + 0x26, 0x00, 0x33, + 0x26, 0x00, 0x00, + 0x26, 0x0C, 0x0C, + 0x26, 0x00, 0x19, + 0x26, 0x0C, 0x33, + 0x26, 0x00, 0x3F, + 0x26, 0x19, 0x00, + 0x26, 0x19, 0x0C, + 0x26, 0x0C, 0x19, + 0x26, 0x19, 0x26, + 0x26, 0x19, 0x33, + 0x26, 0x0C, 0x3F, + 0x26, 0x26, 0x0C, + 0x26, 0x26, 0x19, + 0x26, 0x26, 0x26, + 0x26, 0x26, 0x33, + 0x26, 0x26, 0x3F, + 0x26, 0x33, 0x00, + 0x26, 0x33, 0x0C, + 0x19, 0x33, 0x19, + 0x26, 0x33, 0x26, + 0x26, 0x33, 0x33, + 0x26, 0x33, 0x3F, + 0x26, 0x3F, 0x00, + 0x26, 0x3F, 0x0C, + 0x26, 0x33, 0x19, + 0x26, 0x3F, 0x26, + 0x26, 0x3F, 0x33, + 0x26, 0x3F, 0x3F, + 0x33, 0x00, 0x00, + 0x26, 0x00, 0x0C, + 0x33, 0x00, 0x19, + 0x33, 0x00, 0x26, + 0x33, 0x00, 0x33, + 0x26, 0x0C, 0x00, + 0x33, 0x0C, 0x0C, + 0x33, 0x0C, 0x19, + 0x33, 0x0C, 0x26, + 0x33, 0x0C, 0x33, + 0x33, 0x0C, 0x3F, + 0x33, 0x19, 0x00, + 0x33, 0x19, 0x0C, + 0x26, 0x19, 0x19, + 0x33, 0x19, 0x26, + 0x33, 0x19, 0x33, + 0x26, 0x19, 0x3F, + 0x33, 0x26, 0x00, + 0x33, 0x26, 0x0C, + 0x33, 0x26, 0x19, + 0x33, 0x26, 0x26, + 0x33, 0x26, 0x33, + 0x33, 0x26, 0x3F, + 0x33, 0x33, 0x00, + 0x33, 0x33, 0x0C, + 0x33, 0x33, 0x19, + 0x33, 0x33, 0x26, + 0x33, 0x33, 0x33, + 0x33, 0x33, 0x3F, + 0x33, 0x3F, 0x00, + 0x33, 0x3F, 0x0C, + 0x26, 0x3F, 0x19, + 0x33, 0x3F, 0x26, + 0x33, 0x3F, 0x33, + 0x33, 0x3F, 0x3F, + 0x33, 0x00, 0x0C, + 0x3F, 0x00, 0x19, + 0x3F, 0x00, 0x26, + 0x33, 0x0C, 0x00, + 0x3F, 0x0C, 0x0C, + 0x3F, 0x0C, 0x19, + 0x3F, 0x0C, 0x26, + 0x3F, 0x0C, 0x33, + 0x3F, 0x0C, 0x3F, + 0x3F, 0x19, 0x00, + 0x3F, 0x19, 0x0C, + 0x33, 0x19, 0x19, + 0x3F, 0x19, 0x26, + 0x3F, 0x19, 0x33, + 0x33, 0x19, 0x3F, + 0x3F, 0x26, 0x00, + 0x3F, 0x26, 0x0C, + 0x3F, 0x26, 0x19, + 0x3F, 0x26, 0x26, + 0x3F, 0x26, 0x33, + 0x3F, 0x26, 0x3F, + 0x3F, 0x33, 0x00, + 0x3F, 0x33, 0x0C, + 0x3F, 0x33, 0x19, + 0x3F, 0x33, 0x26, + 0x3F, 0x33, 0x33, + 0x3F, 0x33, 0x3F, + 0x3F, 0x3F, 0x0C, + 0x33, 0x3F, 0x19, + 0x3F, 0x3F, 0x26, + 0x3F, 0x3F, 0x33, + 0x19, 0x19, 0x3F, + 0x19, 0x3F, 0x19, + 0x19, 0x3F, 0x3F, + 0x3F, 0x19, 0x19, + 0x3F, 0x19, 0x3F, + 0x3F, 0x3F, 0x19, + 0x30, 0x30, 0x30, + 0x17, 0x17, 0x17, + 0x1D, 0x1D, 0x1D, + 0x21, 0x21, 0x21, + 0x25, 0x25, 0x25, + 0x32, 0x32, 0x32, + 0x2C, 0x2C, 0x2C, + 0x35, 0x35, 0x35, + 0x37, 0x37, 0x37, + 0x38, 0x38, 0x38, + 0x3A, 0x3A, 0x3A, + 0x3C, 0x3C, 0x3C, + 0x3E, 0x3E, 0x3E, + 0x3C, 0x3E, 0x3F, + 0x29, 0x28, 0x28, + 0x20, 0x20, 0x20, + 0x00, 0x00, 0x3F, + 0x00, 0x3F, 0x00, + 0x00, 0x3F, 0x3F, + 0x3F, 0x00, 0x00, + 0x3F, 0x00, 0x3F, + 0x3F, 0x3F, 0x00, + 0x3F, 0x3F, 0x3F +];