| 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 | } |