]>
Commit | Line | Data |
---|---|---|
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 | } |