add Arxel Tribe video support
[nihav.git] / nihav-game / src / codecs / arxel_vid.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3 use nihav_core::io::bitreader::*;
4
5 const HEADER_SIZE: usize = 0x2F;
6
7 const MV_2BIT: [(i8, i8); 4] = [(-1, 0), (-1, -1), (1, -1), (0, -2)];
8 const MV_4BIT: [(i8, i8); 16] = [
9 (-2, -3), ( 2, -3), (-1, -4), ( 1, -4),
10 (-1, -2), ( 1, -2), ( 0, -3), ( 0, -4),
11 (-2, 0), (-2, -1), ( 1, -1), (-2, -2),
12 ( 2, -2), (-1, -3), ( 1, -3), ( 0, -5)
13 ];
14
15 const BPP: usize = 4;
16
17 struct ArxelVideoDecoder {
18 info: NACodecInfoRef,
19 tiles: [u8; 65536],
20 }
21
22 impl ArxelVideoDecoder {
23 fn new() -> Self {
24 Self {
25 info: NACodecInfoRef::default(),
26 tiles: [0; 65536],
27 }
28 }
29 }
30
31 const RGBA_FORMAT: NAPixelFormaton = NAPixelFormaton {
32 model: ColorModel::RGB(RGBSubmodel::RGB), components: 4,
33 comp_info: [
34 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }),
35 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }),
36 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }),
37 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 3, next_elem: 4 }),
38 None ],
39 elem_size: 4, be: false, alpha: true, palette: false };
40
41 impl NADecoder for ArxelVideoDecoder {
42 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
43 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
44 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), true, RGBA_FORMAT));
45 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
46
47 Ok(())
48 } else {
49 Err(DecoderError::InvalidData)
50 }
51 }
52 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
53 let src = pkt.get_buffer();
54 validate!(src.len() > HEADER_SIZE);
55
56 let mut mr = MemoryReader::new_read(&src);
57 let mut br = ByteReader::new(&mut mr);
58
59 let size = br.read_u32le()? as usize;
60 validate!(src.len() >= size + HEADER_SIZE);
61 let part2_off = br.read_u32le()? as u64;
62 validate!(part2_off > 0 && part2_off < (size as u64));
63 let num_tiles = br.read_u16le()? as usize;
64 validate!(num_tiles > 0 && num_tiles < 4096);
65 let tile_size = br.read_u16le()? as usize;
66 let width = br.read_u32le()? as usize;
67 let height = br.read_u32le()? as usize;
68
69 let vinfo = self.info.get_properties().get_video_info().unwrap();
70 validate!(width == vinfo.get_width());
71 validate!(height == vinfo.get_height());
72
73 br.seek(SeekFrom::Start(part2_off + (HEADER_SIZE as u64)))?;
74 match tile_size {
75 2 => {
76 return Err(DecoderError::NotImplemented);
77 },
78 4 => {
79 br.read_buf(&mut self.tiles[..16])?;
80 let off = br.tell() as usize;
81 let mut bir = BitReader::new(&src[off..], BitReaderMode::BE);
82 for tile in 1..num_tiles {
83 for i in 0..16 {
84 self.tiles[tile * 16 + i] = self.tiles[tile * 16 + i - 16];
85 }
86 let bits = bir.read(3)? as u8 + 1;
87 validate!(bits < 8);
88 for el in self.tiles[tile * 16..][..16].iter_mut() {
89 let mut delta = bir.read(bits)? as i16;
90 if delta != 0 && bir.read_bool()? {
91 delta = -delta;
92 }
93 *el = (i16::from(*el) + delta) as u8;
94 }
95 }
96 },
97 _ => {
98 validate!(tile_size > 0);
99 br.read_buf(&mut self.tiles[..tile_size])?;
100 },
101 };
102
103 let bufinfo = alloc_video_buffer(vinfo, 0)?;
104 let bufo = bufinfo.get_vbuf();
105 let mut buf = bufo.unwrap();
106 let stride = buf.get_stride(0);
107 let data = buf.get_data_mut().unwrap();
108 let dst = data.as_mut_slice();
109
110 let mut br = BitReader::new(&src[HEADER_SIZE..], BitReaderMode::BE);
111 let tile_w = 4;
112 let tsize = tile_w * BPP;
113 let idx_bits = if num_tiles < 0x400 { 10 } else if num_tiles < 0x800 { 11 } else { 12 };
114 for y in (0..height).step_by(2) {
115 for x in (0..width).step_by(tile_w) {
116 let dst_pos = x * BPP + y * stride;
117 if !br.read_bool()? {
118 let idx = br.read(idx_bits)? as usize;
119 validate!(idx < num_tiles);
120 dst[dst_pos..][..tsize].copy_from_slice(&self.tiles[idx * tsize..][..tsize]);
121 } else {
122 let (mv_x, mv_y) = if br.read_bool()? {
123 (0, -1)
124 } else if br.read_bool()? {
125 MV_2BIT[br.read(2)? as usize]
126 } else {
127 MV_4BIT[br.read(4)? as usize]
128 };
129
130 let isrc = (dst_pos as isize) + isize::from(mv_x) * (tsize as isize) + isize::from(mv_y) * ((stride * 2) as isize);
131 validate!(isrc >= 0);
132 let src_pos = isrc as usize;
133 validate!(src_pos + tsize <= dst.len());
134 let (src, dst) = dst.split_at_mut(dst_pos);
135 dst[..tsize].copy_from_slice(&src[src_pos..][..tsize]);
136 }
137 }
138 // double lines
139 let lines = &mut dst[y * stride..];
140 let (src, dst) = lines.split_at_mut(stride);
141 dst[..stride].copy_from_slice(src);
142 }
143
144 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
145 frm.set_keyframe(true);
146 frm.set_frame_type(FrameType::I);
147 Ok(frm.into_ref())
148 }
149 fn flush(&mut self) {
150 }
151 }
152
153 impl NAOptionHandler for ArxelVideoDecoder {
154 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
155 fn set_options(&mut self, _options: &[NAOption]) { }
156 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
157 }
158
159
160 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
161 Box::new(ArxelVideoDecoder::new())
162 }
163
164 #[cfg(test)]
165 mod test {
166 use nihav_core::codecs::RegisteredDecoders;
167 use nihav_core::demuxers::RegisteredDemuxers;
168 use nihav_codec_support::test::dec_video::*;
169 use crate::game_register_all_decoders;
170 use crate::game_register_all_demuxers;
171 // sample from the Ring game
172 #[test]
173 fn test_arxel_video() {
174 let mut dmx_reg = RegisteredDemuxers::new();
175 game_register_all_demuxers(&mut dmx_reg);
176 let mut dec_reg = RegisteredDecoders::new();
177 game_register_all_decoders(&mut dec_reg);
178
179 test_decoding("arxel-cnm", "arxel-video", "assets/Game/logo.cnm", Some(10), &dmx_reg, &dec_reg,
180 ExpectedTestResult::MD5([0xcf12f83a, 0xfdce1ed2, 0x2d183394, 0xa265f164]));
181 }
182 }