]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::codecs::*; | |
2 | use nihav_core::io::byteio::*; | |
3 | use nihav_core::io::bitreader::*; | |
4 | use nihav_codec_support::codecs::HAMShuffler; | |
5 | ||
6 | const HEADER_SIZE: usize = 0x2F; | |
7 | ||
8 | const MV_2BIT: [(i8, i8); 4] = [(-1, 0), (-1, -1), (1, -1), (0, -2)]; | |
9 | const MV_4BIT: [(i8, i8); 16] = [ | |
10 | (-2, -3), ( 2, -3), (-1, -4), ( 1, -4), | |
11 | (-1, -2), ( 1, -2), ( 0, -3), ( 0, -4), | |
12 | (-2, 0), (-2, -1), ( 2, -1), (-2, -2), | |
13 | ( 2, -2), (-1, -3), ( 1, -3), ( 0, -5) | |
14 | ]; | |
15 | ||
16 | const BPP: usize = 4; | |
17 | ||
18 | struct ArxelVideoDecoder { | |
19 | info: NACodecInfoRef, | |
20 | tiles: [u8; 65536], | |
21 | version: u8, | |
22 | idx_buf: Vec<usize>, | |
23 | contexts: [[usize; 16]; 4096], | |
24 | ctx_pos: [usize; 4096], | |
25 | hams: HAMShuffler<u8>, | |
26 | } | |
27 | ||
28 | impl ArxelVideoDecoder { | |
29 | fn new() -> Self { | |
30 | Self { | |
31 | info: NACodecInfoRef::default(), | |
32 | tiles: [0; 65536], | |
33 | version: 0, | |
34 | idx_buf: Vec::new(), | |
35 | contexts: [[0; 16]; 4096], | |
36 | ctx_pos: [0; 4096], | |
37 | hams: HAMShuffler::new(), | |
38 | } | |
39 | } | |
40 | fn decode_v1(&mut self, src: &[u8]) -> DecoderResult<(NABufferType, bool)> { | |
41 | let mut mr = MemoryReader::new_read(src); | |
42 | let mut br = ByteReader::new(&mut mr); | |
43 | ||
44 | let size = br.read_u32le()? as usize; | |
45 | validate!(src.len() >= size + HEADER_SIZE); | |
46 | let part2_off = br.read_u32le()? as u64; | |
47 | validate!(part2_off > 0 && part2_off < (size as u64)); | |
48 | let num_tiles = br.read_u16le()? as usize; | |
49 | validate!(num_tiles > 0 && num_tiles < 4096); | |
50 | let tile_size = br.read_u16le()? as usize; | |
51 | let width = br.read_u32le()? as usize; | |
52 | let height = br.read_u32le()? as usize; | |
53 | ||
54 | let vinfo = self.info.get_properties().get_video_info().unwrap(); | |
55 | validate!(width == vinfo.get_width()); | |
56 | validate!(height == vinfo.get_height()); | |
57 | ||
58 | br.seek(SeekFrom::Start(part2_off + (HEADER_SIZE as u64)))?; | |
59 | let tile_w = if tile_size == 2 { 2 } else { 4 }; | |
60 | let tsize = tile_w * BPP; | |
61 | ||
62 | match tile_size { | |
63 | 2 | 4 => { | |
64 | br.read_buf(&mut self.tiles[..tsize])?; | |
65 | let off = br.tell() as usize; | |
66 | let mut bir = BitReader::new(&src[off..], BitReaderMode::BE); | |
67 | for tile in 1..num_tiles { | |
68 | for i in 0..tsize { | |
69 | self.tiles[tile * tsize + i] = self.tiles[tile * tsize + i - tsize]; | |
70 | } | |
71 | let bits = bir.read(3)? as u8 + 1; | |
72 | validate!(bits < 8); | |
73 | for el in self.tiles[tile * tsize..][..tsize].iter_mut() { | |
74 | let mut delta = bir.read(bits)? as i16; | |
75 | if delta != 0 && bir.read_bool()? { | |
76 | delta = -delta; | |
77 | } | |
78 | *el = (i16::from(*el) + delta) as u8; | |
79 | } | |
80 | } | |
81 | }, | |
82 | _ => { | |
83 | validate!(tile_size == num_tiles * tsize); | |
84 | br.read_buf(&mut self.tiles[..tile_size])?; | |
85 | }, | |
86 | }; | |
87 | ||
88 | let bufinfo = alloc_video_buffer(vinfo, 0)?; | |
89 | let bufo = bufinfo.get_vbuf(); | |
90 | let mut buf = bufo.unwrap(); | |
91 | let stride = buf.get_stride(0); | |
92 | let data = buf.get_data_mut().unwrap(); | |
93 | let dst = data.as_mut_slice(); | |
94 | ||
95 | let mut br = BitReader::new(&src[HEADER_SIZE..], BitReaderMode::BE); | |
96 | let idx_bits = if num_tiles < 0x400 { 10 } else if num_tiles < 0x800 { 11 } else { 12 }; | |
97 | for y in (0..height).step_by(2) { | |
98 | for x in (0..width).step_by(tile_w) { | |
99 | let dst_pos = x * BPP + y * stride; | |
100 | if !br.read_bool()? { | |
101 | let idx = br.read(idx_bits)? as usize; | |
102 | validate!(idx < num_tiles); | |
103 | dst[dst_pos..][..tsize].copy_from_slice(&self.tiles[idx * tsize..][..tsize]); | |
104 | } else { | |
105 | let (mv_x, mv_y) = if br.read_bool()? { | |
106 | (0, -1) | |
107 | } else if br.read_bool()? { | |
108 | MV_2BIT[br.read(2)? as usize] | |
109 | } else { | |
110 | MV_4BIT[br.read(4)? as usize] | |
111 | }; | |
112 | ||
113 | let isrc = (dst_pos as isize) + isize::from(mv_x) * (tsize as isize) + isize::from(mv_y) * ((stride * 2) as isize); | |
114 | validate!(isrc >= 0); | |
115 | let src_pos = isrc as usize; | |
116 | validate!(src_pos + tsize <= dst.len()); | |
117 | let (src, dst) = dst.split_at_mut(dst_pos); | |
118 | dst[..tsize].copy_from_slice(&src[src_pos..][..tsize]); | |
119 | } | |
120 | } | |
121 | // double lines | |
122 | let lines = &mut dst[y * stride..]; | |
123 | let (src, dst) = lines.split_at_mut(stride); | |
124 | dst[..stride].copy_from_slice(src); | |
125 | } | |
126 | Ok((bufinfo, true)) | |
127 | } | |
128 | fn add_to_context(&mut self, prev: usize, cur: usize) { | |
129 | self.contexts[prev][self.ctx_pos[prev]] = cur; | |
130 | self.ctx_pos[prev] += 1; | |
131 | if self.ctx_pos[prev] == 16 { | |
132 | self.ctx_pos[prev] = 0; | |
133 | } | |
134 | } | |
135 | fn decode_v2(&mut self, src: &[u8]) -> DecoderResult<(NABufferType, bool)> { | |
136 | let mut mr = MemoryReader::new_read(src); | |
137 | let mut br = ByteReader::new(&mut mr); | |
138 | ||
139 | let mut has_tiles = false; | |
140 | let mut is_55 = false; | |
141 | loop { | |
142 | let ftype = br.read_byte()?; | |
143 | match ftype { | |
144 | 0x54 => { | |
145 | let size = br.read_u32le()? as usize; | |
146 | let num_tiles = br.read_u16le()? as usize; | |
147 | let tile_size = br.read_u16le()? as usize; | |
148 | ||
149 | let tile_w = if tile_size == 2 { 2 } else { 4 }; | |
150 | let tsize = tile_w * BPP; | |
151 | ||
152 | match tile_size { | |
153 | 2 | 4 => { | |
154 | validate!(size >= tsize); | |
155 | br.read_buf(&mut self.tiles[..tsize])?; | |
156 | let off = br.tell() as usize; | |
157 | let mut bir = BitReader::new(&src[off..][..size - tsize], BitReaderMode::LE); | |
158 | for tile in 1..num_tiles { | |
159 | let (prev_tiles, cur_tile) = self.tiles.split_at_mut(tile * tsize); | |
160 | cur_tile[..16].copy_from_slice(&prev_tiles[prev_tiles.len() - 16..]); | |
161 | for comp in 0..BPP { | |
162 | let bits = bir.read(3)? as u8; | |
163 | if bits == 0 { | |
164 | continue; | |
165 | } | |
166 | for i in 0..tile_size { | |
167 | let el = &mut cur_tile[i * BPP + comp]; | |
168 | *el = match bits { | |
169 | 7 => { | |
170 | bir.read(8)? as u8 | |
171 | }, | |
172 | _ => { | |
173 | let mut delta = bir.read(bits)? as i16; | |
174 | if delta != 0 && bir.read_bool()? { | |
175 | delta = -delta; | |
176 | } | |
177 | (i16::from(*el) + delta) as u8 | |
178 | }, | |
179 | }; | |
180 | } | |
181 | } | |
182 | } | |
183 | br.read_skip(size - tsize)?; | |
184 | has_tiles = true; | |
185 | }, | |
186 | _ => { | |
187 | unimplemented!(); | |
188 | }, | |
189 | }; | |
190 | }, | |
191 | 0x53 => break, | |
192 | 0x55 => { | |
193 | is_55 = true; | |
194 | break; | |
195 | }, | |
196 | _ => return Err(DecoderError::InvalidData), | |
197 | }; | |
198 | } | |
199 | ||
200 | let size = br.read_u32le()? as usize; | |
201 | validate!(size + HEADER_SIZE <= (br.left() as usize) + 4); | |
202 | let part2_off = br.read_u32le()?; | |
203 | validate!(part2_off as usize == size); | |
204 | let num_tiles = br.read_u16le()? as usize; | |
205 | validate!((0..4096).contains(&num_tiles)); | |
206 | let tile_size = br.read_u16le()? as usize; | |
207 | let width = br.read_u32le()? as usize; | |
208 | let height = br.read_u32le()? as usize; | |
209 | br.read_skip(0x1B)?; | |
210 | ||
211 | let vinfo = self.info.get_properties().get_video_info().unwrap(); | |
212 | validate!(width == vinfo.get_width()); | |
213 | validate!(height == vinfo.get_height()); | |
214 | let is_intra = is_55 && has_tiles; | |
215 | ||
216 | let mut vbuf = if is_intra { | |
217 | let binfo = alloc_video_buffer(vinfo, 0)?; | |
218 | let vbuf = binfo.get_vbuf().unwrap(); | |
219 | self.hams.add_frame(vbuf); | |
220 | self.hams.get_output_frame().unwrap() | |
221 | } else { | |
222 | if let Some(buf) = self.hams.clone_ref() { | |
223 | buf | |
224 | } else { | |
225 | return Err(DecoderError::MissingReference); | |
226 | } | |
227 | }; | |
228 | let stride = vbuf.get_stride(0); | |
229 | let data = vbuf.get_data_mut().unwrap(); | |
230 | let dst = data.as_mut_slice(); | |
231 | ||
232 | let tile_w = if tile_size == 2 { 2 } else { 4 }; | |
233 | let tsize = tile_w * BPP; | |
234 | let mut idx_bits = 0; | |
235 | let mut v = num_tiles; | |
236 | while v > 0 { | |
237 | idx_bits += 1; | |
238 | v >>= 1; | |
239 | } | |
240 | let start = br.tell() as usize; | |
241 | let mut br = BitReader::new(&src[start..], BitReaderMode::LE); | |
242 | let mut ypos = 0; | |
243 | let mut last_seen = [0usize.wrapping_sub(1); 4096]; | |
244 | let mut cand_list = Vec::with_capacity(4); | |
245 | let istride = width / tile_w; | |
246 | self.idx_buf.resize(istride * height, 0); | |
247 | self.contexts = [[0; 16]; 4096]; | |
248 | self.ctx_pos = [0; 4096]; | |
249 | ||
250 | for y in 0..height { | |
251 | for x8 in (0..istride).step_by(8) { | |
252 | let pos = ypos + x8; | |
253 | if br.read_bool()? { | |
254 | validate!(y > 0); | |
255 | for x in 0..8 { | |
256 | self.idx_buf[pos + x] = self.idx_buf[pos + x - istride]; | |
257 | } | |
258 | } else { | |
259 | for x in 0..8 { | |
260 | if br.read_bool()? { | |
261 | validate!(y > 0); | |
262 | self.idx_buf[pos + x] = self.idx_buf[pos + x - istride]; | |
263 | } else { | |
264 | let mode = br.read(2)?; | |
265 | match mode { | |
266 | 0 => { | |
267 | let idx = br.read(idx_bits)? as usize; | |
268 | self.idx_buf[pos + x] = idx; | |
269 | if y > 0 { | |
270 | self.add_to_context(self.idx_buf[pos + x - istride], idx); | |
271 | } | |
272 | }, | |
273 | 1 => { | |
274 | cand_list.clear(); | |
275 | let cur_pos = pos + x; | |
276 | if y > 0 { | |
277 | last_seen[self.idx_buf[cur_pos - istride]] = cur_pos; | |
278 | } | |
279 | if x8 + x > 0 { | |
280 | let src_idx = cur_pos - 1; | |
281 | if last_seen[self.idx_buf[src_idx]] != cur_pos { | |
282 | cand_list.push(self.idx_buf[src_idx]); | |
283 | last_seen[self.idx_buf[src_idx]] = cur_pos; | |
284 | } | |
285 | } | |
286 | if (y > 0) && (x8 + x > 0) { | |
287 | let src_idx = cur_pos - 1 - istride; | |
288 | if last_seen[self.idx_buf[src_idx]] != cur_pos { | |
289 | cand_list.push(self.idx_buf[src_idx]); | |
290 | last_seen[self.idx_buf[src_idx]] = cur_pos; | |
291 | } | |
292 | } | |
293 | if (y > 0) && (x8 + x + 1 < istride) { | |
294 | let src_idx = cur_pos + 1 - istride; | |
295 | if last_seen[self.idx_buf[src_idx]] != cur_pos { | |
296 | cand_list.push(self.idx_buf[src_idx]); | |
297 | last_seen[self.idx_buf[src_idx]] = cur_pos; | |
298 | } | |
299 | } | |
300 | if y > 1 { | |
301 | let src_idx = cur_pos - 2 * istride; | |
302 | if last_seen[self.idx_buf[src_idx]] != cur_pos { | |
303 | cand_list.push(self.idx_buf[src_idx]); | |
304 | last_seen[self.idx_buf[src_idx]] = cur_pos; | |
305 | } | |
306 | } | |
307 | ||
308 | validate!(!cand_list.is_empty()); | |
309 | self.idx_buf[cur_pos] = match cand_list.len() { | |
310 | 1 => cand_list[0], | |
311 | 2 => cand_list[br.read(1)? as usize], | |
312 | _ => { | |
313 | let idx = br.read(2)? as usize; | |
314 | validate!(idx < cand_list.len()); | |
315 | cand_list[idx] | |
316 | }, | |
317 | }; | |
318 | if y > 0 { | |
319 | self.add_to_context(self.idx_buf[cur_pos - istride], self.idx_buf[cur_pos]); | |
320 | } | |
321 | }, | |
322 | 2 => { | |
323 | validate!(y > 0); | |
324 | let top_idx = self.idx_buf[pos + x - istride]; | |
325 | let delta = br.read(4)? as usize + 1; | |
326 | self.idx_buf[pos + x] = if !br.read_bool()? { | |
327 | validate!(top_idx + delta < num_tiles); | |
328 | top_idx + delta | |
329 | } else { | |
330 | validate!(top_idx >= delta); | |
331 | top_idx - delta | |
332 | }; | |
333 | if y > 0 { | |
334 | self.add_to_context(self.idx_buf[pos + x - istride], self.idx_buf[pos + x]); | |
335 | } | |
336 | }, | |
337 | _ => { | |
338 | validate!(y > 0); | |
339 | let idx = br.read(4)? as usize; | |
340 | self.idx_buf[pos + x] = self.contexts[self.idx_buf[pos + x - istride]][idx]; | |
341 | }, | |
342 | } | |
343 | } | |
344 | } | |
345 | } | |
346 | } | |
347 | ypos += istride; | |
348 | } | |
349 | ||
350 | for (dline, sline) in dst.chunks_mut(stride).take(height).zip(self.idx_buf.chunks_exact(istride)) { | |
351 | for (dst, &idx) in dline.chunks_exact_mut(tsize).zip(sline.iter()) { | |
352 | if idx != 0 || is_intra { | |
353 | dst.copy_from_slice(&self.tiles[idx * tsize..][..tsize]); | |
354 | } | |
355 | } | |
356 | } | |
357 | ||
358 | Ok((NABufferType::Video(vbuf), is_intra)) | |
359 | } | |
360 | } | |
361 | ||
362 | const RGBA_FORMAT: NAPixelFormaton = NAPixelFormaton { | |
363 | model: ColorModel::RGB(RGBSubmodel::RGB), components: 4, | |
364 | comp_info: [ | |
365 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }), | |
366 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }), | |
367 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }), | |
368 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 3, next_elem: 4 }), | |
369 | None ], | |
370 | elem_size: 4, be: false, alpha: true, palette: false }; | |
371 | ||
372 | impl NADecoder for ArxelVideoDecoder { | |
373 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
374 | if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { | |
375 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), true, RGBA_FORMAT)); | |
376 | if let Some(edata) = info.get_extradata() { | |
377 | validate!(!edata.is_empty()); | |
378 | if edata[0] > 1 { | |
379 | return Err(DecoderError::NotImplemented); | |
380 | } | |
381 | self.version = edata[0]; | |
382 | } | |
383 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); | |
384 | ||
385 | Ok(()) | |
386 | } else { | |
387 | Err(DecoderError::InvalidData) | |
388 | } | |
389 | } | |
390 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
391 | let src = pkt.get_buffer(); | |
392 | validate!(src.len() > HEADER_SIZE); | |
393 | ||
394 | let (bufinfo, is_intra) = match self.version { | |
395 | 0 => self.decode_v1(&src)?, | |
396 | 1 => self.decode_v2(&src)?, | |
397 | _ => return Err(DecoderError::NotImplemented), | |
398 | }; | |
399 | ||
400 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); | |
401 | frm.set_keyframe(is_intra); | |
402 | frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); | |
403 | Ok(frm.into_ref()) | |
404 | } | |
405 | fn flush(&mut self) { | |
406 | self.hams.clear(); | |
407 | } | |
408 | } | |
409 | ||
410 | impl NAOptionHandler for ArxelVideoDecoder { | |
411 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
412 | fn set_options(&mut self, _options: &[NAOption]) { } | |
413 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
414 | } | |
415 | ||
416 | ||
417 | pub fn get_decoder() -> Box<dyn NADecoder + Send> { | |
418 | Box::new(ArxelVideoDecoder::new()) | |
419 | } | |
420 | ||
421 | #[cfg(test)] | |
422 | mod test { | |
423 | use nihav_core::codecs::RegisteredDecoders; | |
424 | use nihav_core::demuxers::RegisteredDemuxers; | |
425 | use nihav_codec_support::test::dec_video::*; | |
426 | use crate::game_register_all_decoders; | |
427 | use crate::game_register_all_demuxers; | |
428 | // sample from the Ring game | |
429 | #[test] | |
430 | fn test_arxel_video() { | |
431 | let mut dmx_reg = RegisteredDemuxers::new(); | |
432 | game_register_all_demuxers(&mut dmx_reg); | |
433 | let mut dec_reg = RegisteredDecoders::new(); | |
434 | game_register_all_decoders(&mut dec_reg); | |
435 | ||
436 | test_decoding("arxel-cnm", "arxel-video", "assets/Game/logo.cnm", Some(10), &dmx_reg, &dec_reg, | |
437 | ExpectedTestResult::MD5([0x9b1fc970, 0x1fe86e2c, 0x44dd9255, 0x3920c49b])); | |
438 | } | |
439 | // sample from Faust: The Seven Games of the Soul game | |
440 | #[test] | |
441 | fn test_arxel_video_v2() { | |
442 | let mut dmx_reg = RegisteredDemuxers::new(); | |
443 | game_register_all_demuxers(&mut dmx_reg); | |
444 | let mut dec_reg = RegisteredDecoders::new(); | |
445 | game_register_all_decoders(&mut dec_reg); | |
446 | ||
447 | test_decoding("arxel-cnm", "arxel-video", "assets/Game/logo.CI2", Some(10), &dmx_reg, &dec_reg, | |
448 | ExpectedTestResult::MD5([0x3bf66a39, 0x6627f529, 0x4ed19e8e, 0xc0693aae])); | |
449 | } | |
450 | } |