X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-commonfmt%2Fsrc%2Fdemuxers%2Fmov.rs;h=35fac0d6a112787cd0cea80d2bc72ed6fc91a0bf;hb=fbf1f900e6e6da46026938f90f1eaba2034aa79a;hp=80054ed0b88a68c46f674af6c61edbdfc438f432;hpb=7e19285f3ddeb71a82802fd878b7914e0e348e5c;p=nihav.git diff --git a/nihav-commonfmt/src/demuxers/mov.rs b/nihav-commonfmt/src/demuxers/mov.rs index 80054ed..35fac0d 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) => ({ @@ -40,22 +41,21 @@ fn read_chunk_header(br: &mut ByteReader) -> DemuxerResult<(u32, u64)> { } } -fn read_palette(br: &mut ByteReader, size: u64, pal: &mut Vec) -> DemuxerResult { +fn read_palette(br: &mut ByteReader, size: u64, pal: &mut [u8; 1024]) -> DemuxerResult { let _seed = br.read_u32be()?; let _flags = br.read_u16be()?; let palsize = (br.read_u16be()? as usize) + 1; validate!(palsize <= 256); validate!((palsize as u64) * 8 + 8 == size); - pal.resize(palsize * 4, 0); for i in 0..palsize { let a = br.read_u16be()?; let r = br.read_u16be()?; let g = br.read_u16be()?; let b = br.read_u16be()?; - pal[i * 4] = a; - pal[i * 4 + 1] = r; - pal[i * 4 + 2] = g; - pal[i * 4 + 3] = b; + pal[i * 4] = (r >> 8) as u8; + pal[i * 4 + 1] = (g >> 8) as u8; + pal[i * 4 + 2] = (b >> 8) as u8; + pal[i * 4 + 3] = (a >> 8) as u8; } Ok(size) } @@ -170,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 }, @@ -203,8 +204,52 @@ 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())?; + validate!(sbuf[0] == 0x78); + validate!(sbuf[1] == 0x9C); + let ret = Inflate::uncompress(&sbuf[2..], 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 { - read_palette(&mut dmx.src, size, &mut dmx.pal) + let mut pal = [0; 1024]; + let size = read_palette(&mut dmx.src, size, &mut pal)?; + dmx.pal = Some(Arc::new(pal)); + Ok(size) } fn read_meta(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult { @@ -305,7 +350,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) } @@ -391,7 +436,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)?; @@ -419,10 +464,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(); - read_palette(br, max_pal_size, &mut track.pal)?; + 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) { @@ -433,17 +518,17 @@ 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 => { @@ -572,7 +657,7 @@ struct MOVDemuxer<'a> { cur_track: usize, tb_den: u32, duration: u32, - pal: Vec, + pal: Option>, } struct Track { @@ -599,7 +684,7 @@ struct Track { cur_sample: usize, samples_left: usize, last_offset: u64, - pal: Vec, + pal: Option>, } impl Track { @@ -628,7 +713,7 @@ impl Track { cur_sample: 0, samples_left: 0, last_offset: 0, - pal: Vec::new(), + pal: None, } } read_chunk_list!(track; "trak", read_trak, TRAK_CHUNK_HANDLERS); @@ -636,7 +721,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() { @@ -720,9 +805,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 { @@ -741,7 +826,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(); @@ -776,7 +861,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); } @@ -786,7 +871,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() { @@ -795,16 +880,21 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { } let track = &mut self.tracks[self.cur_track]; self.cur_track += 1; + let first = track.cur_sample == 0; if let Some((pts, offset, size)) = track.get_next_chunk() { let str = strmgr.get_stream(track.track_str_id); if str.is_none() { return Err(DemuxerError::InvalidData); } let stream = str.unwrap(); self.src.seek(SeekFrom::Start(offset))?; - let pkt = self.src.read_packet(stream, pts, false, size)?; + let mut pkt = self.src.read_packet(stream, pts, false, size)?; + if let Some(ref pal) = track.pal { + let side_data = NASideData::Palette(first, pal.clone()); + pkt.add_side_data(side_data); + } return Ok(pkt); } } - return Err(DemuxerError::EOF); + Err(DemuxerError::EOF) } fn seek(&mut self, time: u64, seek_index: &SeekIndex) -> DemuxerResult<()> { @@ -820,6 +910,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 { @@ -831,7 +927,7 @@ impl<'a> MOVDemuxer<'a> { cur_track: 0, tb_den: 0, duration: 0, - pal: Vec::new(), + pal: None, } } fn read_root(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> { @@ -870,6 +966,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::*;