1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
4 const FRAME_W: usize = 320;
5 const FRAME_H: usize = 240;
6 const HIST_SIZE: usize = 32768;
13 struct HighlanderDecoder {
15 hist: [u8; HIST_SIZE],
16 tmp1: [u8; FRAME_W * FRAME_H],
17 tmp2: [u8; FRAME_W * FRAME_H * 17 / 16],
20 fn unpack(src: &[u8], dst: &mut [u8], hist: &mut [u8; HIST_SIZE]) -> DecoderResult<usize> {
21 let mut mr = MemoryReader::new_read(src);
22 let mut br = ByteReader::new(&mut mr);
24 let mut mw = MemoryWriter::new_write(dst);
25 let mut bw = ByteWriter::new(&mut mw);
27 *hist = [0; HIST_SIZE];
32 let mut flags = br.read_byte()?;
34 let idx = (usize::from(pprev) << 7) ^ usize::from(prev);
39 hist[idx] = br.read_byte()?;
50 Ok(bw.tell() as usize)
53 fn paint_frame(dst: &mut [u8], stride: usize, src: &[u8]) -> DecoderResult<()> {
54 let mut mr = MemoryReader::new_read(src);
55 let mut br = ByteReader::new(&mut mr);
57 let mut blk_offs = [0; 16];
58 for (y, offs) in blk_offs.chunks_mut(4).enumerate() {
60 offs[1] = stride * y + 1;
61 offs[2] = stride * y + 2;
62 offs[3] = stride * y + 3;
65 for row in dst.chunks_mut(stride * 4).take(FRAME_H / 4) {
66 for xoff in (0..FRAME_W).step_by(4) {
67 let idx = br.read_byte()? as usize;
68 validate!(idx < PAINT_MODE.len());
69 let mode = &PAINT_MODE[idx];
70 validate!(i64::from(mode.len) <= br.left());
72 for (&blk_off, &idx) in blk_offs.iter().zip(mode.pattern.iter()) {
74 row[xoff + blk_off] = br.read_byte()?;
77 for (&blk_off, &idx) in blk_offs.iter().zip(mode.pattern.iter()) {
79 row[xoff + blk_off] = row[xoff + blk_offs[idx as usize]];
87 impl HighlanderDecoder {
90 info: NACodecInfoRef::default(),
92 tmp1: [0; FRAME_W * FRAME_H],
93 tmp2: [0; FRAME_W * FRAME_H * 17 / 16],
98 impl NADecoder for HighlanderDecoder {
99 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
100 if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() {
101 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(FRAME_W, FRAME_H, false, PAL8_FORMAT));
102 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
106 Err(DecoderError::InvalidData)
109 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
110 let src = pkt.get_buffer();
111 validate!(src.len() > 4);
113 let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?;
114 let bufo = bufinfo.get_vbuf();
115 let mut buf = bufo.unwrap();
116 let paloff = buf.get_offset(1);
117 let stride = buf.get_stride(0);
118 let data = buf.get_data_mut().unwrap();
119 let dst = data.as_mut_slice();
121 validate!(src.len() > 4);
123 let size = read_u32le(&src)? as usize;
124 validate!(size <= src.len() - 4);
126 let size2 = unpack(&src[4..][..size], &mut self.tmp1, &mut self.hist)?;
127 let size3 = unpack(&self.tmp1[..size2], &mut self.tmp2, &mut self.hist)?;
128 paint_frame(dst, stride, &self.tmp2[..size3])?;
130 let dpal = &mut dst[paloff..][..768];
131 for (dst, &src) in dpal.iter_mut().zip(DEFAULT_PAL.iter()) {
132 *dst = (src << 2) | (src >> 4);
135 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
136 frm.set_keyframe(true);
137 frm.set_frame_type(FrameType::I);
140 fn flush(&mut self) {
144 impl NAOptionHandler for HighlanderDecoder {
145 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
146 fn set_options(&mut self, _options: &[NAOption]) { }
147 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
151 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
152 Box::new(HighlanderDecoder::new())
157 use nihav_core::codecs::RegisteredDecoders;
158 use nihav_core::demuxers::RegisteredDemuxers;
159 use nihav_codec_support::test::dec_video::*;
160 use crate::game_register_all_decoders;
161 use crate::game_register_all_demuxers;
162 // sample extracted from Highlander: The Last of the MacLeods unpublished game
164 fn test_hl_fmv_video() {
165 let mut dmx_reg = RegisteredDemuxers::new();
166 game_register_all_demuxers(&mut dmx_reg);
167 let mut dec_reg = RegisteredDecoders::new();
168 game_register_all_decoders(&mut dec_reg);
170 test_decoding("hl-fmv", "hl-fmv-video", "assets/Game/0260.fmv", Some(10), &dmx_reg, &dec_reg,
171 ExpectedTestResult::MD5([0x369659f0, 0x417ad3a7, 0xc62dfc6f, 0x6e5fe871]));
175 const PAINT_MODE: [Pattern; 9] = [
179 0xFF, 0x00, 0x00, 0x00,
180 0x00, 0x00, 0x00, 0x00,
181 0x00, 0x00, 0x00, 0x00,
182 0x00, 0x00, 0x00, 0x00
188 0x05, 0x09, 0x05, 0x09,
189 0x09, 0xFF, 0x09, 0x05,
190 0x05, 0xFF, 0x05, 0x09,
191 0x09, 0x05, 0x09, 0x05
197 0xFF, 0xFF, 0x00, 0x01,
198 0x01, 0x01, 0x01, 0x01,
199 0x00, 0x01, 0x00, 0x01,
200 0x01, 0x01, 0x01, 0x01
206 0xFF, 0xFF, 0x00, 0x01,
207 0x00, 0x00, 0x00, 0x00,
208 0x00, 0x01, 0x00, 0x01,
209 0x00, 0x00, 0x00, 0x00
215 0x0E, 0x0E, 0x0E, 0x0E,
216 0x0E, 0x0F, 0x0E, 0x0F,
217 0x0E, 0x0E, 0x0E, 0x0E,
218 0x0E, 0x0F, 0xFF, 0xFF
224 0x0F, 0x0F, 0x0F, 0x0F,
225 0x0E, 0x0F, 0x0E, 0x0F,
226 0x0F, 0x0F, 0x0F, 0x0F,
227 0x0E, 0x0F, 0xFF, 0xFF
233 0xFF, 0xFF, 0x00, 0xFF,
234 0x01, 0xFF, 0x01, 0xFF,
235 0x00, 0x01, 0x00, 0x03,
236 0x03, 0x07, 0x03, 0x07
242 0xFF, 0xFF, 0xFF, 0xFF,
243 0x01, 0x00, 0x03, 0x02,
244 0xFF, 0xFF, 0xFF, 0xFF,
245 0x09, 0x08, 0x0B, 0x0A
251 0xFF, 0xFF, 0xFF, 0xFF,
252 0xFF, 0xFF, 0xFF, 0xFF,
253 0xFF, 0xFF, 0xFF, 0xFF,
254 0xFF, 0xFF, 0xFF, 0xFF
259 const DEFAULT_PAL: [u8; 768] = [