| 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), ( 2, -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([0x9b1fc970, 0x1fe86e2c, 0x44dd9255, 0x3920c49b])); |
| 181 | } |
| 182 | } |