--- /dev/null
+use nihav_core::frame::*;
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+
+#[derive(Clone,Copy,Debug,PartialEq)]
+enum TileMode {
+ Start,
+ Fill,
+ ShortPattern(u8, u8),
+ LongPattern(u8, u8),
+ Run,
+ Reuse,
+ FB,
+ MV,
+ Forward(u16),
+ Backward(u16),
+ Skip,
+}
+
+struct QVideoDecoder {
+ info: NACodecInfoRef,
+ pal: [u8; 768],
+ frame: Vec<u8>,
+ w: usize,
+ h: usize,
+ tile_w: usize,
+ tile_h: usize,
+ tile_off: Vec<usize>,
+ mode: u8,
+ patterns: [u16; 128],
+ version: u8,
+}
+
+macro_rules! copy_tile {
+ ($self: expr, $doff: expr, $soff: expr) => {
+ let mut doff = $doff;
+ let mut soff = $soff;
+ for _y in 0..$self.tile_h {
+ for x in 0..$self.tile_w {
+ $self.frame[doff + x] = $self.frame[soff + x];
+ }
+ doff += $self.w;
+ soff += $self.w;
+ }
+ }
+}
+
+impl QVideoDecoder {
+ fn new() -> Self {
+ QVideoDecoder {
+ info: NACodecInfoRef::default(),
+ pal: [0; 768],
+ frame: Vec::new(),
+ w: 0,
+ h: 0,
+ tile_off: Vec::new(),
+ mode: 0,
+ tile_w: 0,
+ tile_h: 0,
+ patterns: [0; 128],
+ version: 0,
+ }
+ }
+
+ fn decode_mode7_tile(dst: &mut [u8], stride: usize, br: &mut ByteReader) -> DecoderResult<()> {
+ let op = br.peek_byte()?;
+ if op < 0xF8 {
+ for dline in dst.chunks_mut(stride).take(4) {
+ br.read_buf(&mut dline[..4])?;
+ }
+ } else if op == 0xF8 || op == 0xFF {
+ br.read_byte()?;
+ } else {
+ br.read_byte()?;
+ let mut clr = [0; 8];
+ let nclr = (op - 0xF6) as usize;
+ if nclr <= 4 {
+ let mut pattern = br.read_u32le()?;
+ br.read_buf(&mut clr[..nclr])?;
+ for dline in dst.chunks_mut(stride).take(4) {
+ for el in dline[..4].iter_mut() {
+ *el = clr[(pattern & 3) as usize];
+ pattern >>= 2;
+ }
+ }
+ } else {
+ let mut pattern = br.read_u24le()?;
+ let pattern2 = br.read_u24le()?;
+ br.read_buf(&mut clr[..nclr])?;
+ for (y, dline) in dst.chunks_mut(stride).take(4).enumerate() {
+ for el in dline[..4].iter_mut() {
+ *el = clr[(pattern & 7) as usize];
+ pattern >>= 3;
+ }
+ if y == 1 {
+ pattern = pattern2;
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn decode_frame_v3(&mut self, br: &mut ByteReader, _ctype: u16) -> DecoderResult<()> {
+ let mut titer = self.tile_off.iter().enumerate();
+ let mut skip_mode = false;
+ while let Some((tile_no, &tile_off)) = titer.next() {
+ let op = br.read_byte()?;
+ if op < 0xF8 {
+ let clr0 = op;
+ let clr1 = br.read_byte()?;
+ if clr0 == clr1 {
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = clr0;
+ }
+ }
+ } else {
+ let mut pattern = br.read_u16le()?;
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = if (pattern & 0x8000) == 0 { clr0 } else { clr1 };
+ pattern <<= 1;
+ }
+ }
+ }
+ } else {
+ match op {
+ 0xF8 => {
+ unimplemented!();
+ },
+ 0xF9 => {
+ let run = br.read_byte()? as usize;
+ validate!(run > 0);
+
+ validate!(tile_no > 0);
+ let mut tile_off = tile_off;
+ for i in 0..run {
+ if !skip_mode {
+ copy_tile!(self, tile_off, self.tile_off[tile_no - 1]);
+ }
+ if i + 1 < run {
+ let (_tno, &toff) = titer.next().unwrap();
+ tile_off = toff;
+ }
+ }
+ },
+ 0xFA => {
+ let off = br.read_u16le()? as usize;
+ validate!(tile_no + off < self.tile_off.len());
+ copy_tile!(self, tile_off, self.tile_off[tile_no + off]);
+ },
+ 0xFB => {
+ let off = br.read_u16le()? as usize;
+ validate!(off <= tile_no);
+ copy_tile!(self, tile_off, self.tile_off[tile_no - off]);
+ },
+ 0xFC => {
+ const MV_PART: [i8; 16] = [ 0, 4, 8, 12, 16, 20, 24, 28, -32, -4, -8, -12, -16, -20, -24, -28 ];
+
+ let idx = br.read_byte()? as usize;
+ let x = MV_PART[(idx & 0xF) as usize] as isize;
+ let y = MV_PART[(idx >> 4) as usize] as isize;
+ let src_off = (tile_off as isize) + x + y * (self.w as isize);
+ validate!(src_off >= 0);
+ validate!((src_off as usize) + self.tile_w + (self.tile_h - 1) * self.w <= self.w * self.h);
+
+ copy_tile!(self, tile_off, src_off as usize);
+ },
+ 0xFD => {
+ let off = (br.read_byte()? as usize) + 1;
+ validate!(tile_no + off < self.tile_off.len());
+ copy_tile!(self, tile_off, self.tile_off[tile_no + off]);
+ },
+ 0xFE => {
+ let off = (br.read_byte()? as usize) + 1;
+ validate!(off <= tile_no);
+ copy_tile!(self, tile_off, self.tile_off[tile_no - off]);
+ },
+ _ => {},
+ };
+ }
+ skip_mode = op == 0xFF;
+ }
+
+ Ok(())
+ }
+
+ fn decode_frame(&mut self, br: &mut ByteReader, _ctype: u16) -> DecoderResult<()> {
+ let mut titer = self.tile_off.iter().enumerate();
+ let mut last_mode = TileMode::Start;
+
+ while let Some((tile_no, &tile_off)) = titer.next() {
+ let op = br.read_byte()?;
+ if op < 0xF8 {
+ let clr0 = op;
+ let clr1 = br.read_byte()?;
+ if clr0 == clr1 {
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = clr0;
+ }
+ }
+ last_mode = TileMode::Fill;
+ } else {
+ let pat = br.read_byte()?;
+ let mut pattern = if pat < 128 {
+ last_mode = TileMode::ShortPattern(clr0, clr1);
+ self.patterns[pat as usize]
+ } else {
+ last_mode = TileMode::LongPattern(clr0, clr1);
+ u16::from(pat) | (u16::from(br.read_byte()?) << 8)
+ };
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = if (pattern & 0x8000) == 0 { clr0 } else { clr1 };
+ pattern <<= 1;
+ }
+ }
+ }
+ } else {
+ match op {
+ 0xF8 => {
+ unimplemented!();
+ },
+ 0xF9 => {
+ let run = br.read_byte()? as usize;
+ validate!(run > 0);
+
+ validate!(tile_no > 0);
+ validate!(last_mode != TileMode::Start);
+ let mut tile_no = tile_no;
+ let mut tile_off = tile_off;
+ for i in 0..run {
+ let copy_off = match last_mode {
+ TileMode::Forward(off) => {
+ tile_no + (off as usize)
+ },
+ TileMode::Backward(off) => {
+ validate!(tile_no >= (off as usize));
+ tile_no - (off as usize)
+ },
+ TileMode::Skip => self.tile_off.len(),
+ _ => tile_no - 1,
+ };
+ if copy_off < self.tile_off.len() {
+ copy_tile!(self, tile_off, self.tile_off[copy_off]);
+ }
+ if i + 1 < run {
+ let (tno, &toff) = titer.next().unwrap();
+ tile_no = tno;
+ tile_off = toff;
+ }
+ }
+ last_mode = TileMode::Run;
+ },
+ 0xFA => {
+ let rtile = br.read_u16le()? as usize;
+ validate!(rtile < self.tile_off.len());
+ copy_tile!(self, tile_off, self.tile_off[rtile]);
+ last_mode = TileMode::Reuse;
+ },
+ 0xFB => {
+ match self.mode {
+ 6 => {
+ let run = br.read_byte()? as usize;
+ validate!(run >= 2);
+ let mut tile_no = tile_no;
+ let mut tile_off = tile_off;
+ for i in 0..run {
+ match last_mode {
+ TileMode::Start => return Err(DecoderError::InvalidData),
+ TileMode::Fill => {
+ let clr = br.read_byte()?;
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = clr;
+ }
+ }
+ },
+ TileMode::ShortPattern(clr0, clr1) => {
+ let pat = br.read_byte()?;
+ let mut pattern = if pat < 128 {
+ self.patterns[pat as usize]
+ } else {
+ u16::from(pat) | (u16::from(br.read_byte()?) << 8)
+ };
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = if (pattern & 0x8000) == 0 { clr0 } else { clr1 };
+ pattern <<= 1;
+ }
+ }
+ },
+ TileMode::LongPattern(clr0, clr1) => {
+ let mut pattern = br.read_u16le()?;
+ for dline in self.frame[tile_off..].chunks_mut(self.w).take(self.tile_h) {
+ for el in dline[..self.tile_w].iter_mut() {
+ *el = if (pattern & 0x8000) == 0 { clr0 } else { clr1 };
+ pattern <<= 1;
+ }
+ }
+ },
+ TileMode::Reuse => {
+ let rtile = br.read_u16le()? as usize;
+ validate!(rtile < self.tile_off.len());
+ copy_tile!(self, tile_off, self.tile_off[rtile]);
+ },
+ TileMode::MV => {
+ let idx = br.read_byte()? as usize;
+ let (x, y) = DEF_MVS[idx];
+ let src_off = (tile_off as isize) + (x as isize) * 4 + (y as isize) * 4 * (self.w as isize);
+ validate!(src_off >= 0);
+ validate!((src_off as usize) + self.tile_w + (self.tile_h - 1) * self.w <= self.w * self.h);
+ copy_tile!(self, tile_off, src_off as usize);
+ },
+ TileMode::Forward(_) => {
+ let off = (br.read_byte()? as usize) + 1;
+ validate!(tile_no + off < self.tile_off.len());
+ copy_tile!(self, tile_off, self.tile_off[tile_no + off]);
+ },
+ TileMode::Backward(_) => {
+ let off = (br.read_byte()? as usize) + 1;
+ validate!(off <= tile_no);
+ copy_tile!(self, tile_off, self.tile_off[tile_no - off]);
+ },
+ _ => unimplemented!(),
+ };
+
+ if i + 1 < run {
+ let (tno, &toff) = titer.next().unwrap();
+ tile_no = tno;
+ tile_off = toff;
+ }
+ }
+ },
+ 7 => {
+ validate!(self.tile_w == 4 && self.tile_h == 4);
+ let run = br.read_byte()? as usize;
+ validate!(run > 0);
+
+ let mut tile_off = tile_off;
+ for i in 0..run {
+ Self::decode_mode7_tile(&mut self.frame[tile_off..], self.w, br)?;
+
+ if i + 1 < run {
+ let (_tno, &toff) = titer.next().unwrap();
+ tile_off = toff;
+ }
+ }
+ },
+ _ => {
+ unimplemented!();
+ },
+ };
+ last_mode = TileMode::FB;
+ },
+ 0xFC => {
+ let idx = br.read_byte()? as usize;
+ let (x, y) = DEF_MVS[idx];
+ let src_off = (tile_off as isize) + (x as isize) * 4 + (y as isize) * 4 * (self.w as isize);
+ validate!(src_off >= 0);
+ validate!((src_off as usize) + self.tile_w + (self.tile_h - 1) * self.w <= self.w * self.h);
+
+ copy_tile!(self, tile_off, src_off as usize);
+ last_mode = TileMode::MV;
+ },
+ 0xFD => {
+ let off = (br.read_byte()? as usize) + 1;
+ validate!(tile_no + off < self.tile_off.len());
+ copy_tile!(self, tile_off, self.tile_off[tile_no + off]);
+ last_mode = TileMode::Forward(off as u16);
+ },
+ 0xFE => {
+ let off = (br.read_byte()? as usize) + 1;
+ validate!(off <= tile_no);
+ copy_tile!(self, tile_off, self.tile_off[tile_no - off]);
+ last_mode = TileMode::Backward(off as u16);
+ },
+ _ => {
+ last_mode = TileMode::Skip;
+ },
+ };
+ }
+ }
+
+ Ok(())
+ }
+
+ fn output_frame(&mut self, bufinfo: &mut NABufferType, w: usize, h: usize) {
+ let bufo = bufinfo.get_vbuf();
+ let mut buf = bufo.unwrap();
+ let paloff = buf.get_offset(1);
+ let stride = buf.get_stride(0);
+ let data = buf.get_data_mut().unwrap();
+ let dst = data.as_mut_slice();
+
+ dst[paloff..][..768].copy_from_slice(&self.pal);
+ for (dline, sline) in dst.chunks_mut(stride).zip(self.frame.chunks(w)).take(h) {
+ dline[..w].copy_from_slice(sline);
+ }
+ }
+}
+
+impl NADecoder for QVideoDecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() {
+ let mut w;
+ let mut h;
+ if let Some(buf) = info.get_extradata() {
+ validate!(buf.len() >= 22);
+ w = read_u16le(&buf[4..])? as usize;
+ h = read_u16le(&buf[6..])? as usize;
+ self.tile_w = buf[8] as usize;
+ self.tile_h = buf[9] as usize;
+ validate!(self.tile_w > 0 && self.tile_h > 0);
+ if self.tile_w != 4 || self.tile_h != 4 {
+ return Err(DecoderError::NotImplemented);
+ }
+ self.version = buf[2];
+ if self.version != 3{
+ w *= self.tile_w;
+ h *= self.tile_h;
+ } else {
+ validate!((w % self.tile_w) == 0);
+ validate!((h % self.tile_h) == 0);
+ }
+ } else {
+ return Err(DecoderError::InvalidData);
+ }
+ let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, PAL8_FORMAT));
+ self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+ self.w = w;
+ self.h = h;
+
+ self.mode = match self.version {
+ 4 => 6,
+ 5 => 7,
+ _ => 0,
+ };
+
+ self.frame.resize(w * h, 0);
+ self.pal = [0; 768];
+ self.tile_off = Vec::with_capacity((w / self.tile_w) * (h / self.tile_h));
+ let mut off = 0;
+ for _y in (0..h).step_by(self.tile_h) {
+ for x in (0..w).step_by(self.tile_w) {
+ self.tile_off.push(off + x);
+ }
+ off += w * self.tile_h;
+ }
+ Ok(())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+ let src = pkt.get_buffer();
+ validate!(!src.is_empty());
+
+ let mut mr = MemoryReader::new_read(&src);
+ let mut br = ByteReader::new(&mut mr);
+
+ while br.left() >= 6 {
+ let ctype = br.read_u16le()?;
+ let csize = br.read_u32le()? as usize;
+ validate!(csize <= (br.left() as usize));
+ match ctype {
+ 1 => {
+ validate!(csize <= 768);
+ br.read_buf(&mut self.pal[..csize])?;
+ for el in self.pal[..csize].iter_mut() {
+ *el = (*el << 2) | (*el >> 4);
+ }
+ },
+ 2 | 3 | 4 | 9 | 11 => {
+ if self.version == 5 {
+ self.mode = if ctype == 9 || ctype == 11 { 7 } else { 6 };
+ }
+ if self.version == 3 {
+ self.decode_frame_v3(&mut br, ctype)?;
+ } else {
+ self.decode_frame(&mut br, ctype)?;
+ }
+ },
+ 5 => {
+ validate!(csize <= 256 && (csize & 1) == 0);
+ for el in self.patterns[..csize/2].iter_mut() {
+ *el = br.read_u16le()?;
+ }
+ },
+ 6 | 7 => {
+ self.mode = ctype as u8;
+ },
+ _ => return Err(DecoderError::InvalidData),
+ };
+ }
+
+ let mut bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?;
+
+ self.output_frame(&mut bufinfo, self.w, self.h);
+
+ let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+ frm.set_frame_type(if pkt.is_keyframe() { FrameType::I } else { FrameType::P });
+ Ok(frm.into_ref())
+ }
+ fn flush(&mut self) {
+ }
+}
+
+impl NAOptionHandler for QVideoDecoder {
+ 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() -> Box<dyn NADecoder + Send> {
+ Box::new(QVideoDecoder::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_q_video3() {
+ 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);
+
+ test_decoding("legend-q", "legend-q-video", "assets/Game/dgate101.q", Some(31), &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5([0x9cc0014c, 0xf6332802, 0xfabeb715, 0xdfaa11c0]));
+ }
+ #[test]
+ fn test_q_video4() {
+ 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);
+
+ test_decoding("legend-q", "legend-q-video", "assets/Game/1925.Q", None, &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5([0xe1af971a, 0xfb509816, 0x9d60f5d6, 0xbcf48a3b]));
+ }
+ #[test]
+ fn test_q_video5() {
+ 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);
+
+ test_decoding("legend-q", "legend-q-video", "assets/Game/mc703.q", Some(16), &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5([0xf65ea3ce, 0x3052b2bb, 0xb10f8f69, 0x530d60f9]));
+ }
+}
+
+const DEF_MVS: [(i8, i8); 256] = [
+ ( 0, 8), ( 1, 8), ( 2, 8), ( 3, 8), ( 4, 8), ( 5, 8), ( 6, 8), ( 7, 8),
+ (-8, 8), (-1, 8), (-2, 8), (-3, 8), (-4, 8), (-5, 8), (-6, 8), (-7, 8),
+ ( 0, 9), ( 1, 9), ( 2, 9), ( 3, 9), ( 4, 9), ( 5, 9), ( 6, 9), ( 7, 9),
+ (-8, 9), (-1, 9), (-2, 9), (-3, 9), (-4, 9), (-5, 9), (-6, 9), (-7, 9),
+ ( 0, 2), ( 1, 2), ( 2, 2), ( 3, 2), ( 4, 2), ( 5, 2), ( 6, 2), ( 7, 2),
+ (-8, 2), (-1, 2), (-2, 2), (-3, 2), (-4, 2), (-5, 2), (-6, 2), (-7, 2),
+ ( 0, 3), ( 1, 3), ( 2, 3), ( 3, 3), ( 4, 3), ( 5, 3), ( 6, 3), ( 7, 3),
+ (-8, 3), (-1, 3), (-2, 3), (-3, 3), (-4, 3), (-5, 3), (-6, 3), (-7, 3),
+ ( 0, 4), ( 1, 4), ( 2, 4), ( 3, 4), ( 4, 4), ( 5, 4), ( 6, 4), ( 7, 4),
+ (-8, 4), (-1, 4), (-2, 4), (-3, 4), (-4, 4), (-5, 4), (-6, 4), (-7, 4),
+ ( 0, 5), ( 1, 5), ( 2, 5), ( 3, 5), ( 4, 5), ( 5, 5), ( 6, 5), ( 7, 5),
+ (-8, 5), (-1, 5), (-2, 5), (-3, 5), (-4, 5), (-5, 5), (-6, 5), (-7, 5),
+ ( 0, 6), ( 1, 6), ( 2, 6), ( 3, 6), ( 4, 6), ( 5, 6), ( 6, 6), ( 7, 6),
+ (-8, 6), (-1, 6), (-2, 6), (-3, 6), (-4, 6), (-5, 6), (-6, 6), (-7, 6),
+ ( 0, 7), ( 1, 7), ( 2, 7), ( 3, 7), ( 4, 7), ( 5, 7), ( 6, 7), ( 7, 7),
+ (-8, 7), (-1, 7), (-2, 7), (-3, 7), (-4, 7), (-5, 7), (-6, 7), (-7, 7),
+ ( 0,-8), ( 1,-8), ( 2,-8), ( 3,-8), ( 4,-8), ( 5,-8), ( 6,-8), ( 7,-8),
+ (-8,-8), (-1,-8), (-2,-8), (-3,-8), (-4,-8), (-5,-8), (-6,-8), (-7,-8),
+ ( 0,-9), ( 1,-9), ( 2,-9), ( 3,-9), ( 4,-9), ( 5,-9), ( 6,-9), ( 7,-9),
+ (-8,-9), (-1,-9), (-2,-9), (-3,-9), (-4,-9), (-5,-9), (-6,-9), (-7,-9),
+ ( 0,-2), ( 1,-2), ( 2,-2), ( 3,-2), ( 4,-2), ( 5,-2), ( 6,-2), ( 7,-2),
+ (-8,-2), (-1,-2), (-2,-2), (-3,-2), (-4,-2), (-5,-2), (-6,-2), (-7,-2),
+ ( 0,-3), ( 1,-3), ( 2,-3), ( 3,-3), ( 4,-3), ( 5,-3), ( 6,-3), ( 7,-3),
+ (-8,-3), (-1,-3), (-2,-3), (-3,-3), (-4,-3), (-5,-3), (-6,-3), (-7,-3),
+ ( 0,-4), ( 1,-4), ( 2,-4), ( 3,-4), ( 4,-4), ( 5,-4), ( 6,-4), ( 7,-4),
+ (-8,-4), (-1,-4), (-2,-4), (-3,-4), (-4,-4), (-5,-4), (-6,-4), (-7,-4),
+ ( 0,-5), ( 1,-5), ( 2,-5), ( 3,-5), ( 4,-5), ( 5,-5), ( 6,-5), ( 7,-5),
+ (-8,-5), (-1,-5), (-2,-5), (-3,-5), (-4,-5), (-5,-5), (-6,-5), (-7,-5),
+ ( 0,-6), ( 1,-6), ( 2,-6), ( 3,-6), ( 4,-6), ( 5,-6), ( 6,-6), ( 7,-6),
+ (-8,-6), (-1,-6), (-2,-6), (-3,-6), (-4,-6), (-5,-6), (-6,-6), (-7,-6),
+ ( 0,-7), ( 1,-7), ( 2,-7), ( 3,-7), ( 4,-7), ( 5,-7), ( 6,-7), ( 7,-7),
+ (-8,-7), (-1,-7), (-2,-7), (-3,-7), (-4,-7), (-5,-7), (-6,-7), (-7,-7)
+];
--- /dev/null
+use nihav_core::frame::*;
+use nihav_core::demuxers::*;
+
+#[allow(dead_code)]
+struct QDemuxer<'a> {
+ src: &'a mut ByteReader<'a>,
+ vpts: u64,
+ apts: u64,
+ bps: usize,
+ a_id: Option<usize>,
+ v_id: Option<usize>,
+ nframes: usize,
+ duration: u64,
+ side_data: Vec<u8>,
+}
+
+impl<'a> DemuxCore<'a> for QDemuxer<'a> {
+ #[allow(unused_variables)]
+ fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+ let src = &mut self.src;
+
+ let mut hdr = [0; 22];
+ src.read_buf(&mut hdr)?;
+ validate!(hdr[0] == 0x39);
+ validate!(hdr[1] == 0x68);
+ let version = hdr[2];
+ validate!(version >= 3 && version <= 5);
+ let mut width = read_u16le(&hdr[4..])? as usize;
+ let mut height = read_u16le(&hdr[6..])? as usize;
+ if version > 3 {
+ width *= hdr[8] as usize;
+ height *= hdr[9] as usize;
+ }
+ validate!(width > 0 && width <= 800);
+ validate!(height > 0 && height <= 600);
+
+ self.nframes = read_u16le(&hdr[10..])? as usize;
+ validate!(self.nframes > 0);
+ let fps = if hdr[16] == 0 { 5 } else { hdr[16] as u32 };
+ self.duration = (self.nframes as u64) * 1000 / u64::from(fps);
+ let asize = if version > 3 {
+ src.read_u32le()?
+ } else { 0 };
+
+ let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT);
+ let vci = NACodecTypeInfo::Video(vhdr);
+ let vinfo = NACodecInfo::new("legend-q-video", vci, Some(hdr.to_vec()));
+ self.v_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps, self.nframes as u64));
+ if asize != 0 {
+ let ntype = self.src.peek_byte()?;
+ if ntype == 8 {
+ let _ = self.src.read_u16le()?;
+ let size = self.src.read_u32le()? as usize;
+ validate!(size >= 44);
+ let mut buf = vec![0; size];
+ self.src.read_buf(&mut buf)?;
+ let arate = read_u32le(&buf[24..])?;
+ let channels = buf[22];
+ let abits = buf[34] as usize;
+ validate!(abits == 8 || abits == 16);
+ self.bps = (channels as usize) * abits / 8;
+ let bsize = read_u16le(&buf[32..])? as usize;
+ let ahdr = NAAudioInfo::new(arate, channels as u8, if abits == 16 { SND_S16_FORMAT } else { SND_U8_FORMAT }, bsize);
+ let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None);
+ self.a_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, arate, 2));
+ }
+ }
+ self.apts = 0;
+ self.vpts = 0;
+ self.side_data.truncate(0);
+ Ok(())
+ }
+
+ fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+ loop {
+ let ctype = self.src.read_u16le()?;
+ let size = self.src.read_u32le()? as usize;
+ match ctype {
+ 0xFFFF => return Err(DemuxerError::EOF),
+ 0 => {
+ if let Some(a_id) = self.a_id {
+ let str = strmgr.get_stream(a_id).unwrap();
+ let (tb_num, tb_den) = str.get_timebase();
+ let ts = NATimeInfo::new(Some(self.apts), None, None, tb_num, tb_den);
+ self.apts += (size / self.bps) as u64;
+ return self.src.read_packet(str, ts, true, size);
+ } else {
+ return Err(DemuxerError::InvalidData);
+ }
+ },
+ 1 => {
+ validate!(size <= 768);
+ let cur_len = self.side_data.len();
+ self.side_data.resize(cur_len + size + 6, 0);
+ self.side_data[cur_len] = ctype as u8;
+ write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?;
+ self.src.read_buf(&mut self.side_data[cur_len + 6..])?;
+ },
+ 2 | 3 | 4 | 11 => {
+ validate!(self.v_id.is_some());
+ let str = strmgr.get_stream(self.v_id.unwrap_or(0)).unwrap();
+ let (tb_num, tb_den) = str.get_timebase();
+ let ts = NATimeInfo::new(Some(self.vpts), None, None, tb_num, tb_den);
+ self.vpts += 1;
+
+ let cur_len = self.side_data.len();
+ self.side_data.resize(cur_len + size + 6, 0);
+ self.side_data[cur_len] = ctype as u8;
+ self.side_data[cur_len] = ctype as u8;
+ write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?;
+ if let Err(err) = self.src.read_buf(&mut self.side_data[cur_len + 6..]) {
+ self.side_data.truncate(cur_len);
+ return Err(err.into());
+ }
+ let mut buf = Vec::new();
+ std::mem::swap(&mut buf, &mut self.side_data);
+ return Ok(NAPacket::new(str, ts, self.vpts == 1, buf));
+ },
+ 5 => {
+ validate!(size <= 256);
+ let cur_len = self.side_data.len();
+ self.side_data.resize(cur_len + size + 6, 0);
+ self.side_data[cur_len] = ctype as u8;
+ write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?;
+ self.src.read_buf(&mut self.side_data[cur_len + 6..])?;
+ },
+ 6 | 7 => {
+ self.side_data.push(ctype as u8);
+ self.side_data.push(0);
+ self.side_data.push(0);
+ self.side_data.push(0);
+ self.side_data.push(0);
+ self.side_data.push(0);
+ },
+ 8 => return Err(DemuxerError::InvalidData), //should be handled before main loop
+ 9 => { // first part of interlaced frame
+ let cur_len = self.side_data.len();
+ self.side_data.resize(cur_len + size + 6, 0);
+ self.side_data[cur_len] = ctype as u8;
+ write_u32le(&mut self.side_data[cur_len + 2..], size as u32)?;
+ self.src.read_buf(&mut self.side_data[cur_len + 6..])?;
+ },
+ _ => {
+ self.src.read_skip(size)?;
+ },
+ };
+ }
+ }
+
+ fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+ Err(DemuxerError::NotPossible)
+ }
+ fn get_duration(&self) -> u64 { self.duration }
+}
+impl<'a> NAOptionHandler for QDemuxer<'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> QDemuxer<'a> {
+ fn new(io: &'a mut ByteReader<'a>) -> Self {
+ QDemuxer {
+ src: io,
+ vpts: 0,
+ apts: 0,
+ bps: 0,
+ a_id: None,
+ v_id: None,
+ nframes: 0,
+ duration: 0,
+ side_data: Vec::with_capacity(256 + 6),
+ }
+ }
+}
+
+pub struct QDemuxerCreator { }
+
+impl DemuxerCreator for QDemuxerCreator {
+ fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+ Box::new(QDemuxer::new(br))
+ }
+ fn get_name(&self) -> &'static str { "legend-q" }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn test_q_demux_v3() {
+ let mut file = File::open("assets/Game/dgate101.q").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = QDemuxer::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);
+ }
+ }
+
+ #[test]
+ fn test_q_demux_v4() {
+ let mut file = File::open("assets/Game/1425A5.Q").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = QDemuxer::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);
+ }
+ }
+
+ #[test]
+ fn test_q_demux_v5() {
+ let mut file = File::open("assets/Game/mc703.q").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = QDemuxer::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);
+ }
+ }
+}