]> git.nihav.org Git - nihav.git/commitdiff
Actimagine VX demuxer and decoders
authorKostya Shishkov <kostya.shishkov@gmail.com>
Mon, 10 Aug 2020 16:18:37 +0000 (18:18 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 13 Aug 2020 14:11:23 +0000 (16:11 +0200)
nihav-game/Cargo.toml
nihav-game/src/codecs/mod.rs
nihav-game/src/codecs/vx.rs [new file with mode: 0644]
nihav-game/src/demuxers/mod.rs
nihav-game/src/demuxers/vx.rs [new file with mode: 0644]
nihav-registry/src/detect.rs
nihav-registry/src/register.rs

index 4f943a0e3b6637432b172b3f349d53046b5cb114..c13f9f335f126fc4b5c19aa2ad3b7a0a6b53b4d4 100644 (file)
@@ -18,22 +18,24 @@ nihav_commonfmt = { path = "../nihav-commonfmt" }
 [features]
 default = ["all_decoders", "all_demuxers"]
 demuxers = []
-all_demuxers = ["demuxer_bmv", "demuxer_bmv3", "demuxer_gdv", "demuxer_vmd"]
+all_demuxers = ["demuxer_bmv", "demuxer_bmv3", "demuxer_gdv", "demuxer_vmd", "demuxer_vx"]
 demuxer_bmv = ["demuxers"]
 demuxer_bmv3 = ["demuxers"]
 demuxer_gdv = ["demuxers"]
 demuxer_vmd = ["demuxers"]
+demuxer_vx = ["demuxers"]
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 decoders = []
 
-all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_gdvvid", "decoder_midivid", "decoder_midivid3", "decoder_vmd"]
+all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_gdvvid", "decoder_midivid", "decoder_midivid3", "decoder_vmd", "decoder_vx"]
 decoder_bmv = ["decoders"]
 decoder_bmv3 = ["decoders"]
 decoder_gdvvid = ["decoders"]
 decoder_midivid = ["decoders"]
 decoder_midivid3 = ["decoders"]
 decoder_vmd = ["decoders"]
+decoder_vx = ["decoders"]
 
 all_audio_decoders = ["decoder_lhst500f22"]
 decoder_lhst500f22 = ["decoders"]
index 064b567343717ecce2cc48aa0bb95d9d5dcbbbd2..4952b13e0cd3d506100ae47a139d0508af41d757 100644 (file)
@@ -18,6 +18,8 @@ pub mod midivid;
 pub mod midivid3;
 #[cfg(feature="decoder_vmd")]
 pub mod vmd;
+#[cfg(feature="decoder_vx")]
+pub mod vx;
 
 const GAME_CODECS: &[DecoderInfo] = &[
 #[cfg(feature="decoder_gdvvid")]
@@ -42,6 +44,10 @@ const GAME_CODECS: &[DecoderInfo] = &[
     DecoderInfo { name: "midivid", get_decoder: midivid::get_decoder_video },
 #[cfg(feature="decoder_midivid3")]
     DecoderInfo { name: "midivid3", get_decoder: midivid3::get_decoder_video },
+#[cfg(feature="decoder_vx")]
+    DecoderInfo { name: "vxaudio", get_decoder: vx::get_decoder_audio },
+#[cfg(feature="decoder_vx")]
+    DecoderInfo { name: "vxvideo", get_decoder: vx::get_decoder_video },
 ];
 
 /// Registers all available codecs provided by this crate.
diff --git a/nihav-game/src/codecs/vx.rs b/nihav-game/src/codecs/vx.rs
new file mode 100644 (file)
index 0000000..1a37359
--- /dev/null
@@ -0,0 +1,1507 @@
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_core::io::intcode::*;
+use std::str::FromStr;
+
+use std::ops::*;
+
+#[derive(Clone,Copy,Default,Debug,PartialEq)]
+struct MV {
+    x: i8,
+    y: i8,
+}
+
+impl Add for MV {
+    type Output = MV;
+    fn add(self, other: MV) -> MV { MV { x: self.x + other.x, y: self.y + other.y } }
+}
+
+impl AddAssign for MV {
+    fn add_assign(&mut self, other: MV) { self.x += other.x; self.y += other.y; }
+}
+
+impl Sub for MV {
+    type Output = MV;
+    fn sub(self, other: MV) -> MV { MV { x: self.x - other.x, y: self.y - other.y } }
+}
+
+impl SubAssign for MV {
+    fn sub_assign(&mut self, other: MV) { self.x -= other.x; self.y -= other.y; }
+}
+
+impl MV {
+    fn pred(t: MV, tl: MV, l: MV) -> MV {
+        let x0 = i16::from(t.x);
+        let x1 = i16::from(tl.x);
+        let x2 = i16::from(l.x);
+        let y0 = i16::from(t.y);
+        let y1 = i16::from(tl.y);
+        let y2 = i16::from(l.y);
+        let x = x0 + x1 + x2 - x0.min(x1).min(x2) - x0.max(x1).max(x2);
+        let y = y0 + y1 + y2 - y0.min(y1).min(y2) - y0.max(y1).max(y2);
+        MV{ x: x as i8, y: y as i8 }
+    }
+}
+
+trait ReadCodes {
+    fn read_gammap(&mut self) -> BitReaderResult<u32>;
+    fn read_gammap_s(&mut self) -> BitReaderResult<i32>;
+    fn read_unary(&mut self) -> BitReaderResult<u32>;
+}
+
+impl<'a> ReadCodes for BitReader<'a> {
+    fn read_gammap(&mut self) -> BitReaderResult<u32> {
+        Ok(self.read_code(UintCodeType::GammaP)? - 1)
+    }
+    fn read_gammap_s(&mut self) -> BitReaderResult<i32> {
+        let val = self.read_code(UintCodeType::GammaP)?;
+        if (val & 1) == 0 {
+            Ok((val >> 1) as i32)
+        } else {
+            Ok((1 - (val as i32)) >> 1)
+        }
+    }
+    fn read_unary(&mut self) -> BitReaderResult<u32> {
+        self.read_code(UintCodeType::UnaryZeroes)
+    }
+}
+
+const CUR_BUF: usize = 3;
+const CHROMA_OFF: usize = 256 * 256;
+
+macro_rules! mc {
+    ($bufs: expr, $src_id: expr, $dst_id: expr, $mx: expr, $my: expr, $xpos: expr, $ypos: expr, $w: expr, $h: expr, $ydelta: expr, $udelta: expr, $vdelta: expr, $width: expr, $height: expr) => {
+        if ($mx + ($xpos as isize) < 0) || ($mx + (($xpos + $w) as isize) > ($width as isize)) {
+            return Err(DecoderError::InvalidData);
+        }
+        if ($my + ($ypos as isize) < 0) || ($my + (($ypos + $h) as isize) > ($height as isize)) {
+            return Err(DecoderError::InvalidData);
+        }
+
+        let sx = (($xpos as isize) + $mx) as usize;
+        let sy = (($ypos as isize) + $my) as usize;
+
+        let mut soff = sx + sy * 256;
+        let mut doff = $xpos + $ypos * 256;
+        for _y in 0..$h {
+            for x in 0..$w {
+                $bufs[$dst_id][doff + x] = (i32::from($bufs[$src_id][soff + x]) + $ydelta).max(0).min(255) as u8;
+            }
+            soff += 256;
+            doff += 256;
+        }
+        let mut soff = CHROMA_OFF + sx / 2 + (sy / 2) * 256;
+        let mut doff = CHROMA_OFF + $xpos / 2 + $ypos / 2 * 256;
+        for _y in 0..$h / 2 {
+            for x in 0..$w / 2 {
+                $bufs[$dst_id][doff + x] = (i32::from($bufs[$src_id][soff + x]) + $udelta).max(0).min(255) as u8;
+                $bufs[$dst_id][doff + x + 128] = (i32::from($bufs[$src_id][soff + x + 128]) + $vdelta).max(0).min(255) as u8;
+            }
+            soff += 256;
+            doff += 256;
+        }
+    }
+}
+
+fn pred_dc(buf: &mut [u8], mut pos: usize, x: usize, y: usize, w: usize, h: usize) {
+    let dc = if x == 0 && y == 0 {
+            128
+        } else if y == 0 {
+            let mut sum = 0;
+            let hh = h as u16;
+            for i in 0..h {
+                sum += u16::from(buf[pos - 1 + i * 256]);
+            }
+            ((sum + hh / 2) / hh) as u8
+        } else if x == 0 {
+            let mut sum = 0;
+            let ww = w as u16;
+            for i in 0..w {
+                sum += u16::from(buf[pos - 256 + i]);
+            }
+            ((sum + ww / 2) / ww) as u8
+        } else {
+            let mut sum = 0;
+            let ww = w as u16;
+            for i in 0..w {
+                sum += u16::from(buf[pos - 256 + i]);
+            }
+            let wdc = (sum + ww / 2) / ww;
+
+            let mut sum = 0;
+            let hh = h as u16;
+            for i in 0..h {
+                sum += u16::from(buf[pos - 1 + i * 256]);
+            }
+            let hdc = (sum + hh / 2) / hh;
+
+            ((wdc + hdc + 1) >> 1) as u8
+        };
+    for _ in 0..h {
+        for x in 0..w {
+            buf[pos + x] = dc;
+        }
+        pos += 256;
+    }
+}
+fn pred_dc4x4(buf: &mut [u8], mut pos: usize, x: usize, y: usize) {
+    if x == 0 && y == 0 {
+        for _ in 0..4 {
+            for x in 0..4 {
+                buf[x] = 0x80;
+            }
+            pos += 256;
+        }
+        return;
+    }
+    let mut sum = 0;
+    let mut shift = 1;
+    if y != 0 {
+        for i in 0..4 {
+            sum += u16::from(buf[pos - 256 + i]);
+        }
+        sum += 2;
+        shift += 1;
+    }
+    if x != 0 {
+        for i in 0..4 {
+            sum += u16::from(buf[pos + i * 256 - 1]);
+        }
+        sum += 2;
+        shift += 1;
+    }
+    let dc = (sum >> shift) as u8;
+    for _ in 0..4 {
+        for x in 0..4 {
+            buf[pos + x] = dc;
+        }
+        pos += 256;
+    }
+}
+
+fn pred_hor(buf: &mut [u8], mut pos: usize, w: usize, h: usize) {
+    for _ in 0..h {
+        for x in 0..w {
+            buf[pos + x] = buf[pos - 1];
+        }
+        pos += 256;
+    }
+}
+
+fn pred_ver(buf: &mut [u8], mut pos: usize, w: usize, h: usize) {
+    for _ in 0..h {
+        for x in 0..w {
+            buf[pos + x] = buf[pos + x - 256];
+        }
+        pos += 256;
+    }
+}
+
+fn avg(a: u8, b: u8) -> u8 {
+    ((u16::from(a) + u16::from(b) + 1) >> 1) as u8
+}
+fn avg_nr(a: u8, b: u8) -> u8 {
+    ((u16::from(a) + u16::from(b)) >> 1) as u8
+}
+fn interp2(a: u8, b: u8, c: u8) -> u8 {
+    ((u16::from(a) + 2 * u16::from(b) + u16::from(c) + 2) >> 2) as u8
+}
+
+fn pred_ddown_left(blk: &mut [u8], pos: usize) {
+    let (t0, t1, t2, t3) = (blk[pos - 256], blk[pos - 256 + 1], blk[pos - 256 + 2], blk[pos - 256 + 3]);
+    let (t4, t5, t6, t7) = (blk[pos - 256 + 4], blk[pos - 256 + 5], blk[pos - 256 + 6], blk[pos - 256 + 7]);
+
+    blk[pos + 0 + 0 * 256] = interp2(t0, t1, t2);
+    let pix = interp2(t1, t2, t3);
+    blk[pos + 1 + 0 * 256] = pix;
+    blk[pos + 0 + 1 * 256] = pix;
+    let pix = interp2(t2, t3, t4);
+    blk[pos + 2 + 0 * 256] = pix;
+    blk[pos + 1 + 1 * 256] = pix;
+    blk[pos + 0 + 2 * 256] = pix;
+    let pix = interp2(t3, t4, t5);
+    blk[pos + 3 + 0 * 256] = pix;
+    blk[pos + 2 + 1 * 256] = pix;
+    blk[pos + 1 + 2 * 256] = pix;
+    blk[pos + 0 + 3 * 256] = pix;
+    let pix = interp2(t4, t5, t6);
+    blk[pos + 3 + 1 * 256] = pix;
+    blk[pos + 2 + 2 * 256] = pix;
+    blk[pos + 1 + 3 * 256] = pix;
+    let pix = interp2(t5, t6, t7);
+    blk[pos + 3 + 2 * 256] = pix;
+    blk[pos + 2 + 3 * 256] = pix;
+    blk[pos + 3 + 3 * 256] = interp2(t6, t7, t7);
+}
+fn pred_ddown_right(blk: &mut [u8], pos: usize) {
+    let (l0, l1, l2, l3) = (blk[pos - 1], blk[pos + 256 - 1], blk[pos + 256 * 2 - 1], blk[pos + 256 * 3 - 1]);
+    let (tl, t0, t1, t2) = (blk[pos - 256 - 1], blk[pos - 256], blk[pos - 256 + 1], blk[pos - 256 + 2]);
+    let t3 = blk[pos - 256 + 3];
+
+    blk[pos + 0 + 3 * 256] = interp2(l1, l2, l3);
+    let pix = interp2(l0, l1, l2);
+    blk[pos + 0 + 2 * 256] = pix;
+    blk[pos + 1 + 3 * 256] = pix;
+    let pix = interp2(tl, l0, l1);
+    blk[pos + 0 + 1 * 256] = pix;
+    blk[pos + 1 + 2 * 256] = pix;
+    blk[pos + 2 + 3 * 256] = pix;
+    let pix = interp2(l0, tl, t0);
+    blk[pos + 0 + 0 * 256] = pix;
+    blk[pos + 1 + 1 * 256] = pix;
+    blk[pos + 2 + 2 * 256] = pix;
+    blk[pos + 3 + 3 * 256] = pix;
+    let pix = interp2(tl, t0, t1);
+    blk[pos + 1 + 0 * 256] = pix;
+    blk[pos + 2 + 1 * 256] = pix;
+    blk[pos + 3 + 2 * 256] = pix;
+    let pix = interp2(t0, t1, t2);
+    blk[pos + 2 + 0 * 256] = pix;
+    blk[pos + 3 + 1 * 256] = pix;
+    blk[pos + 3 + 0 * 256] = interp2(t1, t2, t3);
+}
+fn pred_ver_right(blk: &mut [u8], pos: usize) {
+    let (l0, l1, l2) = (blk[pos - 1], blk[pos + 256 - 1], blk[pos + 256 * 2 - 1]);
+    let (tl, t0, t1, t2, t3) = (blk[pos - 256 - 1], blk[pos - 256], blk[pos - 256 + 1], blk[pos - 256 + 2], blk[pos - 256 + 3]);
+
+    blk[pos + 0 + 3 * 256] = interp2(l0, l1, l2);
+    blk[pos + 0 + 2 * 256] = interp2(tl, l0, l1);
+    let pix = interp2(l0, tl, t0);
+    blk[pos + 0 + 1 * 256] = pix;
+    blk[pos + 1 + 3 * 256] = pix;
+    let pix = avg(tl, t0);
+    blk[pos + 0 + 0 * 256] = pix;
+    blk[pos + 1 + 2 * 256] = pix;
+    let pix = interp2(tl, t0, t1);
+    blk[pos + 1 + 1 * 256] = pix;
+    blk[pos + 2 + 3 * 256] = pix;
+    let pix = avg(t0, t1);
+    blk[pos + 1 + 0 * 256] = pix;
+    blk[pos + 2 + 2 * 256] = pix;
+    let pix = interp2(t0, t1, t2);
+    blk[pos + 2 + 1 * 256] = pix;
+    blk[pos + 3 + 3 * 256] = pix;
+    let pix = avg(t1, t2);
+    blk[pos + 2 + 0 * 256] = pix;
+    blk[pos + 3 + 2 * 256] = pix;
+    blk[pos + 3 + 1 * 256] = interp2(t1, t2, t3);
+    blk[pos + 3 + 0 * 256] = avg(t2, t3);
+}
+fn pred_hor_down(blk: &mut [u8], pos: usize) {
+    let (l0, l1, l2, l3) = (blk[pos - 1], blk[pos + 256 - 1], blk[pos + 256 * 2 - 1], blk[pos + 256 * 3 - 1]);
+    let (tl, t0, t1, t2) = (blk[pos - 256 - 1], blk[pos - 256], blk[pos - 256 + 1], blk[pos - 256 + 2]);
+
+    blk[pos + 0 + 3 * 256] = avg(l2, l3);
+    blk[pos + 1 + 3 * 256] = interp2(l1, l2, l3);
+    let pix = avg(l1, l2);
+    blk[pos + 0 + 2 * 256] = pix;
+    blk[pos + 2 + 3 * 256] = pix;
+    let pix = interp2(l0, l1, l2);
+    blk[pos + 1 + 2 * 256] = pix;
+    blk[pos + 3 + 3 * 256] = pix;
+    let pix = avg(l0, l1);
+    blk[pos + 0 + 1 * 256] = pix;
+    blk[pos + 2 + 2 * 256] = pix;
+    let pix = interp2(tl, t0, l1);
+    blk[pos + 1 + 1 * 256] = pix;
+    blk[pos + 3 + 2 * 256] = pix;
+    let pix = avg(tl, l0);
+    blk[pos + 0 + 0 * 256] = pix;
+    blk[pos + 2 + 1 * 256] = pix;
+    let pix = interp2(l0, tl, t0);
+    blk[pos + 1 + 0 * 256] = pix;
+    blk[pos + 3 + 1 * 256] = pix;
+    blk[pos + 2 + 0 * 256] = interp2(tl, t0, t1);
+    blk[pos + 3 + 0 * 256] = interp2(t0, t1, t2);
+}
+fn pred_ver_left(blk: &mut [u8], pos: usize) {
+    let (t0, t1, t2, t3) = (blk[pos - 256], blk[pos - 256 + 1], blk[pos - 256 + 2], blk[pos - 256 + 3]);
+    let (t4, t5, t6) = (blk[pos - 256 + 4], blk[pos - 256 + 5], blk[pos - 256 + 6]);
+
+    blk[pos + 3 + 3 * 256] = interp2(t4, t5, t6);
+    blk[pos + 3 + 2 * 256] = avg(t4, t5);
+    let pix = interp2(t3, t4, t5);
+    blk[pos + 3 + 1 * 256] = pix;
+    blk[pos + 2 + 3 * 256] = pix;
+    let pix = avg(t3, t4);
+    blk[pos + 3 + 0 * 256] = pix;
+    blk[pos + 2 + 2 * 256] = pix;
+    let pix = interp2(t2, t3, t4);
+    blk[pos + 2 + 1 * 256] = pix;
+    blk[pos + 1 + 3 * 256] = pix;
+    let pix = avg(t2, t3);
+    blk[pos + 2 + 0 * 256] = pix;
+    blk[pos + 1 + 2 * 256] = pix;
+    let pix = interp2(t1, t2, t3);
+    blk[pos + 1 + 1 * 256] = pix;
+    blk[pos + 0 + 3 * 256] = pix;
+    let pix = avg(t1, t2);
+    blk[pos + 1 + 0 * 256] = pix;
+    blk[pos + 0 + 2 * 256] = pix;
+    blk[pos + 0 + 1 * 256] = interp2(t0, t1, t2);
+    blk[pos + 0 + 0 * 256] = avg(t0, t1);
+}
+fn pred_hor_up(blk: &mut [u8], pos: usize) {
+    let (l0, l1, l2, l3) = (blk[pos - 1], blk[pos + 256 - 1], blk[pos + 256 * 2 - 1], blk[pos + 256 * 3 - 1]);
+
+    blk[pos + 0 + 0 * 256] = avg(l0, l1);
+    blk[pos + 1 + 0 * 256] = interp2(l0, l1, l2);
+    let pix = avg(l1, l2);
+    blk[pos + 2 + 0 * 256] = pix;
+    blk[pos + 0 + 1 * 256] = pix;
+    let pix = interp2(l1, l2, l3);
+    blk[pos + 3 + 0 * 256] = pix;
+    blk[pos + 1 + 1 * 256] = pix;
+    let pix = avg(l2, l3);
+    blk[pos + 2 + 1 * 256] = pix;
+    blk[pos + 0 + 2 * 256] = pix;
+    let pix = interp2(l2, l3, l3);
+    blk[pos + 3 + 1 * 256] = pix;
+    blk[pos + 1 + 2 * 256] = pix;
+    blk[pos + 0 + 3 * 256] = l3;
+    blk[pos + 2 + 2 * 256] = l3;
+    blk[pos + 3 + 2 * 256] = l3;
+    blk[pos + 1 + 3 * 256] = l3;
+    blk[pos + 2 + 3 * 256] = l3;
+    blk[pos + 3 + 3 * 256] = l3;
+}
+
+fn pred_plane(blk: &mut [u8], pos: usize, w: usize, h: usize) {
+    if w == 1 && h == 1 {
+        return;
+    }
+    if h == 1 {
+        blk[pos + w / 2 - 1] = avg_nr(blk[pos - 1], blk[pos + w - 1]);
+        if w > 2 {
+            pred_plane(blk, pos,         w / 2, 1);
+            pred_plane(blk, pos + w / 2, w / 2, 1);
+        }
+        return;
+    }
+    if w == 1 {
+        blk[pos + (h / 2 - 1) * 256] = avg_nr(blk[pos - 256], blk[pos + (h - 1) * 256]);
+        if h > 2 {
+            pred_plane(blk, pos,                 1, h / 2);
+            pred_plane(blk, pos + (h / 2) * 256, 1, h / 2);
+        }
+        return;
+    }
+
+    let is_even_block = ((w.trailing_zeros() + h.trailing_zeros()) & 1) == 0; // i.e. w*h = 256/64/16/4
+
+    let hoff  = (h     - 1) * 256;
+    let h2off = (h / 2 - 1) * 256;
+    let woff  = w - 1;
+    let w2off = w / 2 - 1;
+    let dr = blk[pos + woff + hoff];
+    let tr = blk[pos + woff - 256];
+    let dl = blk[pos -    1 + hoff];
+
+    let dm = avg_nr(dl, dr);
+    blk[pos + w2off + hoff]  = dm;
+    blk[pos + woff  + h2off] = avg_nr(tr, dr);
+    let val2 = if is_even_block { blk[pos + w2off - 256] } else { blk[pos - 1 + h2off] };
+    blk[pos + w2off + h2off] = avg_nr(dm, val2);
+
+    let hw = w / 2;
+    let hh = h / 2;
+    pred_plane(blk, pos,                 hw, hh);
+    pred_plane(blk, pos + hw,            hw, hh);
+    pred_plane(blk, pos      + hh * 256, hw, hh);
+    pred_plane(blk, pos + hw + hh * 256, hw, hh);
+}
+fn pred_plane_delta(blk: &mut [u8], pos: usize, w: usize, h: usize, delta: i32) {
+    let tr = blk[pos + w - 1 - 256];
+    let dl = blk[pos + 256 * (h - 1) - 1];
+    //XXX: this is a hack since it should not wrap around normally but this conceals some artefacts so let it be until the reconstruction is properly fixed
+    let pred = (((i32::from(tr) + i32::from(dl) + 1) >> 1) + delta).max(0).min(255) as u8;
+    //let pred = avg(tr, dl).wrapping_add(delta as u8);
+    blk[pos + 256 * (h - 1) + w - 1] = pred;
+    pred_plane(blk, pos, w, h);
+}
+
+struct Codebooks {
+    nc_cb:          [Codebook<u8>; 3],
+    num_zero_cb:    [Codebook<u8>; 15],
+    zero_run_cb:    [Codebook<u8>; 6],
+}
+
+fn map_idx(idx: usize) -> u8 { idx as u8 }
+macro_rules! create_cb {
+    ($bits_tab: expr, $lens_tab: expr) => {{
+        let mut cbr = TableCodebookDescReader::new($bits_tab, $lens_tab, map_idx);
+        Codebook::new(&mut cbr, CodebookMode::MSB).unwrap()
+    }}
+}
+
+impl Codebooks {
+    fn new() -> Self {
+        let nc_cb0 = create_cb!(&NC_BITS[0], &NC_LENS[0]);
+        let nc_cb1 = create_cb!(&NC_BITS[1], &NC_LENS[1]);
+        let nc_cb2 = create_cb!(&NC_BITS[2], &NC_LENS[2]);
+
+        let nz0  = create_cb!(&NUM_ZERO_BITS[ 0], &NUM_ZERO_LENS[ 0]);
+        let nz1  = create_cb!(&NUM_ZERO_BITS[ 1], &NUM_ZERO_LENS[ 1]);
+        let nz2  = create_cb!(&NUM_ZERO_BITS[ 2], &NUM_ZERO_LENS[ 2]);
+        let nz3  = create_cb!(&NUM_ZERO_BITS[ 3], &NUM_ZERO_LENS[ 3]);
+        let nz4  = create_cb!(&NUM_ZERO_BITS[ 4], &NUM_ZERO_LENS[ 4]);
+        let nz5  = create_cb!(&NUM_ZERO_BITS[ 5], &NUM_ZERO_LENS[ 5]);
+        let nz6  = create_cb!(&NUM_ZERO_BITS[ 6], &NUM_ZERO_LENS[ 6]);
+        let nz7  = create_cb!(&NUM_ZERO_BITS[ 7], &NUM_ZERO_LENS[ 7]);
+        let nz8  = create_cb!(&NUM_ZERO_BITS[ 8], &NUM_ZERO_LENS[ 8]);
+        let nz9  = create_cb!(&NUM_ZERO_BITS[ 9], &NUM_ZERO_LENS[ 9]);
+        let nz10 = create_cb!(&NUM_ZERO_BITS[10], &NUM_ZERO_LENS[10]);
+        let nz11 = create_cb!(&NUM_ZERO_BITS[11], &NUM_ZERO_LENS[11]);
+        let nz12 = create_cb!(&NUM_ZERO_BITS[12], &NUM_ZERO_LENS[12]);
+        let nz13 = create_cb!(&NUM_ZERO_BITS[13], &NUM_ZERO_LENS[13]);
+        let nz14 = create_cb!(&NUM_ZERO_BITS[14], &NUM_ZERO_LENS[14]);
+
+        let zcb0 = create_cb!(&ZERO_RUN_BITS[0], &ZERO_RUN_LENS[0]);
+        let zcb1 = create_cb!(&ZERO_RUN_BITS[1], &ZERO_RUN_LENS[1]);
+        let zcb2 = create_cb!(&ZERO_RUN_BITS[2], &ZERO_RUN_LENS[2]);
+        let zcb3 = create_cb!(&ZERO_RUN_BITS[3], &ZERO_RUN_LENS[3]);
+        let zcb4 = create_cb!(&ZERO_RUN_BITS[4], &ZERO_RUN_LENS[4]);
+        let zcb5 = create_cb!(&ZERO_RUN_BITS[5], &ZERO_RUN_LENS[5]);
+        Self {
+            nc_cb:          [nc_cb0, nc_cb1, nc_cb2],
+            num_zero_cb:    [nz0, nz1, nz2, nz3, nz4, nz5, nz6, nz7, nz8, nz9, nz10, nz11, nz12, nz13, nz14],
+            zero_run_cb:    [zcb0, zcb1, zcb2, zcb3, zcb4, zcb5],
+        }
+    }
+}
+
+macro_rules! transform {
+    ($a: expr, $b: expr, $c: expr, $d: expr, $q0: expr, $q1: expr) => {
+        let t0 = ($a + $c).wrapping_mul($q0);
+        let t1 = ($a - $c).wrapping_mul($q0);
+        let tb = $b.wrapping_mul($q1);
+        let td = $d.wrapping_mul($q1);
+        let t2 =  tb       + (td >> 1);
+        let t3 = (tb >> 1) -  td;
+        $a = t0 + t2;
+        $b = t1 + t3;
+        $c = t1 - t3;
+        $d = t0 - t2;
+    }
+}
+
+fn idct_add(qmat: &[i32; 8], blk: &mut [i32; 16], dst: &mut [u8]) {
+    for i in 0..4 {
+        transform!(blk[i + 0 * 4], blk[i + 1 * 4], blk[i + 2 * 4], blk[i + 3 * 4], qmat[i], qmat[i + 4]);
+    }
+    for (dline, row) in dst.chunks_mut(256).zip(blk.chunks_mut(4)) {
+        transform!(row[0], row[1], row[2], row[3], 1, 1);
+        for (out, coef) in dline.iter_mut().zip(row.iter_mut()) {
+            *out = (i32::from(*out) + ((*coef + 0x20) >> 6)).max(0).min(255) as u8;
+        }
+    }
+}
+
+fn decode_coeffs(br: &mut BitReader, codebooks: &Codebooks, qmat: &[i32; 8], ctx: u8, dst: &mut [u8]) -> DecoderResult<u8> {
+    const ZIGZAG: [usize; 16] = [
+        0,  1,  4,  8,
+        5,  2,  3,  6,
+        9, 12, 13, 10,
+        7, 11, 14, 15
+    ];
+    const MAX_LEVEL: [i32; 6] = [ 2, 5, 11, 23, 47, 0x8000 ];
+
+    let (ncoeffs, nones) = if ctx < 8 {
+            let sym                     = br.read_cb(&codebooks.nc_cb[NC_MAP[ctx as usize]])?;
+            if sym == 0 {
+                return Ok(0);
+            }
+            (sym >> 2, sym & 3)
+        } else {
+            let ncoeffs                 = (br.read(4)? + 1) as u8;
+            let nones                   = br.read(2)? as u8;
+            if ncoeffs < nones {
+                return Ok(0);
+            }
+            (ncoeffs, nones)
+        };
+    let mut num_zero = if ncoeffs == 16 { 0 } else {
+            br.read_cb(&codebooks.num_zero_cb[ncoeffs as usize - 1])?
+        };
+    validate!(ncoeffs + num_zero <= 16);
+    let mut blk = [0i32; 16];
+    let mut level = 0usize;
+    let mut coef_left = ncoeffs;
+    let mut ones_left = nones;
+    let mut idx = ncoeffs + num_zero;
+    while coef_left > 0 {
+        let val = if ones_left > 0 {
+                ones_left -= 1;
+                if !br.read_bool()? { 1 } else { -1 }
+            } else {
+                let prefix              = br.read_unary()?;
+                let val = if prefix < 15 {
+                        (br.read(level as u8)? | (prefix << level)) as i32
+                    } else {
+                        (br.read(11)? + (15 << level)) as i32
+                    };
+                if val > MAX_LEVEL[level] {
+                    level += 1;
+                }
+                if !br.read_bool()? {
+                    val + 1
+                } else {
+                    -(val + 1)
+                }
+            };
+        idx -= 1;
+        blk[ZIGZAG[idx as usize]] = val;
+        coef_left -= 1;
+        if num_zero > 0 && coef_left > 0 {
+            let run = if num_zero < 7 {
+                    br.read_cb(&codebooks.zero_run_cb[num_zero as usize - 1])?
+                } else {
+                    if br.peek(3) != 0 {
+                        7 - (br.read(3)? as u8)
+                    } else {
+                        (br.read_unary()? as u8) + 4
+                    }
+                };
+            validate!(run <= num_zero);
+            idx      -= run;
+            num_zero -= run;
+        }
+    }
+    idct_add(qmat, &mut blk, dst);
+    Ok(ncoeffs)
+}
+
+const NCSTRIDE: usize = 64 + 1;
+
+struct VXVideoDecoder {
+    info:       NACodecInfoRef,
+    width:      usize,
+    height:     usize,
+    buf:        [[u8; 256 * 392]; 4], // layout is luma with stride 256 and at 256*256 chroma lines with two components starting at positions 0 and 128 and stride 256
+    refs:       [usize; 4],
+    ipred4x4:   [u8; 25],
+    y_ncoeffs:  [u8; NCSTRIDE * (256 / 4 + 1)],
+    c_ncoeffs:  [u8; NCSTRIDE * (256 / 8 + 1)],
+    mvs:        [MV; 16 * 16],
+    pred_mv:    MV,
+    cur_mv:     MV,
+    qmat:       [i32; 8],
+    codebooks:  Codebooks,
+}
+
+impl VXVideoDecoder {
+    fn new() -> Self {
+        Self {
+            info:       NACodecInfoRef::default(),
+            buf:        [[0x80; 256 * 392]; 4],
+            width:      0,
+            height:     0,
+            refs:       [0, 1, 2, 3],
+            ipred4x4:   [9; 25],
+            y_ncoeffs:  [0; NCSTRIDE * (256 / 4 + 1)],
+            c_ncoeffs:  [0; NCSTRIDE * (256 / 8 + 1)],
+            mvs:        [MV::default(); 16 * 16],
+            cur_mv:     MV::default(),
+            pred_mv:    MV::default(),
+            qmat:       [0; 8],
+            codebooks:  Codebooks::new(),
+        }
+    }
+    fn update_refs(&mut self) {
+        for el in self.refs.iter_mut() {
+            *el = (*el + 3) & 3;
+        }
+    }
+    fn decode_frame(&mut self, br: &mut BitReader) -> DecoderResult<()> {
+        let mut mv_pos = 18;
+        for ypos in (0..self.height).step_by(16) {
+            for xpos in (0..self.width).step_by(16) {
+                let left_mv = self.mvs[mv_pos - 1];
+                let top_mv  = self.mvs[mv_pos - 17];
+                let tl_mv   = self.mvs[mv_pos - 18];
+                self.pred_mv = MV::pred(top_mv, tl_mv, left_mv);
+                self.cur_mv  = MV::default();
+                self.decode_block(br, xpos, ypos, 16, 16)?;
+                self.mvs[mv_pos] = self.cur_mv;
+                mv_pos += 1;
+            }
+            mv_pos -= self.width / 16;
+            mv_pos += 18;
+        }
+        Ok(())
+    }
+    fn copy_block(&mut self, xpos: usize, ypos: usize, w: usize, h: usize, ref_buf: usize) -> DecoderResult<()> {
+        let src = self.refs[ref_buf];
+        let dst = self.refs[CUR_BUF];
+        self.cur_mv = self.pred_mv;
+        let mx = self.pred_mv.x as isize;
+        let my = self.pred_mv.y as isize;
+        mc!(self.buf, src, dst, mx, my, xpos, ypos, w, h, 0, 0, 0, self.width, self.height);
+        Ok(())
+    }
+    fn do_mc(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize, ref_buf: usize) -> DecoderResult<()> {
+        let src = self.refs[ref_buf];
+        let dst = self.refs[CUR_BUF];
+        let dx                          = br.read_gammap_s()? as i8;
+        let dy                          = br.read_gammap_s()? as i8;
+        self.cur_mv = self.pred_mv + MV { x: dx, y: dy };
+        let mx = self.cur_mv.x as isize;
+        let my = self.cur_mv.y as isize;
+        mc!(self.buf, src, dst, mx, my, xpos, ypos, w, h, 0, 0, 0, self.width, self.height);
+        
+        Ok(())
+    }
+    fn do_mc_bias(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize) -> DecoderResult<()> {
+        let src = self.refs[0];
+        let dst = self.refs[CUR_BUF];
+        let mx                          = br.read_gammap_s()? as isize;
+        let my                          = br.read_gammap_s()? as isize;
+        let ydelta                      = br.read_gammap_s()? * 2;
+        let udelta                      = br.read_gammap_s()? * 2;
+        let vdelta                      = br.read_gammap_s()? * 2;
+        mc!(self.buf, src, dst, mx, my, xpos, ypos, w, h, ydelta, udelta, vdelta, self.width, self.height);
+        
+        Ok(())
+    }
+    fn pred_plane(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize) -> DecoderResult<()> {
+        let ydelta                      = br.read_gammap_s()? * 2;
+        let udelta                      = br.read_gammap_s()? * 2;
+        let vdelta                      = br.read_gammap_s()? * 2;
+        let yoff = xpos + ypos * 256;
+        let coff = CHROMA_OFF + xpos / 2 + ypos / 2 * 256;
+        pred_plane_delta(&mut self.buf[self.refs[CUR_BUF]], yoff,       w,     h,     ydelta);
+        pred_plane_delta(&mut self.buf[self.refs[CUR_BUF]], coff,       w / 2, h / 2, udelta);
+        pred_plane_delta(&mut self.buf[self.refs[CUR_BUF]], coff + 128, w / 2, h / 2, vdelta);
+        Ok(())
+    }
+    fn intra_pred(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize) -> DecoderResult<()> {
+        let ymode                       = br.read_gammap()? as usize;
+        let cmode                       = br.read_gammap()? as usize;
+        let yoff = xpos + ypos * 256;
+        let coff = CHROMA_OFF + xpos / 2 + ypos / 2 * 256;
+        let blk = &mut self.buf[self.refs[CUR_BUF]];
+        match ymode {
+            0 => pred_ver(blk, yoff, w, h),
+            1 => pred_hor(blk, yoff, w, h),
+            2 => pred_dc (blk, yoff, xpos, ypos, w, h),
+            3 => pred_plane_delta(blk, yoff, w, h, 0),
+            _ => return Err(DecoderError::InvalidData),
+        };
+        match cmode {
+            0 => {
+                pred_dc(blk, coff,       xpos / 2, ypos / 2, w / 2, h / 2);
+                pred_dc(blk, coff + 128, xpos / 2, ypos / 2, w / 2, h / 2);
+            },
+            1 => {
+                pred_hor(blk, coff,       w / 2, h / 2);
+                pred_hor(blk, coff + 128, w / 2, h / 2);
+            },
+            2 => {
+                pred_ver(blk, coff,       w / 2, h / 2);
+                pred_ver(blk, coff + 128, w / 2, h / 2);
+            },
+            3 => {
+                pred_plane_delta(blk, coff,       w / 2, h / 2, 0);
+                pred_plane_delta(blk, coff + 128, w / 2, h / 2, 0);
+            },
+            _ => return Err(DecoderError::InvalidData),
+        };
+        Ok(())
+    }
+    fn intra_pred4x4(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize) -> DecoderResult<()> {
+        let mut yoff = xpos + ypos * 256;
+        let mut idx = 6;
+        let blk = &mut self.buf[self.refs[CUR_BUF]];
+        for y in (0..h).step_by(4) {
+            for x in (0..w).step_by(4) {
+                let mut mode = self.ipred4x4[idx - 5].min(self.ipred4x4[idx - 1]);
+                if mode == 9 {
+                    mode = 2;
+                }
+                if !br.read_bool()? {
+                    let mode1           = br.read(3)? as u8;
+                    mode = if mode1 >= mode { mode1 + 1 } else { mode1 };
+                }
+                match mode {
+                    0 => pred_ver(blk, yoff + x, 4, 4),
+                    1 => pred_hor(blk, yoff + x, 4, 4),
+                    2 => pred_dc4x4(blk, yoff + x, xpos + x, ypos + y),
+                    3 => pred_ddown_left (blk, yoff + x),
+                    4 => pred_ddown_right(blk, yoff + x),
+                    5 => pred_ver_right  (blk, yoff + x),
+                    6 => pred_hor_down   (blk, yoff + x),
+                    7 => pred_ver_left   (blk, yoff + x),
+                    8 => pred_hor_up     (blk, yoff + x),
+                    _ => unreachable!(),
+                };
+                self.ipred4x4[idx] = mode;
+                idx += 1;
+            }
+            yoff += 256 * 4;
+            idx -= w / 4;
+            idx += 5;
+        }
+        let cmode                       = br.read_gammap()? as usize;
+        let coff = CHROMA_OFF + xpos / 2 + ypos / 2 * 256;
+        match cmode {
+            0 => {
+                pred_dc(blk, coff,       xpos / 2, ypos / 2, w / 2, h / 2);
+                pred_dc(blk, coff + 128, xpos / 2, ypos / 2, w / 2, h / 2);
+            },
+            1 => {
+                pred_hor(blk, coff,       w / 2, h / 2);
+                pred_hor(blk, coff + 128, w / 2, h / 2);
+            },
+            2 => {
+                pred_ver(blk, coff,       w / 2, h / 2);
+                pred_ver(blk, coff + 128, w / 2, h / 2);
+            },
+            3 => {
+                pred_plane_delta(blk, coff,       w / 2, h / 2, 0);
+                pred_plane_delta(blk, coff + 128, w / 2, h / 2, 0);
+            },
+            _ => return Err(DecoderError::InvalidData),
+        };
+        Ok(())
+    }
+    fn decode_residue(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize) -> DecoderResult<()> {
+        const CBP: [u8; 32] = [
+            0x00, 0x08, 0x04, 0x02, 0x01, 0x1F, 0x0F, 0x0A,
+            0x05, 0x0C, 0x03, 0x10, 0x0E, 0x0D, 0x0B, 0x07,
+            0x09, 0x06, 0x1E, 0x1B, 0x1A, 0x1D, 0x17, 0x15,
+            0x18, 0x12, 0x11, 0x1C, 0x14, 0x13, 0x16, 0x19
+        ];
+
+        let mut yoff = xpos + ypos * 256;
+        let mut coff = CHROMA_OFF + xpos / 2 + ypos / 2 * 256;
+        let blk = &mut self.buf[self.refs[CUR_BUF]];
+        let mut yidx = (xpos / 4 + 1) + NCSTRIDE * (ypos / 4 + 1);
+        let mut cidx = (xpos / 8 + 1) + NCSTRIDE * (ypos / 8 + 1);
+        for _y in (0..h).step_by(8) {
+            for x in (0..w).step_by(8) {
+                let idx                 = br.read_gammap()? as usize;
+                validate!(idx < CBP.len());
+                let cbp = CBP[idx];
+                for bno in 0..4 {
+                    let cur_yidx = yidx + x / 4 + (bno & 1) + (bno / 2) * NCSTRIDE;
+                    if (cbp & (1 << bno)) != 0 {
+                        let ctx = avg(self.y_ncoeffs[cur_yidx - 1], self.y_ncoeffs[cur_yidx - NCSTRIDE]);
+                        self.y_ncoeffs[cur_yidx] = decode_coeffs(br, &self.codebooks, &self.qmat, ctx, &mut blk[yoff + x + (bno & 1) * 4 + (bno / 2) * 4 * 256..])?;
+                    } else {
+                        self.y_ncoeffs[cur_yidx] = 0;
+                    }
+                }
+                if (cbp & 0x10) != 0 {
+                    let ctx = avg(self.c_ncoeffs[cidx + x / 8 - 1], self.c_ncoeffs[cidx + x / 8 - NCSTRIDE]);
+                    let unc = decode_coeffs(br, &self.codebooks, &self.qmat, ctx, &mut blk[coff + x / 2..])?;
+                    let vnc = decode_coeffs(br, &self.codebooks, &self.qmat, ctx, &mut blk[coff + 128 + x / 2..])?;
+                    self.c_ncoeffs[cidx + x / 8] = avg(unc, vnc);
+                } else {
+                    self.c_ncoeffs[cidx + x / 8] = 0;
+                }
+            }
+            yidx += NCSTRIDE * 2;
+            cidx += NCSTRIDE;
+            yoff += 8 * 256;
+            coff += 4 * 256;
+        }
+        Ok(())
+    }
+    fn decode_block(&mut self, br: &mut BitReader, xpos: usize, ypos: usize, w: usize, h: usize) -> DecoderResult<()> {
+        let mode                        = br.read_gammap()?;
+        let min_dim = w.min(h);
+        let large_block = min_dim >= 8;
+        if mode >= 16 && !large_block {
+            return Err(DecoderError::InvalidData);
+        }
+        match mode {
+            0 if w > 2 => {
+                let hw = w / 2;
+                self.decode_block(br, xpos,      ypos, hw, h)?;
+                self.decode_block(br, xpos + hw, ypos, hw, h)?;
+            },
+            1 => { self.copy_block(xpos, ypos, w, h, 0)?; },
+            2 if h > 2 => {
+                let hh = h / 2;
+                self.decode_block(br, xpos, ypos,      w, hh)?;
+                self.decode_block(br, xpos, ypos + hh, w, hh)?;
+            },
+            3 => { self.do_mc_bias(br, xpos, ypos, w, h)?; },
+            4 | 5 | 6 => {
+                let ref_id = (mode - 4) as usize;
+                self.do_mc(br, xpos, ypos, w, h, ref_id)?;
+            },
+            7 => { self.pred_plane(br, xpos, ypos, w, h)?; },
+            8 if large_block => {
+                let hw = w / 2;
+                self.decode_block(br, xpos,      ypos, hw, h)?;
+                self.decode_block(br, xpos + hw, ypos, hw, h)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            9 => { self.copy_block(xpos, ypos, w, h, 1)?; },
+            10 if large_block => {
+                self.do_mc_bias(br, xpos, ypos, w, h)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            11 => {
+                if min_dim >= 4 {
+                    self.intra_pred(br, xpos, ypos, w, h)?;
+                }
+            },
+            12 if large_block => {
+                self.copy_block(xpos, ypos, w, h, 0)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            13 if large_block => {
+                let hh = h / 2;
+                self.decode_block(br, xpos, ypos,      w, hh)?;
+                self.decode_block(br, xpos, ypos + hh, w, hh)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            14 => { self.copy_block(xpos, ypos, w, h, 2)?; },
+            15 => {
+                if min_dim >= 4 {
+                    self.intra_pred4x4(br, xpos, ypos, w, h)?;
+                }
+            },
+            16 | 17 | 18 => {
+                let ref_id = (mode - 16) as usize;
+                self.do_mc(br, xpos, ypos, w, h, ref_id)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            19 => {
+                self.intra_pred4x4(br, xpos, ypos, w, h)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            20 => {
+                self.copy_block(xpos, ypos, w, h, 1)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            21 => {
+                self.copy_block(xpos, ypos, w, h, 2)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            22 => {
+                self.intra_pred(br, xpos, ypos, w, h)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            23 => {
+                self.pred_plane(br, xpos, ypos, w, h)?;
+                self.decode_residue(br, xpos, ypos, w, h)?;
+            },
+            _ => return Err(DecoderError::InvalidData),
+        };
+        Ok(())
+    }
+}
+
+const QUANTISERS: [u8; 52] = [
+    10, 13, 10, 13, 13, 16, 13, 16, 11, 14, 11, 14, 14, 18, 14, 18,
+    13, 16, 13, 16, 16, 20, 16, 20, 14, 18, 14, 18, 18, 23, 18, 23,
+    16, 20, 16, 20, 20, 25, 20, 25, 18, 23, 18, 23, 23, 29, 23, 29,
+    20, 14, 12, 10
+];
+
+impl NADecoder for VXVideoDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            self.width  = vinfo.get_width();
+            self.height = vinfo.get_height();
+            validate!(self.width <= 256 && self.height <= 256);
+
+            let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, false, YUV420_FORMAT));
+            self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+
+            if let Some(edata) = info.get_extradata() {
+                validate!(edata.len() > 0);
+                let fps = edata[0] as usize;
+                validate!(fps <= 60);
+                let base = &QUANTISERS[(fps % 6) * 8..][..8];
+                let scale = fps / 6;
+                for (dq, iq) in self.qmat.iter_mut().zip(base.iter()) {
+                    *dq = i32::from(*iq) << scale;
+                }
+            } else {
+                return Err(DecoderError::InvalidData);
+            }
+
+            for frm in self.buf.iter_mut() {
+                let (ybuf, cbuf) = frm.split_at_mut(CHROMA_OFF);
+                for el in ybuf.iter_mut() {
+                    *el = 0;
+                }
+                for el in cbuf.iter_mut() {
+                    *el = 0x80;
+                }
+            }
+
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+        validate!(src.len() > 0);
+
+        let mut br = BitReader::new(&src[0..], BitReaderMode::LE16MSB);
+
+        self.y_ncoeffs = [0; NCSTRIDE * (256 / 4 + 1)];
+        self.c_ncoeffs = [0; NCSTRIDE * (256 / 8 + 1)];
+        self.mvs = [MV::default(); 16 * 16];
+        self.decode_frame(&mut br)?;
+
+        let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 4)?;
+        let mut buf = bufinfo.get_vbuf().unwrap();
+        let ystride = buf.get_stride(0);
+        let ustride = buf.get_stride(1);
+        let vstride = buf.get_stride(2);
+        let yoff = buf.get_offset(0);
+        let uoff = buf.get_offset(1);
+        let voff = buf.get_offset(2);
+        let data = buf.get_data_mut().unwrap();
+        let cur = self.refs[CUR_BUF];
+        for (sline, dline) in self.buf[cur][0..].chunks(256).take(self.height).zip(data[yoff..].chunks_mut(ystride)) {
+            dline[..self.width].copy_from_slice(&sline[..self.width]);
+        }
+        for (sline, dline) in self.buf[cur][CHROMA_OFF..].chunks(256).take(self.height / 2).zip(data[uoff..].chunks_mut(ustride)) {
+            dline[..self.width / 2].copy_from_slice(&sline[..self.width / 2]);
+        }
+        for (sline, dline) in self.buf[cur][CHROMA_OFF + 128..].chunks(256).take(self.height / 2).zip(data[voff..].chunks_mut(vstride)) {
+            dline[..self.width / 2].copy_from_slice(&sline[..self.width / 2]);
+        }
+        let videobuf = NABufferType::Video(buf);
+        self.update_refs();
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), videobuf);
+        let is_intra = pkt.get_pts() == Some(0);
+        frm.set_keyframe(is_intra);
+        frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P });
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {
+    }
+}
+
+impl NAOptionHandler for VXVideoDecoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+
+pub fn get_decoder_video() -> Box<dyn NADecoder + Send> {
+    Box::new(VXVideoDecoder::new())
+}
+
+
+struct AudioState {
+    lpc0_idx:   usize,
+    lpc1_idx:   usize,
+    lpc2_idx:   usize,
+    scale:      i32,
+    frame_mode: usize,
+    cur_filt:   [i32; 8],
+
+    lpc0_cb:    [[i16; 8]; 64],
+    lpc1_cb:    [[i16; 8]; 64],
+    lpc2_cb:    [[i16; 8]; 64],
+    decays:     [i32; 8],
+    base_filt:  [i32; 8],
+    base_scale: i32,
+}
+
+impl AudioState {
+    fn new() -> Self {
+        Self {
+            lpc0_idx:   0,
+            lpc1_idx:   0,
+            lpc2_idx:   0,
+            scale:      0,
+            frame_mode: 0,
+            cur_filt:   [0; 8],
+
+            lpc0_cb:    [[0; 8]; 64],
+            lpc1_cb:    [[0; 8]; 64],
+            lpc2_cb:    [[0; 8]; 64],
+            decays:     [0; 8],
+            base_filt:  [0; 8],
+            base_scale: 0,
+        }
+    }
+    fn read_initial_params(&mut self, br: &mut ByteReader) -> DecoderResult<()> {
+        for entry in self.lpc0_cb.iter_mut() {
+            for el in entry.iter_mut() {
+                *el                     = br.read_u16le()? as i16;
+            }
+        }
+        for entry in self.lpc1_cb.iter_mut() {
+            for el in entry.iter_mut() {
+                *el                     = br.read_u16le()? as i16;
+            }
+        }
+        for entry in self.lpc2_cb.iter_mut() {
+            for el in entry.iter_mut() {
+                *el                     = br.read_u16le()? as i16;
+            }
+        }
+        for el in self.decays.iter_mut() {
+            *el                         = i32::from(br.read_u16le()? as i16);
+        }
+        for el in self.base_filt.iter_mut() {
+            *el                         = br.read_u32le()? as i32;
+        }
+        self.base_scale                 = br.read_u32le()? as i32;
+        Ok(())
+    }
+    fn unpack_data(&mut self, br: &mut ByteReader, val: u16, dst: &mut [i32]) -> DecoderResult<()> {
+        self.lpc0_idx = (val & 0x3F) as usize;
+        self.scale = (self.decays[((val >> 6) & 7) as usize] * self.scale) >> 13;
+        let val1                        = br.read_u16le()?;
+        self.lpc1_idx = ((val1 >> 6) & 0x3F) as usize;
+        self.lpc2_idx = (val1 & 0x3F) as usize;
+        self.frame_mode = ((val1 >> 12) & 3) as usize;
+        let mut idx = (val1 >> 14) as usize;
+        if self.frame_mode == 0 {
+            let mut tail = 0;
+            for _ in 0..8 {
+                let val                 = br.read_u16le()?;
+                for i in 0..5 {
+                    let add = i32::from((val >> (13 - i * 3)) & 7);
+                    dst[idx] += self.scale * (add * 2 - 7);
+                    idx += 3;
+                }
+                tail = tail * 2 + (val & 1);
+            }
+            let add = i32::from((tail >> 5) & 7);
+            dst[idx] += self.scale * (add * 2 - 7);
+            idx += 3;
+            let add = i32::from((tail >> 2) & 7);
+            dst[idx] += self.scale * (add * 2 - 7);
+        } else {
+            let (len, step) = match self.frame_mode {
+                    1 => (5, 3),
+                    2 => (4, 4),
+                    3 => (3, 5),
+                    _ => unreachable!(),
+                };
+            idx += 128;
+            for _ in 0..len {
+                let val                 = br.read_u16le()?;
+                for i in 0..4 {
+                    let add = i32::from((val >> (14 - i * 2)) & 3);
+                    dst[idx] += self.scale * (add * 2 - 3);
+                    idx += step;
+                }
+            }
+        }
+        Ok(())
+    }
+    fn update_intra(&mut self) {
+        self.cur_filt = self.base_filt;
+        for i in 0..8 {
+            self.cur_filt[i] += i32::from(self.lpc0_cb[self.lpc0_idx][i]);
+            self.cur_filt[i] += i32::from(self.lpc1_cb[self.lpc1_idx][i]);
+            self.cur_filt[i] += i32::from(self.lpc2_cb[self.lpc2_idx][i]);
+        }
+    }
+    fn update_inter(&mut self) {
+        for i in 0..8 {
+            self.cur_filt[i] += i32::from(self.lpc0_cb[self.lpc0_idx][i]);
+            self.cur_filt[i] += i32::from(self.lpc1_cb[self.lpc1_idx][i]);
+            self.cur_filt[i] += i32::from(self.lpc2_cb[self.lpc2_idx][i]);
+        }
+    }
+}
+
+fn apply_lpc(dst: &mut [i32], src: &[i32], hist: &mut [i32], filt: &[i32; 8]) {
+    let mut hidx = 0;
+    for (out, src) in dst.iter_mut().zip(src.iter()) {
+        let mut sum = *src << 14;
+        for i in 0..8 {
+            sum += hist[(hidx + i) & 7] * filt[i];
+        }
+        let samp = sum >> 14;
+        *out = samp;
+        hist[hidx & 7] = samp;
+        hidx += 1;
+    }
+}
+
+struct VXAudioDecoder {
+    ainfo:      NAAudioInfo,
+    info:       Arc<NACodecInfo>,
+    chmap:      NAChannelMap,
+    buf:        [i32; 256 * 2],
+    flip_buf:   bool,
+    state:      AudioState,
+    lpc_hist:   [i32; 8],
+    lpc_filt:   [i32; 8],
+    lpc_filt1:  [i32; 8],
+}
+
+impl VXAudioDecoder {
+    fn new() -> Self {
+        Self {
+            ainfo:      NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0),
+            info:       NACodecInfo::new_dummy(),
+            chmap:      NAChannelMap::new(),
+            buf:        [0; 256 * 2],
+            flip_buf:   true,
+            state:      AudioState::new(),
+            lpc_hist:   [0; 8],
+            lpc_filt:   [0; 8],
+            lpc_filt1:  [0; 8],
+        }
+    }
+    fn decode_inter(&mut self, br: &mut ByteReader, mode: u16, val: u16) -> DecoderResult<()> {
+        let (mut cur_buf, mut prev_buf) = self.buf.split_at_mut(256);
+        if self.flip_buf {
+            std::mem::swap(&mut cur_buf, &mut prev_buf);
+        }
+        cur_buf[0..128].copy_from_slice(&prev_buf[128..]);
+        if mode == 0x7E {
+            for el in cur_buf[128..].iter_mut() {
+                *el = 0;
+            }
+        } else {
+            let src = &prev_buf[127 - (mode as usize)..];
+            let dst = &mut cur_buf[128..];
+            for i in 0..7 {
+                dst[i] = (src[i] * ((i + 1) as i32)) >> 4;
+            }
+            for i in 7..121 {
+                dst[i] = src[i] >> 1;
+            }
+            for i in 121..128 {
+                dst[i] = (src[i] * ((128 - i) as i32)) >> 4;
+            }
+        }
+
+        self.state.unpack_data(br, val, prev_buf )?;
+        self.state.update_inter();
+
+        let (cfilt, pfilt) = if !self.flip_buf {
+                (&mut self.lpc_filt, &mut self.lpc_filt1)
+            } else {
+                (&mut self.lpc_filt1, &mut self.lpc_filt)
+            };
+        *cfilt = self.state.cur_filt;
+        let mut f0 = [0; 8];
+        let mut f1 = [0; 8];
+        let mut f2 = [0; 8];
+        for i in 0..8 {
+            f1[i] = (pfilt[i] + cfilt[i]) >> 1;
+            f0[i] = (pfilt[i] + f1   [i]) >> 1;
+            f2[i] = (f1   [i] + cfilt[i]) >> 1;
+        }
+        apply_lpc(&mut cur_buf[ 0..][..32], &prev_buf[128..],      &mut self.lpc_hist,   &f0);
+        apply_lpc(&mut cur_buf[32..][..32], &prev_buf[128 + 32..], &mut self.lpc_hist, &f1);
+        apply_lpc(&mut cur_buf[64..][..32], &prev_buf[128 + 64..], &mut self.lpc_hist, &f2);
+        apply_lpc(&mut cur_buf[96..][..32], &prev_buf[128 + 96..], &mut self.lpc_hist, &cfilt);
+        Ok(())
+    }
+    fn decode_intra(&mut self, br: &mut ByteReader, val: u16) -> DecoderResult<()> {
+        self.state.scale = self.state.base_scale;
+        self.lpc_hist = [0; 8];
+        self.flip_buf = true;
+
+        let (mut cur_buf, mut prev_buf) = self.buf.split_at_mut(256);
+        if self.flip_buf {
+            std::mem::swap(&mut cur_buf, &mut prev_buf);
+        }
+        for el in cur_buf[128..].iter_mut() {
+            *el = 0;
+        }
+        self.state.unpack_data(br, val, prev_buf)?;
+        self.state.update_intra();
+
+        self.lpc_filt = self.state.cur_filt;
+        apply_lpc(&mut cur_buf[..128], &prev_buf[128..], &mut self.lpc_hist, &self.lpc_filt);
+        Ok(())
+    }
+    fn output(&mut self, dst: &mut [i16]) {
+        let src = if self.flip_buf { &self.buf[256..][..128] } else { &self.buf[..128] };
+        for (src, dst) in src.iter().zip(dst.iter_mut()) {
+            *dst = (*src).max(-0x8000).min(0x7FFF) as i16;
+        }
+        self.flip_buf = !self.flip_buf;
+    }
+}
+
+impl NADecoder for VXAudioDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
+            if let Some(edata) = info.get_extradata() {
+                validate!(edata.len() >= 3124);
+                let mut mr = MemoryReader::new_read(edata.as_slice());
+                let mut br = ByteReader::new(&mut mr);
+                self.state.read_initial_params(&mut br)?;
+            } else {
+                return Err(DecoderError::InvalidData);
+            }
+            self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), 1, SND_S16_FORMAT, 1);
+            self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo));
+            self.chmap = NAChannelMap::from_str("C").unwrap();
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        const SUBFRAME_LEN: [usize; 4] = [20, 14, 12, 10];
+
+        let info = pkt.get_stream().get_info();
+        if let NACodecTypeInfo::Audio(_) = info.get_properties() {
+            let pktbuf = pkt.get_buffer();
+            validate!(pktbuf.len() >= 10);
+
+            let mut mr = MemoryReader::new_read(&pktbuf);
+            let mut br = ByteReader::new(&mut mr);
+            let mut nblocks = 0;
+            while br.left() > 4 {
+                                          br.read_skip(2)?;
+                let val                 = br.read_u16le()?;
+                nblocks += 1;
+                let sf_len = SUBFRAME_LEN[((val >> 12) & 3) as usize];
+                if br.left() <= sf_len as i64 {
+                    break;
+                }
+                                          br.read_skip(sf_len - 4)?;
+            }
+
+            let samples = 128 * nblocks;
+            let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?;
+            let mut adata = abuf.get_abuf_i16().unwrap();
+            let dst = adata.get_data_mut().unwrap();
+
+            let mut mr = MemoryReader::new_read(&pktbuf);
+            let mut br = ByteReader::new(&mut mr);
+
+            let mut blk_no = 0usize;
+            while br.left() > 0 {
+                let val                 = br.read_u16le()?;
+                let mode = val >> 9;
+                if mode == 0x7F {
+                    self.decode_intra(&mut br, val)?;
+                } else {
+                    self.decode_inter(&mut br, val & 0x1FF, mode)?;
+                }
+                self.output(&mut dst[blk_no * 128..]);
+                blk_no += 1;
+            }
+
+            let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), abuf);
+            frm.set_duration(Some(samples as u64));
+            frm.set_keyframe(true);
+            Ok(frm.into_ref())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn flush(&mut self) {
+    }
+}
+
+
+impl NAOptionHandler for VXAudioDecoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+
+pub fn get_decoder_audio() -> Box<dyn NADecoder + Send> {
+    Box::new(VXAudioDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::game_register_all_decoders;
+    use crate::game_register_all_demuxers;
+    #[test]
+    fn test_vx_video() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        game_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        game_register_all_decoders(&mut dec_reg);
+
+        //XXX: the current version does not reconstruct videos perfectly yet
+        test_decoding("vx", "vxvideo", "assets/Game/bioware.vx", Some(31), &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5Frames(vec![
+                                [0x33de14fa, 0x00948eb7, 0x028141d7, 0x1d07abd6],
+                                [0x07442eef, 0x36ec00c1, 0x3d9556c6, 0x6ddbcd46],
+                                [0xb5b48d03, 0x0063faaf, 0xfa64de6f, 0xbe5d916b],
+                                [0xb760b777, 0xe7159f5e, 0x352b7326, 0xd21e3d14],
+                                [0xee1ccadc, 0xd2b54891, 0xb5f6f31a, 0x73d7e869],
+                                [0xcd234bd9, 0xad3d8f3e, 0xdb405b8c, 0x4faa738b],
+                                [0x615957be, 0x8a20b367, 0xadc031af, 0x886311cc],
+                                [0xade60b19, 0x2560fffa, 0x8bafff67, 0x698485ad],
+                                [0x131b874c, 0x4bb41da5, 0xc0f52bf2, 0x626d6e66],
+                                [0x1dc1bb4b, 0xc3552467, 0xa5598162, 0xe6220dde],
+                                [0xa1e27935, 0xd01c18c4, 0x6e6e1be7, 0xc42b021c],
+                                [0x28876ccb, 0xfaca435f, 0xe8cf81d7, 0x8f103d89],
+                                [0xab610d4d, 0x0bf5b213, 0x28749eda, 0xcbf5c34c],
+                                [0x48a452f8, 0x544b8a4d, 0x0a031be4, 0xe60b6f59],
+                                [0xe6f88898, 0x46171e3b, 0xf39e2605, 0x3d16e75a],
+                                [0xe1461669, 0x3dd4daa6, 0x1e82b9e7, 0x49214174],
+                                [0x39d89d57, 0x5c0bf9ba, 0xf254cdc4, 0x8c1b9bb9],
+                                [0x81f1a684, 0xd2a0bc8a, 0xe1355afa, 0xbe33e476],
+                                [0x571b0722, 0x25643452, 0x509ed72d, 0x1d55214e],
+                                [0xc799fa72, 0xed2bc13d, 0xa9be1b55, 0xece4d7f0],
+                                [0x0eae7358, 0x87d80481, 0xcac84bf9, 0x76bee392],
+                                [0xf0e6fb26, 0x95a9a362, 0x474edaa2, 0xd8d0b5fc],
+                                [0xee57bff2, 0x490b9ffc, 0x6189e2d4, 0xb1b97277],
+                                [0xa5f83723, 0xaa807527, 0x5c722b29, 0x3ea10cfc],
+                                [0x83562752, 0x27021714, 0x5b81f808, 0x192fa17c],
+                                [0x50f5ab82, 0x01a482b2, 0xa44b7525, 0xac4d1916],
+                                [0x8cf1fffd, 0xf09364c5, 0x57847078, 0x1a811a61],
+                                [0x3d8e4221, 0xe69532b1, 0x7bd4fa20, 0x78dc0676],
+                                [0x7b6432a5, 0x01ee9819, 0xfd0fd634, 0x96fd612b],
+                                [0xb0e0d469, 0xc651dd3a, 0xf2fa8e9b, 0xc58d55be],
+                                [0xa9d7fa17, 0xaf2d05f8, 0xd307d0c1, 0xd83dc57a],
+                                [0x14e644a7, 0xd8d9e459, 0x0d81e68c, 0xe57b9b81]]));
+    }
+}
+
+const NC_MAP: [usize; 8] = [ 0, 0, 1, 1, 2, 2, 2, 2 ];
+const NC_BITS: [[u16; 68]; 3] = [
+  [
+    0x0001, 0x0000, 0x0000, 0x0000, 0x0005, 0x0001, 0x0000, 0x0000,
+    0x0007, 0x0004, 0x0001, 0x0000, 0x0007, 0x0006, 0x0005, 0x0003,
+    0x0007, 0x0006, 0x0005, 0x0003, 0x0007, 0x0006, 0x0005, 0x0004,
+    0x000F, 0x0006, 0x0005, 0x0004, 0x000B, 0x000E, 0x0005, 0x0004,
+    0x0008, 0x000A, 0x000D, 0x0004, 0x000F, 0x000E, 0x0009, 0x0004,
+    0x000B, 0x000A, 0x000D, 0x000C, 0x000F, 0x000E, 0x0009, 0x000C,
+    0x000B, 0x000A, 0x000D, 0x0008, 0x000F, 0x0001, 0x0009, 0x000C,
+    0x000B, 0x000E, 0x000D, 0x0008, 0x0007, 0x000A, 0x0009, 0x000C,
+    0x0004, 0x0006, 0x0005, 0x0008
+  ], [
+    0x0003, 0x0000, 0x0000, 0x0000, 0x000B, 0x0002, 0x0000, 0x0000,
+    0x0007, 0x0007, 0x0003, 0x0000, 0x0007, 0x000A, 0x0009, 0x0005,
+    0x0007, 0x0006, 0x0005, 0x0004, 0x0004, 0x0006, 0x0005, 0x0006,
+    0x0007, 0x0006, 0x0005, 0x0008, 0x000F, 0x0006, 0x0005, 0x0004,
+    0x000B, 0x000E, 0x000D, 0x0004, 0x000F, 0x000A, 0x0009, 0x0004,
+    0x000B, 0x000E, 0x000D, 0x000C, 0x0008, 0x000A, 0x0009, 0x0008,
+    0x000F, 0x000E, 0x000D, 0x000C, 0x000B, 0x000A, 0x0009, 0x000C,
+    0x0007, 0x000B, 0x0006, 0x0008, 0x0009, 0x0008, 0x000A, 0x0001,
+    0x0007, 0x0006, 0x0005, 0x0004
+  ], [
+    0x000F, 0x0000, 0x0000, 0x0000, 0x000F, 0x000E, 0x0000, 0x0000,
+    0x000B, 0x000F, 0x000D, 0x0000, 0x0008, 0x000C, 0x000E, 0x000C,
+    0x000F, 0x000A, 0x000B, 0x000B, 0x000B, 0x0008, 0x0009, 0x000A,
+    0x0009, 0x000E, 0x000D, 0x0009, 0x0008, 0x000A, 0x0009, 0x0008,
+    0x000F, 0x000E, 0x000D, 0x000D, 0x000B, 0x000E, 0x000A, 0x000C,
+    0x000F, 0x000A, 0x000D, 0x000C, 0x000B, 0x000E, 0x0009, 0x000C,
+    0x0008, 0x000A, 0x000D, 0x0008, 0x000D, 0x0007, 0x0009, 0x000C,
+    0x0009, 0x000C, 0x000B, 0x000A, 0x0005, 0x0008, 0x0007, 0x0006,
+    0x0001, 0x0004, 0x0003, 0x0002
+  ]
+];
+const NC_LENS: [[u8; 68]; 3] = [
+  [
+     1,  0,  0,  0,  6,  2,  0,  0,
+     8,  6,  3,  0,  9,  8,  7,  5,
+    10,  9,  8,  6, 11, 10,  9,  7,
+    13, 11, 10,  8, 13, 13, 11,  9,
+    13, 13, 13, 10, 14, 14, 13, 11,
+    14, 14, 14, 13, 15, 15, 14, 14,
+    15, 15, 15, 14, 16, 15, 15, 15,
+    16, 16, 16, 15, 16, 16, 16, 16,
+    16, 16, 16, 16
+  ], [
+     2,  0,  0,  0,  6,  2,  0,  0,
+     6,  5,  3,  0,  7,  6,  6,  4,
+     8,  6,  6,  4,  8,  7,  7,  5,
+     9,  8,  8,  6, 11,  9,  9,  6,
+    11, 11, 11,  7, 12, 11, 11,  9,
+    12, 12, 12, 11, 12, 12, 12, 11,
+    13, 13, 13, 12, 13, 13, 13, 13,
+    13, 14, 13, 13, 14, 14, 14, 13,
+    14, 14, 14, 14
+  ], [
+     4,  0,  0,  0,  6,  4,  0,  0,
+     6,  5,  4,  0,  6,  5,  5,  4,
+     7,  5,  5,  4,  7,  5,  5,  4,
+     7,  6,  6,  4,  7,  6,  6,  4,
+     8,  7,  7,  5,  8,  8,  7,  6,
+     9,  8,  8,  7,  9,  9,  8,  8,
+     9,  9,  9,  8, 10,  9,  9,  9,
+    10, 10, 10, 10, 10, 10, 10, 10,
+    10, 10, 10, 10
+  ]
+];
+
+const NUM_ZERO_BITS: [[u8; 16]; 15] = [
+  [
+    0x01, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01
+  ], [
+    0x07, 0x06, 0x05, 0x04, 0x03, 0x05, 0x04, 0x03, 0x02, 0x03, 0x02, 0x03, 0x02, 0x01, 0x00, 0x00
+  ], [
+    0x05, 0x07, 0x06, 0x05, 0x04, 0x03, 0x04, 0x03, 0x02, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00
+  ], [
+    0x03, 0x07, 0x05, 0x04, 0x06, 0x05, 0x04, 0x03, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x05, 0x04, 0x03, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x01, 0x01, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x01, 0x01, 0x05, 0x04, 0x03, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x01, 0x01, 0x01, 0x03, 0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x01, 0x00, 0x01, 0x03, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x01, 0x00, 0x01, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x00, 0x01, 0x01, 0x02, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ], [
+    0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+  ]
+];
+const NUM_ZERO_LENS: [[u8; 16]; 15] = [
+    [ 1,  3,  3,  4,  4,  5,  5,  6,  6,  7,  7,  8,  8,  9,  9,  9 ],
+    [ 3,  3,  3,  3,  3,  4,  4,  4,  4,  5,  5,  6,  6,  6,  6,  0 ],
+    [ 4,  3,  3,  3,  4,  4,  3,  3,  4,  5,  5,  6,  5,  6,  0,  0 ],
+    [ 5,  3,  4,  4,  3,  3,  3,  4,  3,  4,  5,  5,  5,  0,  0,  0 ],
+    [ 4,  4,  4,  3,  3,  3,  3,  3,  4,  5,  4,  5,  0,  0,  0,  0 ],
+    [ 6,  5,  3,  3,  3,  3,  3,  3,  4,  3,  6,  0,  0,  0,  0,  0 ],
+    [ 6,  5,  3,  3,  3,  2,  3,  4,  3,  6,  0,  0,  0,  0,  0,  0 ],
+    [ 6,  4,  5,  3,  2,  2,  3,  3,  6,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 6,  6,  4,  2,  2,  3,  2,  5,  0,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 5,  5,  3,  2,  2,  2,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 4,  4,  3,  3,  1,  3,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 4,  4,  2,  1,  3,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 3,  3,  1,  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 2,  2,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ],
+    [ 1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 ]
+];
+
+const ZERO_RUN_BITS: [[u8; 8]; 6] = [
+    [ 1, 0, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 1, 0, 0, 0, 0, 0, 0 ],
+    [ 3, 2, 1, 0, 0, 0, 0, 0 ],
+    [ 3, 2, 1, 1, 0, 0, 0, 0 ],
+    [ 3, 2, 3, 2, 1, 0, 0, 0 ],
+    [ 3, 0, 1, 3, 2, 5, 4, 0 ]
+];
+const ZERO_RUN_LENS: [[u8; 8]; 6] = [
+    [ 1, 1, 0, 0, 0, 0, 0, 0 ],
+    [ 1, 2, 2, 0, 0, 0, 0, 0 ],
+    [ 2, 2, 2, 2, 0, 0, 0, 0 ],
+    [ 2, 2, 2, 3, 3, 0, 0, 0 ],
+    [ 2, 2, 3, 3, 3, 3, 0, 0 ],
+    [ 2, 3, 3, 3, 3, 3, 3, 0 ]
+];
+
index 236b3cf8d229f9a16e45d44f4e7757eeed6651a5..d9eeaaa04cf1297aa4545858171fe250cdc1593f 100644 (file)
@@ -11,6 +11,8 @@ mod bmv;
 mod gdv;
 #[cfg(feature="demuxer_vmd")]
 mod vmd;
+#[cfg(feature="demuxer_vx")]
+mod vx;
 
 const GAME_DEMUXERS: &[&DemuxerCreator] = &[
 #[cfg(feature="demuxer_bmv")]
@@ -21,6 +23,8 @@ const GAME_DEMUXERS: &[&DemuxerCreator] = &[
     &gdv::GDVDemuxerCreator {},
 #[cfg(feature="demuxer_vmd")]
     &vmd::VMDDemuxerCreator {},
+#[cfg(feature="demuxer_vx")]
+    &vx::VXDemuxerCreator {},
 ];
 
 /// Registers all available demuxers provided by this crate.
diff --git a/nihav-game/src/demuxers/vx.rs b/nihav-game/src/demuxers/vx.rs
new file mode 100644 (file)
index 0000000..cfc44e0
--- /dev/null
@@ -0,0 +1,161 @@
+use nihav_core::frame::*;
+use nihav_core::demuxers::*;
+use std::io::SeekFrom;
+
+const AUDIO_EXTRADATA_LEN: usize = 3124;
+
+struct VXDemuxer<'a> {
+    src:        &'a mut ByteReader<'a>,
+    vid_id:     usize,
+    aud_id:     usize,
+    video_pos:  u64,
+    num_vid:    usize,
+    vno:        u64,
+    num_vfrm:   u64,
+    num_aud:    usize,
+    ano:        u64,
+    num_afrm:   u64,
+}
+
+impl<'a> DemuxCore<'a> for VXDemuxer<'a> {
+    #[allow(unused_variables)]
+    fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+        let src = &mut self.src;
+
+        let mut magic = [0u8; 4];
+                                          src.read_buf(&mut magic)?;
+        validate!(&magic == b"VXDS");
+        let nframes                     = src.read_u32le()? as usize;
+        let width                       = src.read_u32le()? as usize;
+        let height                      = src.read_u32le()? as usize;
+        let _unk                        = src.read_u32le()? as usize;
+        let fps                         = src.read_u32le()?;
+        validate!(fps > 0 && fps < 256);
+        let srate                       = src.read_u32le()?;
+        let num_audio_tracks            = src.read_u32le()? as usize;
+        let _max_frame_size             = src.read_u32le()? as usize;
+        let audio_off                   = src.read_u32le()? as u64;
+        validate!(audio_off == 0 || audio_off >= 0x30);
+        let vinfo_off                   = src.read_u32le()? as u64;
+        validate!(vinfo_off == 0 || vinfo_off >= 0x30);
+        let num_vstreams                = src.read_u32le()? as usize;
+        if num_vstreams != 1 || num_audio_tracks > 1 {
+            return Err(DemuxerError::NotImplemented);
+        }
+
+        let vhdr = NAVideoInfo::new(width, height, false, YUV420_FORMAT);
+        let vci = NACodecTypeInfo::Video(vhdr);
+        let edata = [fps as u8].to_vec();
+        let vinfo = NACodecInfo::new("vxvideo", vci, Some(edata));
+        self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps)).unwrap();
+
+        if num_audio_tracks != 0 {
+            validate!(audio_off + ((num_audio_tracks * AUDIO_EXTRADATA_LEN) as u64) == vinfo_off);
+            src.seek(SeekFrom::Start(audio_off))?;
+            let mut edata = vec![0u8; AUDIO_EXTRADATA_LEN];
+                                          src.read_buf(edata.as_mut_slice())?;
+            let ahdr = NAAudioInfo::new(srate, 1, SND_S16P_FORMAT, 1);
+            let ainfo = NACodecInfo::new("vxaudio", NACodecTypeInfo::Audio(ahdr), Some(edata));
+            self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate)).unwrap();
+            self.num_afrm = nframes as u64;
+            self.ano = 0;
+            self.num_aud = num_audio_tracks;
+        }
+
+        if num_vstreams > 0 {
+            src.seek(SeekFrom::Start(vinfo_off))?;
+            for _ in 0..num_vstreams {
+                let _smth               = src.read_u32le()?;
+                let offset              = src.read_u32le()? as u64;
+                self.video_pos = offset;
+            }
+        }
+
+        self.num_vid  = num_vstreams;
+        self.vno = 0;
+        self.num_vfrm = nframes as u64;
+        Ok(())
+    }
+
+    fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+        if self.vno >= self.num_vfrm { return Err(DemuxerError::EOF); }
+        self.src.seek(SeekFrom::Start(self.video_pos))?;
+        let stream = strmgr.get_stream(self.vid_id);
+        if stream.is_none() { return Err(DemuxerError::InvalidData); }
+        let stream = stream.unwrap();
+        let (tn, td) = stream.get_timebase();
+        let ts = NATimeInfo::new(Some(self.vno), None, None, tn, td);
+        let size                    = self.src.read_u16le()? as usize;
+        validate!(size > 2);
+        let _num_achunks            = self.src.read_u16le()?;
+        let fsize = size - 2;
+        let pkt                     = self.src.read_packet(stream, ts, false, fsize)?;
+        self.video_pos = self.src.tell();
+        self.vno += 1;
+        Ok(pkt)
+    }
+
+    fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+        Err(DemuxerError::NotPossible)
+    }
+}
+
+impl<'a> NAOptionHandler for VXDemuxer<'a> {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+impl<'a> VXDemuxer<'a> {
+    fn new(io: &'a mut ByteReader<'a>) -> Self {
+        Self {
+            vid_id:     0,
+            aud_id:     0,
+            video_pos:  0,
+            num_vid:    0,
+            num_aud:    0,
+            vno:        0,
+            ano:        0,
+            num_vfrm:   0,
+            num_afrm:   0,
+            src:        io,
+        }
+    }
+}
+
+pub struct VXDemuxerCreator { }
+
+impl DemuxerCreator for VXDemuxerCreator {
+    fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+        Box::new(VXDemuxer::new(br))
+    }
+    fn get_name(&self) -> &'static str { "vx" }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::fs::File;
+
+    #[test]
+    fn test_vx_demux() {
+        let mut file = File::open("assets/Game/bioware.vx").unwrap();
+        //let mut file = File::open("assets/Game/BS_01_Intro.vx").unwrap();
+        //let mut file = File::open("assets/Game/sega.vx").unwrap();
+        let mut fr = FileReader::new_read(&mut file);
+        let mut br = ByteReader::new(&mut fr);
+        let mut dmx = VXDemuxer::new(&mut br);
+        let mut sm = StreamManager::new();
+        let mut si = SeekIndex::new();
+        dmx.open(&mut sm, &mut si).unwrap();
+        loop {
+            let pktres = dmx.get_frame(&mut sm);
+            if let Err(e) = pktres {
+                if (e as i32) == (DemuxerError::EOF as i32) { break; }
+                panic!("error");
+            }
+            let pkt = pktres.unwrap();
+            println!("Got {}", pkt);
+        }
+    }
+}
index 92bb920e0e36555840d067f7c20a22242b2a62dc..6f67fbd7253502f17f82a212d8bfce2a6eb531a0 100644 (file)
@@ -282,6 +282,11 @@ const DETECTORS: &[DetectConditions] = &[
         extensions: ".vmd",
         conditions: &[],
     },
+    DetectConditions {
+        demux_name: "vx",
+        extensions: ".vx",
+        conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"VXDS") }],
+    },
 ];
 
 /// Tries to detect container format.
index 177cf0604546f6797abaa29dff5d4115e6c4f0bd..0accdd4e6263307ab0986bf4a452381d0e22421c 100644 (file)
@@ -226,6 +226,8 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[
     desc!(video-ll; "midivid-ll",    "MidiVid Lossless"),
     desc!(video;    "vmd-video",     "VMD video"),
     desc!(audio;    "vmd-audio",     "VMD audio"),
+    desc!(video;    "vxvideo",       "Actimagine Vx"),
+    desc!(audio;    "vxaudio",       "Actimagine Sx"),
 
     desc!(video;    "smacker-video", "Smacker video"),
     desc!(audio;    "smacker-audio", "Smacker audio"),