X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-commonfmt%2Fsrc%2Fdemuxers%2Fmov.rs;h=95e6d599e6d05866d54fa6ec449a1c16f28a0a4d;hb=9efec4edd8d222e8a8fb91a61a955c4b3c4e3382;hp=b7aecd52af18ed0e3c9a50adea471391466d117f;hpb=58cd51fa50481321712f1e22c91e725ffd2e3607;p=nihav.git diff --git a/nihav-commonfmt/src/demuxers/mov.rs b/nihav-commonfmt/src/demuxers/mov.rs index b7aecd5..95e6d59 100644 --- a/nihav-commonfmt/src/demuxers/mov.rs +++ b/nihav-commonfmt/src/demuxers/mov.rs @@ -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 { + 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 { let mut pal = [0; 1024]; let size = read_palette(&mut dmx.src, size, &mut pal)?; @@ -307,7 +348,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 +425,39 @@ 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>> { + let read_part = br.tell() - start_pos; + if read_part + 8 < size { + 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 { const KNOWN_STSD_SIZE: u64 = 24; validate!(size >= KNOWN_STSD_SIZE); @@ -393,7 +467,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 +495,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 +549,21 @@ 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; + let mut buf = vec![0; edata_size]; 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 +585,17 @@ 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; + } 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; @@ -540,6 +661,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; @@ -592,6 +716,7 @@ struct Track { height: usize, channels: usize, bits: usize, + bsize: usize, fcc: [u8; 4], keyframes: Vec, chunk_sizes: Vec, @@ -620,6 +745,7 @@ impl Track { height: 0, channels: 0, bits: 0, + bsize: 0, fcc: [0; 4], keyframes: Vec::new(), chunk_sizes: Vec::new(), @@ -640,7 +766,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 +781,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 +807,7 @@ impl Track { b"ms\x00\x21" => { //IMA ADPCM (nsamp / 2 + 4) * self.channels }, - _ => self.sample_size as usize, + _ => self.bsize, } } } @@ -724,9 +850,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 +863,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 +871,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 +906,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 +916,7 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { } fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { - if self.tracks.len() == 0 { + if self.tracks.is_empty() { return Err(DemuxerError::EOF); } for _ in 0..self.tracks.len() { @@ -813,7 +939,7 @@ 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<()> { @@ -829,6 +955,12 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { } } +impl<'a> NAOptionHandler for MOVDemuxer<'a> { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + impl<'a> MOVDemuxer<'a> { fn new(io: &'a mut ByteReader<'a>) -> Self { MOVDemuxer { @@ -879,6 +1011,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::*;