introduce stream and container duration
[nihav.git] / nihav-commonfmt / src / demuxers / mov.rs
index 29c512de3ae0039c9f5169d11fd2d0757f3c05e7..9236baa1b0667d0132745e31e3465d959dafe193 100644 (file)
@@ -1,5 +1,6 @@
 use nihav_core::demuxers::*;
 use nihav_registry::register::*;
+use nihav_core::compr::deflate::*;
 
 macro_rules! mktag {
     ($a:expr, $b:expr, $c:expr, $d:expr) => ({
@@ -169,6 +170,7 @@ fn read_moov(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> Dem
 
 const MOOV_CHUNK_HANDLERS: &[RootChunkHandler] = &[
     RootChunkHandler { ctype: mktag!(b"mvhd"), parse: read_mvhd },
+    RootChunkHandler { ctype: mktag!(b"cmov"), parse: read_cmov },
     RootChunkHandler { ctype: mktag!(b"ctab"), parse: read_ctab },
     RootChunkHandler { ctype: mktag!(b"trak"), parse: read_trak },
     RootChunkHandler { ctype: mktag!(b"meta"), parse: read_meta },
@@ -202,6 +204,45 @@ fn read_mvhd(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> De
     Ok(KNOWN_MVHD_SIZE)
 }
 
+fn read_cmov(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult<u64> {
+    let br = &mut dmx.src;
+    validate!(size > 24);
+    let dcom_size           = br.read_u32be()?;
+    let dcom_tag            = br.read_tag()?;
+    let compr_type          = br.read_tag()?;
+    validate!(&dcom_tag == b"dcom" && dcom_size == 12);
+    if &compr_type != b"zlib" {
+        return Err(DemuxerError::NotImplemented);
+    }
+    let cmvd_size           = u64::from(br.read_u32be()?);
+    let cmvd_tag            = br.read_tag()?;
+    validate!(&cmvd_tag == b"cmvd" && cmvd_size > 14 && cmvd_size == size - 12);
+    let comp_size = (cmvd_size - 12) as usize;
+    let uncomp_size         = br.read_u32be()? as usize;
+    validate!(uncomp_size > 8);
+    let mut sbuf = vec![0; comp_size];
+    let mut dbuf = vec![0; uncomp_size];
+                              br.read_buf(sbuf.as_mut_slice())?;
+    let ret = Inflate::uncompress(sbuf.as_slice(), dbuf.as_mut_slice());
+    if ret.is_err() {
+        return Err(DemuxerError::InvalidData);
+    }
+    let len = ret.unwrap();
+    validate!(len == uncomp_size);
+    let mut mr = MemoryReader::new_read(dbuf.as_slice());
+    let mut br = ByteReader::new(&mut mr);
+    let (ctype, csize) = read_chunk_header(&mut br)?;
+    validate!(ctype == mktag!(b"moov"));
+    let mut ddmx = MOVDemuxer::new(&mut br);
+    ddmx.read_moov(strmgr, csize)?;
+    std::mem::swap(&mut dmx.tracks, &mut ddmx.tracks);
+    dmx.duration = ddmx.duration;
+    dmx.tb_den = ddmx.tb_den;
+    std::mem::swap(&mut dmx.pal, &mut ddmx.pal);
+
+    Ok(size)
+}
+
 fn read_ctab(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult<u64> {
     let mut pal = [0; 1024];
     let size = read_palette(&mut dmx.src, size, &mut pal)?;
@@ -251,7 +292,7 @@ fn read_tkhd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     let _mtime              = br.read_u32be()?;
     let track_id            = br.read_u32be()?;
                               br.read_skip(4)?;
-    let _duration           = br.read_u32be()?;
+    let duration            = br.read_u32be()?;
                               br.read_skip(8)?;
     let _layer              = br.read_u16be()?;
     let _alt_group          = br.read_u16be()?;
@@ -263,6 +304,7 @@ fn read_tkhd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     track.width  = width  >> 16;
     track.height = height >> 16;
     track.track_id = track_id;
+    track.duration = duration;
 
     track.tkhd_found = true;
     Ok(KNOWN_TKHD_SIZE)
@@ -307,7 +349,7 @@ fn read_hdlr(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
         println!("Unknown stream type");
         track.stream_type = StreamType::Data;
     }
-    
+
     Ok(KNOWN_HDLR_SIZE)
 }
 
@@ -384,6 +426,47 @@ const STBL_CHUNK_HANDLERS: &[TrackChunkHandler] = &[
     TrackChunkHandler { ctype: mktag!(b"stsh"), parse: skip_chunk },
 ];
 
+fn parse_audio_edata(br: &mut ByteReader, start_pos: u64, size: u64) -> DemuxerResult<Option<Vec<u8>>> {
+    let read_part = br.tell() - start_pos;
+    if read_part + 8 < size {
+        let mut buf = [0; 8];
+                              br.peek_buf(&mut buf)?;
+        if &buf[4..8] != b"wave" {
+            let mut buf = vec![0; (size - read_part) as usize];
+                              br.read_buf(&mut buf)?;
+            return Ok(Some(buf));
+        }
+
+        let csize           = br.read_u32be()? as u64;
+        let ctag            = br.read_u32be()?;
+        validate!(read_part + csize <= size);
+        validate!(ctag == mktag!(b"wave"));
+        if csize == 8 {
+            return Ok(None);
+        }
+        let mut buf = [0; 8];
+                              br.peek_buf(&mut buf)?;
+        if &buf[4..8] == b"frma" {
+                              br.read_skip(12)?;
+            if csize > 20 {
+                let mut buf = vec![0; (csize - 20) as usize];
+                              br.read_buf(&mut buf)?;
+                Ok(Some(buf))
+            } else {
+                Ok(None)
+            }
+        } else if csize > 8 {
+            let mut buf = vec![0; (csize as usize) - 8];
+                              br.read_buf(&mut buf)?;
+            Ok(Some(buf))
+        } else {
+            Ok(None)
+        }
+    } else {
+        Ok(None)
+    }
+}
+
 fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult<u64> {
     const KNOWN_STSD_SIZE: u64 = 24;
     validate!(size >= KNOWN_STSD_SIZE);
@@ -393,7 +476,7 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     let _flags              = br.read_u24be()?;
     let entries             = br.read_u32be()?;
     validate!(entries > 0);
-    let esize               = br.read_u32be()? as u64;
+    let esize               = u64::from(br.read_u32be()?);
     validate!(esize + 8 <= size);
     let mut fcc = [0u8; 4];
                               br.read_buf(&mut fcc)?;
@@ -421,12 +504,50 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
                                   br.read_skip(31)?; // actual compressor name
             let depth           = br.read_u16be()?;
             let ctable_id       = br.read_u16be()?;
-            validate!((depth <= 8) || (ctable_id == 0xFFFF));
+            let grayscale = depth > 0x20 || depth == 1;
+            let depth = if grayscale { depth & 0x1F } else { depth };
+            validate!(depth <= 8 || (ctable_id == 0xFFFF));
             if ctable_id == 0 {
                 let max_pal_size = start_pos + size - br.tell();
                 let mut pal = [0; 1024];
                 read_palette(br, max_pal_size, &mut pal)?;
                 track.pal = Some(Arc::new(pal));
+            } else if (depth <= 8) && !grayscale {
+                match depth & 0x1F {
+                    2 => {
+                        let mut pal = [0; 1024];
+                        (&mut pal[..4 * 4]).copy_from_slice(&MOV_DEFAULT_PAL_2BIT);
+                        track.pal = Some(Arc::new(pal));
+                    },
+                    4 => {
+                        let mut pal = [0; 1024];
+                        (&mut pal[..16 * 4]).copy_from_slice(&MOV_DEFAULT_PAL_4BIT);
+                        track.pal = Some(Arc::new(pal));
+                    },
+                    8 => {
+                        track.pal = Some(Arc::new(MOV_DEFAULT_PAL_8BIT));
+                    },
+                    _ => {},
+                };
+            } else if grayscale && ctable_id != 0xFFFF {
+                let mut pal = [0; 1024];
+                let cdepth = depth & 0x1F;
+                let size = 1 << cdepth;
+                for i in 0..size {
+                    let mut clr = ((size - 1 - i) as u8) << (8 - cdepth);
+                    let mut off = 8 - cdepth;
+                    while off >= cdepth {
+                        clr |= clr >> (8 - off);
+                        off -= cdepth;
+                    }
+                    if off > 0 {
+                        clr |= clr >> (8 - off);
+                    }
+                    pal[i * 4]     = clr;
+                    pal[i * 4 + 1] = clr;
+                    pal[i * 4 + 2] = clr;
+                }
+                track.pal = Some(Arc::new(pal));
             }
 // todo other atoms, put as extradata
             let cname = if let Some(name) = find_codec_from_mov_video_fourcc(&fcc) {
@@ -437,21 +558,22 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
                     "unknown"
                 };
             let format = if depth > 8 { RGB24_FORMAT } else { PAL8_FORMAT };
-            let vhdr = NAVideoInfo::new(width, height, false, format);
-            let edata;
-            if br.tell() - start_pos + 4 < size {
+            let mut vhdr = NAVideoInfo::new(width, height, false, format);
+            vhdr.bits = depth as u8;
+            let edata = if br.tell() - start_pos + 4 < size {
 //todo skip various common atoms
-                let edata_size  = br.read_u32be()? as usize;
-                let mut buf = vec![0; edata_size];
+                    let edata_size  = br.read_u32be()? as usize;
+                    validate!(edata_size >= 4);
+                    let mut buf = vec![0; edata_size - 4];
                                   br.read_buf(buf.as_mut_slice())?;
-                edata = Some(buf);
-            } else {
-                edata = None;
-            }
+                    Some(buf)
+                } else {
+                    None
+                };
             codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Video(vhdr), edata);
         },
         StreamType::Audio => {
-            let _ver            = br.read_u16be()?;
+            let sver            = br.read_u16be()?;
             let _revision       = br.read_u16le()?;
             let _vendor         = br.read_u32be()?;
             let nchannels       = br.read_u16be()?;
@@ -473,8 +595,18 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
 //todo adjust format for various PCM kinds
             let soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE);
             let block_align = 1;
+            if sver == 1 {
+                let samples_per_packet      = br.read_u32be()?;
+                let _bytes_per_packet       = br.read_u32be()?;
+                let bytes_per_frame         = br.read_u32be()?;
+                let _bytes_per_sample       = br.read_u32be()?;
+                track.bsize = bytes_per_frame as usize;
+                track.frame_samples = samples_per_packet as usize;
+            } else {
+                track.bsize = sample_size as usize;
+            }
             let ahdr = NAAudioInfo::new(sample_rate >> 16, nchannels as u8, soniton, block_align);
-            let edata = None;
+            let edata = parse_audio_edata(br, start_pos, size)?;
             codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata);
             track.channels  = nchannels as usize;
             track.bits      = sample_size as usize;
@@ -490,7 +622,7 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     };
     let read_size = br.tell() - start_pos;
     validate!(read_size <= size);
-    track.stream = Some(NAStream::new(track.stream_type, track.track_no, codec_info, 1, track.tb_den));
+    track.stream = Some(NAStream::new(track.stream_type, track.track_no, codec_info, 1, track.tb_den, u64::from(track.duration)));
     track.stsd_found = true;
     Ok(read_size)
 }
@@ -540,6 +672,9 @@ fn read_stsz(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     let sample_size         = br.read_u32be()?;
     if sample_size != 0 {
         track.sample_size = sample_size;
+        if track.sample_size != 1 || track.bsize == 0 {
+            track.bsize = sample_size as usize;
+        }
         Ok(8)
     } else {
         let entries             = br.read_u32be()? as usize;
@@ -584,6 +719,7 @@ struct Track {
     track_str_id:   usize,
     track_no:       u32,
     tb_den:         u32,
+    duration:       u32,
     depth:          u8,
     tkhd_found:     bool,
     stsd_found:     bool,
@@ -592,12 +728,14 @@ struct Track {
     height:         usize,
     channels:       usize,
     bits:           usize,
+    bsize:          usize,
     fcc:            [u8; 4],
     keyframes:      Vec<u32>,
     chunk_sizes:    Vec<u32>,
     chunk_offsets:  Vec<u64>,
     sample_map:     Vec<(u32, u32)>,
     sample_size:    u32,
+    frame_samples:  usize,
     stream:         Option<NAStream>,
     cur_chunk:      usize,
     cur_sample:     usize,
@@ -615,17 +753,20 @@ impl Track {
             track_str_id:   0,
             track_no,
             tb_den,
+            duration:       0,
             stream_type:    StreamType::None,
             width:          0,
             height:         0,
             channels:       0,
             bits:           0,
+            bsize:          0,
             fcc:            [0; 4],
             keyframes:      Vec::new(),
             chunk_sizes:    Vec::new(),
             chunk_offsets:  Vec::new(),
             sample_map:     Vec::new(),
             sample_size:    0,
+            frame_samples:  0,
             stream:         None,
             depth:          0,
             cur_chunk:      0,
@@ -640,7 +781,7 @@ impl Track {
     read_chunk_list!(track; "minf", read_minf, MINF_CHUNK_HANDLERS);
     read_chunk_list!(track; "stbl", read_stbl, STBL_CHUNK_HANDLERS);
     fn fill_seek_index(&self, seek_index: &mut SeekIndex) {
-        if self.keyframes.len() > 0 {
+        if !self.keyframes.is_empty() {
             seek_index.mode = SeekIndexMode::Present;
         }
         for kf_time in self.keyframes.iter() {
@@ -655,7 +796,7 @@ impl Track {
     }
     fn calculate_chunk_size(&self, nsamp: usize) -> usize {
         if nsamp == 0 {
-            self.sample_size as usize
+            self.bsize
         } else {
             match &self.fcc {
                 b"NONE" | b"raw " | b"twos" | b"sowt" => {
@@ -681,7 +822,7 @@ impl Track {
                 b"ms\x00\x21" => { //IMA ADPCM
                     (nsamp / 2 + 4) * self.channels
                 },
-                _ => self.sample_size as usize,
+                _ => self.bsize,
             }
         }
     }
@@ -716,6 +857,14 @@ impl Track {
             self.last_offset += size as u64;
             if self.stream_type == StreamType::Video {
                 self.samples_left -= 1;
+            } else if self.frame_samples != 0 && self.bsize != 0 {
+                let nblocks = size / self.bsize;
+                if nblocks > 0 {
+                    let consumed = (nblocks * self.frame_samples).min(self.samples_left);
+                    self.samples_left -= consumed;
+                } else {
+                    self.samples_left = 0;
+                }
             } else {
                 self.samples_left = 0;
             }
@@ -724,9 +873,9 @@ impl Track {
         }
     }
     fn get_size(&self, sample_no: usize) -> usize {
-        if self.chunk_sizes.len() > 0 {
+        if !self.chunk_sizes.is_empty() {
             self.chunk_sizes[sample_no] as usize
-        } else if self.sample_map.len() > 0 {
+        } else if !self.sample_map.is_empty() {
             let mut nsamp = 0;
             for (idx, samples) in self.sample_map.iter() {
                 if *idx as usize <= self.cur_chunk {
@@ -737,7 +886,7 @@ impl Track {
             }
             self.calculate_chunk_size(nsamp as usize)
         } else {
-            self.sample_size as usize
+            self.bsize
         }
     }
     fn seek(&mut self, pts: u64) {
@@ -745,7 +894,7 @@ impl Track {
         self.samples_left = 0;
         if self.stream_type == StreamType::Audio {
             self.cur_chunk = self.cur_sample;
-        } else if self.chunk_offsets.len() != self.chunk_sizes.len() && self.sample_map.len() > 0{
+        } else if self.chunk_offsets.len() != self.chunk_sizes.len() && !self.sample_map.is_empty() {
             let mut csamp = 0;
             self.cur_chunk = 0;
             let mut cmap = self.sample_map.iter();
@@ -780,7 +929,7 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
     fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> {
         self.read_root(strmgr)?;
         validate!(self.mdat_pos > 0);
-        validate!(self.tracks.len() > 0);
+        validate!(!self.tracks.is_empty());
         for track in self.tracks.iter() {
             track.fill_seek_index(seek_index);
         }
@@ -790,7 +939,7 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
     }
 
     fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
-        if self.tracks.len() == 0 {
+        if self.tracks.is_empty() {
             return Err(DemuxerError::EOF);
         }
         for _ in 0..self.tracks.len() {
@@ -813,10 +962,10 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
                 return Ok(pkt);
             }
         }
-        return Err(DemuxerError::EOF);
+        Err(DemuxerError::EOF)
     }
 
-    fn seek(&mut self, time: u64, seek_index: &SeekIndex) -> DemuxerResult<()> {
+    fn seek(&mut self, time: NATimePoint, seek_index: &SeekIndex) -> DemuxerResult<()> {
         let ret = seek_index.find_pos(time);
         if ret.is_none() {
             return Err(DemuxerError::SeekError);
@@ -827,6 +976,13 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
         }
         Ok(())
     }
+    fn get_duration(&self) -> u64 {
+        if self.tb_den != 0 {
+            u64::from(self.duration) * 1000 / u64::from(self.tb_den)
+        } else {
+            0
+        }
+    }
 }
 
 impl<'a> NAOptionHandler for MOVDemuxer<'a> {
@@ -885,6 +1041,289 @@ impl DemuxerCreator for MOVDemuxerCreator {
     fn get_name(&self) -> &'static str { "mov" }
 }
 
+const MOV_DEFAULT_PAL_2BIT: [u8; 4 * 4] = [
+    0x93, 0x65, 0x5E, 0x00,
+    0xFF, 0xFF, 0xFF, 0x00,
+    0xDF, 0xD0, 0xAB, 0x00,
+    0x00, 0x00, 0x00, 0x00
+];
+const MOV_DEFAULT_PAL_4BIT: [u8; 16 * 4] = [
+    0xFF, 0xFB, 0xFF, 0x00,
+    0xEF, 0xD9, 0xBB, 0x00,
+    0xE8, 0xC9, 0xB1, 0x00,
+    0x93, 0x65, 0x5E, 0x00,
+    0xFC, 0xDE, 0xE8, 0x00,
+    0x9D, 0x88, 0x91, 0x00,
+    0xFF, 0xFF, 0xFF, 0x00,
+    0xFF, 0xFF, 0xFF, 0x00,
+    0xFF, 0xFF, 0xFF, 0x00,
+    0x47, 0x48, 0x37, 0x00,
+    0x7A, 0x5E, 0x55, 0x00,
+    0xDF, 0xD0, 0xAB, 0x00,
+    0xFF, 0xFB, 0xF9, 0x00,
+    0xE8, 0xCA, 0xC5, 0x00,
+    0x8A, 0x7C, 0x77, 0x00,
+    0x00, 0x00, 0x00, 0x00
+];
+const MOV_DEFAULT_PAL_8BIT: [u8; 256 * 4] = [
+    0xFF, 0xFF, 0xFF, 0x00,
+    0xFF, 0xFF, 0xCC, 0x00,
+    0xFF, 0xFF, 0x99, 0x00,
+    0xFF, 0xFF, 0x66, 0x00,
+    0xFF, 0xFF, 0x33, 0x00,
+    0xFF, 0xFF, 0x00, 0x00,
+    0xFF, 0xCC, 0xFF, 0x00,
+    0xFF, 0xCC, 0xCC, 0x00,
+    0xFF, 0xCC, 0x99, 0x00,
+    0xFF, 0xCC, 0x66, 0x00,
+    0xFF, 0xCC, 0x33, 0x00,
+    0xFF, 0xCC, 0x00, 0x00,
+    0xFF, 0x99, 0xFF, 0x00,
+    0xFF, 0x99, 0xCC, 0x00,
+    0xFF, 0x99, 0x99, 0x00,
+    0xFF, 0x99, 0x66, 0x00,
+    0xFF, 0x99, 0x33, 0x00,
+    0xFF, 0x99, 0x00, 0x00,
+    0xFF, 0x66, 0xFF, 0x00,
+    0xFF, 0x66, 0xCC, 0x00,
+    0xFF, 0x66, 0x99, 0x00,
+    0xFF, 0x66, 0x66, 0x00,
+    0xFF, 0x66, 0x33, 0x00,
+    0xFF, 0x66, 0x00, 0x00,
+    0xFF, 0x33, 0xFF, 0x00,
+    0xFF, 0x33, 0xCC, 0x00,
+    0xFF, 0x33, 0x99, 0x00,
+    0xFF, 0x33, 0x66, 0x00,
+    0xFF, 0x33, 0x33, 0x00,
+    0xFF, 0x33, 0x00, 0x00,
+    0xFF, 0x00, 0xFF, 0x00,
+    0xFF, 0x00, 0xCC, 0x00,
+    0xFF, 0x00, 0x99, 0x00,
+    0xFF, 0x00, 0x66, 0x00,
+    0xFF, 0x00, 0x33, 0x00,
+    0xFF, 0x00, 0x00, 0x00,
+    0xCC, 0xFF, 0xFF, 0x00,
+    0xCC, 0xFF, 0xCC, 0x00,
+    0xCC, 0xFF, 0x99, 0x00,
+    0xCC, 0xFF, 0x66, 0x00,
+    0xCC, 0xFF, 0x33, 0x00,
+    0xCC, 0xFF, 0x00, 0x00,
+    0xCC, 0xCC, 0xFF, 0x00,
+    0xCC, 0xCC, 0xCC, 0x00,
+    0xCC, 0xCC, 0x99, 0x00,
+    0xCC, 0xCC, 0x66, 0x00,
+    0xCC, 0xCC, 0x33, 0x00,
+    0xCC, 0xCC, 0x00, 0x00,
+    0xCC, 0x99, 0xFF, 0x00,
+    0xCC, 0x99, 0xCC, 0x00,
+    0xCC, 0x99, 0x99, 0x00,
+    0xCC, 0x99, 0x66, 0x00,
+    0xCC, 0x99, 0x33, 0x00,
+    0xCC, 0x99, 0x00, 0x00,
+    0xCC, 0x66, 0xFF, 0x00,
+    0xCC, 0x66, 0xCC, 0x00,
+    0xCC, 0x66, 0x99, 0x00,
+    0xCC, 0x66, 0x66, 0x00,
+    0xCC, 0x66, 0x33, 0x00,
+    0xCC, 0x66, 0x00, 0x00,
+    0xCC, 0x33, 0xFF, 0x00,
+    0xCC, 0x33, 0xCC, 0x00,
+    0xCC, 0x33, 0x99, 0x00,
+    0xCC, 0x33, 0x66, 0x00,
+    0xCC, 0x33, 0x33, 0x00,
+    0xCC, 0x33, 0x00, 0x00,
+    0xCC, 0x00, 0xFF, 0x00,
+    0xCC, 0x00, 0xCC, 0x00,
+    0xCC, 0x00, 0x99, 0x00,
+    0xCC, 0x00, 0x66, 0x00,
+    0xCC, 0x00, 0x33, 0x00,
+    0xCC, 0x00, 0x00, 0x00,
+    0x99, 0xFF, 0xFF, 0x00,
+    0x99, 0xFF, 0xCC, 0x00,
+    0x99, 0xFF, 0x99, 0x00,
+    0x99, 0xFF, 0x66, 0x00,
+    0x99, 0xFF, 0x33, 0x00,
+    0x99, 0xFF, 0x00, 0x00,
+    0x99, 0xCC, 0xFF, 0x00,
+    0x99, 0xCC, 0xCC, 0x00,
+    0x99, 0xCC, 0x99, 0x00,
+    0x99, 0xCC, 0x66, 0x00,
+    0x99, 0xCC, 0x33, 0x00,
+    0x99, 0xCC, 0x00, 0x00,
+    0x99, 0x99, 0xFF, 0x00,
+    0x99, 0x99, 0xCC, 0x00,
+    0x99, 0x99, 0x99, 0x00,
+    0x99, 0x99, 0x66, 0x00,
+    0x99, 0x99, 0x33, 0x00,
+    0x99, 0x99, 0x00, 0x00,
+    0x99, 0x66, 0xFF, 0x00,
+    0x99, 0x66, 0xCC, 0x00,
+    0x99, 0x66, 0x99, 0x00,
+    0x99, 0x66, 0x66, 0x00,
+    0x99, 0x66, 0x33, 0x00,
+    0x99, 0x66, 0x00, 0x00,
+    0x99, 0x33, 0xFF, 0x00,
+    0x99, 0x33, 0xCC, 0x00,
+    0x99, 0x33, 0x99, 0x00,
+    0x99, 0x33, 0x66, 0x00,
+    0x99, 0x33, 0x33, 0x00,
+    0x99, 0x33, 0x00, 0x00,
+    0x99, 0x00, 0xFF, 0x00,
+    0x99, 0x00, 0xCC, 0x00,
+    0x99, 0x00, 0x99, 0x00,
+    0x99, 0x00, 0x66, 0x00,
+    0x99, 0x00, 0x33, 0x00,
+    0x99, 0x00, 0x00, 0x00,
+    0x66, 0xFF, 0xFF, 0x00,
+    0x66, 0xFF, 0xCC, 0x00,
+    0x66, 0xFF, 0x99, 0x00,
+    0x66, 0xFF, 0x66, 0x00,
+    0x66, 0xFF, 0x33, 0x00,
+    0x66, 0xFF, 0x00, 0x00,
+    0x66, 0xCC, 0xFF, 0x00,
+    0x66, 0xCC, 0xCC, 0x00,
+    0x66, 0xCC, 0x99, 0x00,
+    0x66, 0xCC, 0x66, 0x00,
+    0x66, 0xCC, 0x33, 0x00,
+    0x66, 0xCC, 0x00, 0x00,
+    0x66, 0x99, 0xFF, 0x00,
+    0x66, 0x99, 0xCC, 0x00,
+    0x66, 0x99, 0x99, 0x00,
+    0x66, 0x99, 0x66, 0x00,
+    0x66, 0x99, 0x33, 0x00,
+    0x66, 0x99, 0x00, 0x00,
+    0x66, 0x66, 0xFF, 0x00,
+    0x66, 0x66, 0xCC, 0x00,
+    0x66, 0x66, 0x99, 0x00,
+    0x66, 0x66, 0x66, 0x00,
+    0x66, 0x66, 0x33, 0x00,
+    0x66, 0x66, 0x00, 0x00,
+    0x66, 0x33, 0xFF, 0x00,
+    0x66, 0x33, 0xCC, 0x00,
+    0x66, 0x33, 0x99, 0x00,
+    0x66, 0x33, 0x66, 0x00,
+    0x66, 0x33, 0x33, 0x00,
+    0x66, 0x33, 0x00, 0x00,
+    0x66, 0x00, 0xFF, 0x00,
+    0x66, 0x00, 0xCC, 0x00,
+    0x66, 0x00, 0x99, 0x00,
+    0x66, 0x00, 0x66, 0x00,
+    0x66, 0x00, 0x33, 0x00,
+    0x66, 0x00, 0x00, 0x00,
+    0x33, 0xFF, 0xFF, 0x00,
+    0x33, 0xFF, 0xCC, 0x00,
+    0x33, 0xFF, 0x99, 0x00,
+    0x33, 0xFF, 0x66, 0x00,
+    0x33, 0xFF, 0x33, 0x00,
+    0x33, 0xFF, 0x00, 0x00,
+    0x33, 0xCC, 0xFF, 0x00,
+    0x33, 0xCC, 0xCC, 0x00,
+    0x33, 0xCC, 0x99, 0x00,
+    0x33, 0xCC, 0x66, 0x00,
+    0x33, 0xCC, 0x33, 0x00,
+    0x33, 0xCC, 0x00, 0x00,
+    0x33, 0x99, 0xFF, 0x00,
+    0x33, 0x99, 0xCC, 0x00,
+    0x33, 0x99, 0x99, 0x00,
+    0x33, 0x99, 0x66, 0x00,
+    0x33, 0x99, 0x33, 0x00,
+    0x33, 0x99, 0x00, 0x00,
+    0x33, 0x66, 0xFF, 0x00,
+    0x33, 0x66, 0xCC, 0x00,
+    0x33, 0x66, 0x99, 0x00,
+    0x33, 0x66, 0x66, 0x00,
+    0x33, 0x66, 0x33, 0x00,
+    0x33, 0x66, 0x00, 0x00,
+    0x33, 0x33, 0xFF, 0x00,
+    0x33, 0x33, 0xCC, 0x00,
+    0x33, 0x33, 0x99, 0x00,
+    0x33, 0x33, 0x66, 0x00,
+    0x33, 0x33, 0x33, 0x00,
+    0x33, 0x33, 0x00, 0x00,
+    0x33, 0x00, 0xFF, 0x00,
+    0x33, 0x00, 0xCC, 0x00,
+    0x33, 0x00, 0x99, 0x00,
+    0x33, 0x00, 0x66, 0x00,
+    0x33, 0x00, 0x33, 0x00,
+    0x33, 0x00, 0x00, 0x00,
+    0x00, 0xFF, 0xFF, 0x00,
+    0x00, 0xFF, 0xCC, 0x00,
+    0x00, 0xFF, 0x99, 0x00,
+    0x00, 0xFF, 0x66, 0x00,
+    0x00, 0xFF, 0x33, 0x00,
+    0x00, 0xFF, 0x00, 0x00,
+    0x00, 0xCC, 0xFF, 0x00,
+    0x00, 0xCC, 0xCC, 0x00,
+    0x00, 0xCC, 0x99, 0x00,
+    0x00, 0xCC, 0x66, 0x00,
+    0x00, 0xCC, 0x33, 0x00,
+    0x00, 0xCC, 0x00, 0x00,
+    0x00, 0x99, 0xFF, 0x00,
+    0x00, 0x99, 0xCC, 0x00,
+    0x00, 0x99, 0x99, 0x00,
+    0x00, 0x99, 0x66, 0x00,
+    0x00, 0x99, 0x33, 0x00,
+    0x00, 0x99, 0x00, 0x00,
+    0x00, 0x66, 0xFF, 0x00,
+    0x00, 0x66, 0xCC, 0x00,
+    0x00, 0x66, 0x99, 0x00,
+    0x00, 0x66, 0x66, 0x00,
+    0x00, 0x66, 0x33, 0x00,
+    0x00, 0x66, 0x00, 0x00,
+    0x00, 0x33, 0xFF, 0x00,
+    0x00, 0x33, 0xCC, 0x00,
+    0x00, 0x33, 0x99, 0x00,
+    0x00, 0x33, 0x66, 0x00,
+    0x00, 0x33, 0x33, 0x00,
+    0x00, 0x33, 0x00, 0x00,
+    0x00, 0x00, 0xFF, 0x00,
+    0x00, 0x00, 0xCC, 0x00,
+    0x00, 0x00, 0x99, 0x00,
+    0x00, 0x00, 0x66, 0x00,
+    0x00, 0x00, 0x33, 0x00,
+    0xEE, 0x00, 0x00, 0x00,
+    0xDD, 0x00, 0x00, 0x00,
+    0xBB, 0x00, 0x00, 0x00,
+    0xAA, 0x00, 0x00, 0x00,
+    0x88, 0x00, 0x00, 0x00,
+    0x77, 0x00, 0x00, 0x00,
+    0x55, 0x00, 0x00, 0x00,
+    0x44, 0x00, 0x00, 0x00,
+    0x22, 0x00, 0x00, 0x00,
+    0x11, 0x00, 0x00, 0x00,
+    0x00, 0xEE, 0x00, 0x00,
+    0x00, 0xDD, 0x00, 0x00,
+    0x00, 0xBB, 0x00, 0x00,
+    0x00, 0xAA, 0x00, 0x00,
+    0x00, 0x88, 0x00, 0x00,
+    0x00, 0x77, 0x00, 0x00,
+    0x00, 0x55, 0x00, 0x00,
+    0x00, 0x44, 0x00, 0x00,
+    0x00, 0x22, 0x00, 0x00,
+    0x00, 0x11, 0x00, 0x00,
+    0x00, 0x00, 0xEE, 0x00,
+    0x00, 0x00, 0xDD, 0x00,
+    0x00, 0x00, 0xBB, 0x00,
+    0x00, 0x00, 0xAA, 0x00,
+    0x00, 0x00, 0x88, 0x00,
+    0x00, 0x00, 0x77, 0x00,
+    0x00, 0x00, 0x55, 0x00,
+    0x00, 0x00, 0x44, 0x00,
+    0x00, 0x00, 0x22, 0x00,
+    0x00, 0x00, 0x11, 0x00,
+    0xEE, 0xEE, 0xEE, 0x00,
+    0xDD, 0xDD, 0xDD, 0x00,
+    0xBB, 0xBB, 0xBB, 0x00,
+    0xAA, 0xAA, 0xAA, 0x00,
+    0x88, 0x88, 0x88, 0x00,
+    0x77, 0x77, 0x77, 0x00,
+    0x55, 0x55, 0x55, 0x00,
+    0x44, 0x44, 0x44, 0x00,
+    0x22, 0x22, 0x22, 0x00,
+    0x11, 0x11, 0x11, 0x00,
+    0x00, 0x00, 0x00, 0x00
+];
+
 #[cfg(test)]
 mod test {
     use super::*;