add LinePack decoder
[nihav.git] / nihav-game / src / codecs / beam.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3
4 const VB_FLAG_GMC: u16 = 0x0001;
5 const VB_FLAG_AUDIO: u16 = 0x0004;
6 const VB_FLAG_VIDEO: u16 = 0x0008;
7 const VB_FLAG_PALETTE: u16 = 0x0010;
8
9 const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
10 comp_info: [
11 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
12 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 1, next_elem: 2 }),
13 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 2, next_elem: 2 }),
14 None, None],
15 elem_size: 2, be: false, alpha: false, palette: false };
16
17 fn check_size(x: usize, row_no: usize, dx: i16, dy: i16, stride: usize, len: usize) -> Option<usize> {
18 let start = (x as i32) + i32::from(dx) + ((row_no * 4) as i32 + i32::from(dy)) * (stride as i32);
19 if start >= 0 {
20 let start = start as usize;
21 let end = start + 4 + stride * 3;
22
23 if end <= len {
24 Some(start)
25 } else {
26 None
27 }
28 } else {
29 None
30 }
31 }
32
33 fn decode_video8(br: &mut ByteReader, dst: &mut [u8], prev_frame: &[u8], width: usize, gmv: [i16; 2]) -> DecoderResult<FrameType> {
34 let mut ftype = FrameType::I;
35
36 let mut btc = 0;
37 let mut btypes = 0;
38 for (row_no, strip) in dst.chunks_mut(width * 4).enumerate() {
39 for x in (0..width).step_by(4) {
40 if btc == 0 {
41 btypes = br.read_byte()?;
42 btc = 4;
43 }
44 match btypes & 0xC0 {
45 0xC0 => {
46 let t = br.read_byte()?;
47 let mut pattern = VB_PATTERNS[(t & 0x3F) as usize];
48 let op = t >> 6;
49 validate!(op != 3);
50 if op == 0 {
51 let mut clr = [0; 2];
52 br.read_buf(&mut clr)?;
53 for dline in strip[x..].chunks_mut(width).take(4) {
54 for el in dline[..4].iter_mut() {
55 *el = clr[(pattern & 1) as usize];
56 pattern >>= 1;
57 }
58 }
59 } else {
60 if op == 2 {
61 pattern = !pattern;
62 }
63 let clr = br.read_byte()?;
64
65 if let Some(start) = check_size(x, row_no, gmv[0], gmv[1], width, prev_frame.len()) {
66 for (dline, sline) in strip[x..].chunks_mut(width).zip(prev_frame[start..].chunks(width)).take(4) {
67 for (dst, &src) in dline[..4].iter_mut().zip(sline.iter()) {
68 *dst = if (pattern & 1) != 0 { clr } else { src };
69 pattern >>= 1;
70 }
71 }
72 } else {
73 return Err(DecoderError::InvalidData);
74 }
75
76 ftype = FrameType::P;
77 }
78 },
79 0x80 => {
80 let clr = br.read_byte()?;
81 for dline in strip[x..].chunks_mut(width).take(4) {
82 for el in dline[..4].iter_mut() {
83 *el = clr;
84 }
85 }
86 },
87 0x40 => {
88 let mv = br.read_byte()?;
89 if mv == 0 {
90 for dline in strip[x..].chunks_mut(width).take(4) {
91 br.read_buf(&mut dline[..4])?;
92 }
93 } else {
94 let mvx = (((mv & 0xF) ^ 8) as i16) - 8;
95 let mvy = (((mv >> 4) ^ 8) as i16) - 8;
96 if let Some(start) = check_size(x, row_no, gmv[0] + mvx, gmv[1] + mvy, width, prev_frame.len()) {
97 for (dline, sline) in strip[x..].chunks_mut(width).zip(prev_frame[start..].chunks(width)).take(4) {
98 dline[..4].copy_from_slice(&sline[..4]);
99 }
100 } else {
101 return Err(DecoderError::InvalidData);
102 }
103 ftype = FrameType::P;
104 }
105 },
106 _ => {
107 if let Some(start) = check_size(x, row_no, gmv[0], gmv[1], width, prev_frame.len()) {
108 for (dline, sline) in strip[x..].chunks_mut(width).zip(prev_frame[start..].chunks(width)).take(4) {
109 dline[..4].copy_from_slice(&sline[..4]);
110 }
111 } else {
112 return Err(DecoderError::InvalidData);
113 }
114 ftype = FrameType::P;
115 },
116 }
117
118 btypes <<= 2;
119 btc -= 1;
120 }
121 }
122
123 Ok(ftype)
124 }
125
126 struct OldData {
127 pal: [u8; 768],
128 prev_frame: Vec<u8>,
129 cur_frame: Vec<u8>,
130 width: usize,
131 flags: u16,
132 }
133
134 impl OldData {
135 fn new(w: usize, h: usize, flags: u16) -> Self {
136 Self {
137 pal: [0; 768],
138 prev_frame: vec![0; w * h],
139 cur_frame: vec![0; w * h],
140 width: w,
141 flags,
142 }
143 }
144 fn decode(&mut self, br: &mut ByteReader, vbuf: &mut NAVideoBufferRef<u8>) -> DecoderResult<FrameType> {
145 let asize = br.read_u16le()? as usize;
146 let _smth = br.read_u16le()? as usize;
147 let pal_size = br.read_u16le()? as usize;
148 if pal_size > 0 {
149 let pal_start = br.read_u16le()? as usize;
150 validate!(pal_start + pal_size <= 256);
151 br.read_buf(&mut self.pal[pal_start * 3..][..pal_size * 3])?;
152 }
153 br.read_skip(asize)?;
154
155 let (gmv_x, gmv_y) = if (self.flags & 0x2000) != 0 {
156 let mvx = br.read_u16le()? as i16;
157 let mvy = br.read_u16le()? as i16;
158 (mvx, mvy)
159 } else {
160 (0, 0)
161 };
162
163 std::mem::swap(&mut self.cur_frame, &mut self.prev_frame);
164
165 let ftype = decode_video8(br, &mut self.cur_frame, &self.prev_frame, self.width, [gmv_x, gmv_y])?;
166
167 let stride = vbuf.get_stride(0);
168 let pal_off = vbuf.get_offset(1);
169 let data = vbuf.get_data_mut().unwrap();
170
171 for (dline, sline) in data.chunks_mut(stride).zip(self.cur_frame.chunks(self.width)) {
172 dline[..self.width].copy_from_slice(sline);
173 }
174 data[pal_off..][..768].copy_from_slice(&self.pal);
175
176 Ok(ftype)
177 }
178 }
179
180 struct Data8 {
181 pal: [u8; 768],
182 prev_frame: Vec<u8>,
183 cur_frame: Vec<u8>,
184 width: usize,
185 }
186
187 impl Data8 {
188 fn new(w: usize, h: usize) -> Self {
189 Self {
190 pal: [0; 768],
191 prev_frame: vec![0; w * h],
192 cur_frame: vec![0; w * h],
193 width: w,
194 }
195 }
196 fn decode(&mut self, br: &mut ByteReader, vbuf: &mut NAVideoBufferRef<u8>) -> DecoderResult<FrameType> {
197 let flags = br.read_u16le()?;
198 let (gmv_x, gmv_y) = if (flags & VB_FLAG_GMC) != 0 {
199 let mvx = br.read_u16le()? as i16;
200 let mvy = br.read_u16le()? as i16;
201 (mvx, mvy)
202 } else {
203 (0, 0)
204 };
205 if (flags & VB_FLAG_AUDIO) != 0 {
206 let asize = br.read_u32le()? as usize;
207 validate!(asize >= 4 && asize <= (br.left() as usize));
208 br.read_skip(asize - 4)?;
209 }
210 let mut ftype = FrameType::Skip;
211 if (flags & VB_FLAG_VIDEO) != 0 {
212 std::mem::swap(&mut self.cur_frame, &mut self.prev_frame);
213
214 let vsize = br.read_u32le()? as u64;
215 validate!(vsize > 4);
216 let end = br.tell() + vsize - 4;
217
218 ftype = decode_video8(br, &mut self.cur_frame, &self.prev_frame, self.width, [gmv_x, gmv_y])?;
219
220 validate!(br.tell() == end);
221 }
222 if (flags & VB_FLAG_PALETTE) != 0 {
223 let pal_size = br.read_u32le()? as usize;
224 validate!(pal_size > 6);
225 validate!(br.left() as usize >= pal_size - 4);
226 let start = br.read_byte()? as usize;
227 let mut size = br.read_byte()? as usize;
228 if size == 0 {
229 size = 256;
230 }
231 validate!(start + size <= 256);
232 validate!(size * 3 + 6 == pal_size);
233 br.read_buf(&mut self.pal[start * 3..][..size * 3])?;
234 }
235
236 let stride = vbuf.get_stride(0);
237 let pal_off = vbuf.get_offset(1);
238 let data = vbuf.get_data_mut().unwrap();
239
240 for (dline, sline) in data.chunks_mut(stride).zip(self.cur_frame.chunks(self.width)) {
241 dline[..self.width].copy_from_slice(sline);
242 }
243 data[pal_off..][..768].copy_from_slice(&self.pal);
244
245 Ok(ftype)
246 }
247 }
248
249 struct Data16 {
250 prev_frame: Vec<u16>,
251 cur_frame: Vec<u16>,
252 pal: [u16; 256],
253 width: usize,
254 }
255
256 impl Data16 {
257 fn new(w: usize, h: usize) -> Self {
258 Self {
259 prev_frame: vec![0; w * h],
260 cur_frame: vec![0; w * h],
261 pal: [0; 256],
262 width: w,
263 }
264 }
265 fn decode(&mut self, br: &mut ByteReader, vbuf: &mut NAVideoBufferRef<u16>) -> DecoderResult<FrameType> {
266 let flags = br.read_u16le()?;
267 let (gmv_x, gmv_y) = if (flags & VB_FLAG_GMC) != 0 {
268 let mvx = br.read_u16le()? as i16;
269 let mvy = br.read_u16le()? as i16;
270 (mvx, mvy)
271 } else {
272 (0, 0)
273 };
274 if (flags & VB_FLAG_AUDIO) != 0 {
275 let asize = br.read_u32le()? as usize;
276 validate!(asize <= (br.left() as usize));
277 br.read_skip(asize - 4)?;
278 }
279 let mut ftype = FrameType::Skip;
280 if (flags & VB_FLAG_VIDEO) != 0 {
281 let vsize = br.read_u32le()? as u64;
282 validate!(vsize > 4);
283 let end = br.tell() + vsize - 4;
284 let has_pal = (flags & VB_FLAG_PALETTE) != 0;
285 if has_pal {
286 let cur_off = br.tell();
287 br.seek(SeekFrom::Current((vsize - 4) as i64))?;
288 let psize = br.read_u32le()? as usize;
289 validate!(psize > 4 && psize <= 0x204 && (psize & 1) == 0);
290 for el in self.pal[..(psize - 4)/ 2].iter_mut() {
291 *el = br.read_u16le()?;
292 }
293 br.seek(SeekFrom::Start(cur_off))?;
294 }
295
296 std::mem::swap(&mut self.cur_frame, &mut self.prev_frame);
297
298 let mut btc = 0;
299 let mut btypes = 0;
300 ftype = FrameType::I;
301 for (row_no, strip) in self.cur_frame.chunks_mut(self.width * 4).enumerate() {
302 for x in (0..self.width).step_by(4) {
303 if btc == 0 {
304 btypes = br.read_byte()?;
305 btc = 4;
306 }
307 match btypes & 0xC0 {
308 0xC0 => {
309 let t = br.read_byte()?;
310 let mut pattern = VB_PATTERNS[(t & 0x3F) as usize];
311 let op = t >> 6;
312 validate!(op != 3);
313 if op == 0 {
314 let mut clr = [0; 2];
315 if has_pal {
316 clr[0] = self.pal[br.read_byte()? as usize];
317 clr[1] = self.pal[br.read_byte()? as usize];
318 } else {
319 clr[0] = br.read_u16le()?;
320 clr[1] = br.read_u16le()?;
321 }
322 for dline in strip[x..].chunks_mut(self.width).take(4) {
323 for el in dline[..4].iter_mut() {
324 *el = clr[(pattern & 1) as usize];
325 pattern >>= 1;
326 }
327 }
328 } else {
329 if op == 2 {
330 pattern = !pattern;
331 }
332 let clr = if has_pal {
333 self.pal[br.read_byte()? as usize]
334 } else {
335 br.read_u16le()?
336 };
337
338 if let Some(start) = check_size(x, row_no, gmv_x, gmv_y, self.width, self.prev_frame.len()) {
339 for (dline, sline) in strip[x..].chunks_mut(self.width).zip(self.prev_frame[start..].chunks(self.width)).take(4) {
340 for (dst, &src) in dline[..4].iter_mut().zip(sline.iter()) {
341 *dst = if (pattern & 1) != 0 { clr } else { src };
342 pattern >>= 1;
343 }
344 }
345 } else {
346 return Err(DecoderError::InvalidData);
347 }
348 ftype = FrameType::P;
349 }
350 },
351 0x80 => {
352 let clr = if has_pal {
353 self.pal[br.read_byte()? as usize]
354 } else {
355 br.read_u16le()?
356 };
357 for dline in strip[x..].chunks_mut(self.width).take(4) {
358 for el in dline[..4].iter_mut() {
359 *el = clr;
360 }
361 }
362 },
363 0x40 => {
364 let mv = br.read_byte()?;
365 if mv == 0 {
366 if has_pal {
367 for dline in strip[x..].chunks_mut(self.width).take(4) {
368 for el in dline[..4].iter_mut() {
369 *el = self.pal[br.read_byte()? as usize];
370 }
371 }
372 } else {
373 for dline in strip[x..].chunks_mut(self.width).take(4) {
374 for el in dline[..4].iter_mut() {
375 *el = br.read_u16le()?;
376 }
377 }
378 }
379 } else {
380 let mvx = (((mv & 0xF) ^ 8) as i16) - 8;
381 let mvy = (((mv >> 4) ^ 8) as i16) - 8;
382 if let Some(start) = check_size(x, row_no, gmv_x + mvx, gmv_y + mvy, self.width, self.prev_frame.len()) {
383 for (dline, sline) in strip[x..].chunks_mut(self.width).zip(self.prev_frame[start..].chunks(self.width)).take(4) {
384 dline[..4].copy_from_slice(&sline[..4]);
385 }
386 } else {
387 return Err(DecoderError::InvalidData);
388 }
389 ftype = FrameType::P;
390 }
391 },
392 _ => {
393 if let Some(start) = check_size(x, row_no, gmv_x, gmv_y, self.width, self.prev_frame.len()) {
394 for (dline, sline) in strip[x..].chunks_mut(self.width).zip(self.prev_frame[start..].chunks(self.width)).take(4) {
395 dline[..4].copy_from_slice(&sline[..4]);
396 }
397 } else {
398 return Err(DecoderError::InvalidData);
399 }
400 ftype = FrameType::P;
401 },
402 }
403
404 btypes <<= 2;
405 btc -= 1;
406 }
407 }
408 validate!(br.tell() == end);
409 }
410 if (flags & VB_FLAG_PALETTE) != 0 {
411 let psize = br.read_u32le()? as usize;
412 br.read_skip(psize - 4)?;
413 }
414
415 let stride = vbuf.get_stride(0);
416 let data = vbuf.get_data_mut().unwrap();
417
418 for (dline, sline) in data.chunks_mut(stride).zip(self.cur_frame.chunks(self.width)) {
419 dline[..self.width].copy_from_slice(sline);
420 }
421
422 Ok(ftype)
423 }
424 }
425
426 enum FrameData {
427 Old(OldData),
428 Pal(Data8),
429 New(Data16),
430 None,
431 }
432
433 struct BeamVideoDecoder {
434 info: NACodecInfoRef,
435 data: FrameData,
436 fcp: bool,
437 }
438
439 impl BeamVideoDecoder {
440 fn new(fcp: bool) -> Self {
441 Self {
442 info: NACodecInfoRef::default(),
443 data: FrameData::None,
444 fcp,
445 }
446 }
447 }
448
449 impl NADecoder for BeamVideoDecoder {
450 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
451 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
452 let width = vinfo.get_width();
453 let height = vinfo.get_height();
454 validate!((width & 3) == 0 && (height & 3) == 0);
455 let fmt = match vinfo.bits {
456 _ if self.fcp => {
457 let edata = info.get_extradata();
458 validate!(edata.is_some());
459 let edata = edata.unwrap();
460 validate!(edata.len() >= 2);
461 let flags = read_u16le(&edata)?;
462 self.data = FrameData::Old(OldData::new(width, height, flags));
463 PAL8_FORMAT
464 },
465 8 => {
466 self.data = FrameData::Pal(Data8::new(width, height));
467 PAL8_FORMAT
468 },
469 16 => {
470 self.data = FrameData::New(Data16::new(width, height));
471 RGB555_FORMAT
472 },
473 _ => return Err(DecoderError::InvalidData),
474 };
475
476 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, fmt));
477 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
478
479 Ok(())
480 } else {
481 Err(DecoderError::InvalidData)
482 }
483 }
484 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
485 let src = pkt.get_buffer();
486 validate!(src.len() > 1);
487
488 let mut mr = MemoryReader::new_read(&src);
489 let mut br = ByteReader::new(&mut mr);
490
491 let mut bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?;
492
493 let ftype = match (&mut self.data, &mut bufinfo) {
494 (FrameData::Old(ref mut data), NABufferType::Video(ref mut vbuf)) => data.decode(&mut br, vbuf)?,
495 (FrameData::Pal(ref mut data), NABufferType::Video(ref mut vbuf)) => data.decode(&mut br, vbuf)?,
496 (FrameData::New(ref mut data), NABufferType::Video16(ref mut vbuf)) => data.decode(&mut br, vbuf)?,
497 _ => unreachable!(),
498 };
499
500 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
501 frm.set_keyframe(ftype == FrameType::I);
502 frm.set_frame_type(ftype);
503 Ok(frm.into_ref())
504 }
505 fn flush(&mut self) {
506 }
507 }
508
509 impl NAOptionHandler for BeamVideoDecoder {
510 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
511 fn set_options(&mut self, _options: &[NAOption]) { }
512 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
513 }
514
515 pub fn get_decoder_vbv() -> Box<dyn NADecoder + Send> {
516 Box::new(BeamVideoDecoder::new(false))
517 }
518
519 pub fn get_decoder_fcp() -> Box<dyn NADecoder + Send> {
520 Box::new(BeamVideoDecoder::new(true))
521 }
522
523 #[cfg(test)]
524 mod test {
525 use nihav_core::codecs::RegisteredDecoders;
526 use nihav_core::demuxers::RegisteredDemuxers;
527 use nihav_codec_support::test::dec_video::*;
528 use crate::game_register_all_decoders;
529 use crate::game_register_all_demuxers;
530 #[test]
531 fn test_beam_fcp() {
532 let mut dmx_reg = RegisteredDemuxers::new();
533 game_register_all_demuxers(&mut dmx_reg);
534 let mut dec_reg = RegisteredDecoders::new();
535 game_register_all_decoders(&mut dec_reg);
536
537 // sample from The Dame was Loaded
538 test_decoding("siff", "beam-fcp", "assets/Game/siff/BEAM.FCP", Some(1000), &dmx_reg, &dec_reg,
539 ExpectedTestResult::MD5Frames(vec![
540 [0xa31342c0, 0x7afe5a76, 0x4111f17a, 0x30ec3a18],
541 [0xa31342c0, 0x7afe5a76, 0x4111f17a, 0x30ec3a18],
542 [0xa31342c0, 0x7afe5a76, 0x4111f17a, 0x30ec3a18],
543 [0xa31342c0, 0x7afe5a76, 0x4111f17a, 0x30ec3a18],
544 [0xa31342c0, 0x7afe5a76, 0x4111f17a, 0x30ec3a18],
545 [0x447870bf, 0x9bb22dab, 0x1f557dae, 0xc41575ad],
546 [0xeb3bd749, 0xc72811d3, 0x23d430b2, 0xc31dbd85],
547 [0x34bb16b4, 0x8e6066fe, 0xf3f6170a, 0xae4ec54e],
548 [0xee12f3ff, 0x4000ed43, 0x446336d7, 0x6cc344bf],
549 [0xfe6ba9e1, 0xcc97f503, 0xf6fc8f14, 0x40c334c0],
550 [0xb4229527, 0x8591ac0b, 0x1ba40ea8, 0x15aa5710]]));
551 }
552 #[test]
553 fn test_beam_video_8bit() {
554 let mut dmx_reg = RegisteredDemuxers::new();
555 game_register_all_demuxers(&mut dmx_reg);
556 let mut dec_reg = RegisteredDecoders::new();
557 game_register_all_decoders(&mut dec_reg);
558
559 // sample from Lost Vikings 2
560 test_decoding("siff", "beam-video", "assets/Game/siff/BEAM.VB", Some(1000), &dmx_reg, &dec_reg,
561 ExpectedTestResult::MD5Frames(vec![
562 [0x23604e03, 0x5781ea8a, 0x6b28d338, 0xc32d52d6],
563 [0xba68d1af, 0xacf630b7, 0x3899073d, 0x07135315],
564 [0xb10cd159, 0xd787a566, 0x523123ca, 0x8a757f9e],
565 [0x7f4dfb53, 0x581884ab, 0x71de61c0, 0xfec72a11],
566 [0xc2a40282, 0x729548e1, 0xc63211bf, 0x586158fc],
567 [0xb8bf753c, 0x53686fb2, 0x7d307625, 0xdab70698],
568 [0xa2e90475, 0x076ef58a, 0xe2276941, 0x9c47ca14],
569 [0x80f785df, 0x19e015ab, 0x93beaf9b, 0xff1d0907],
570 [0x957a9a60, 0xc8a8ae09, 0xad5ecf6c, 0x06097f03],
571 [0xdfd331d4, 0x2f6ba92b, 0x5bfcb6f8, 0x85bab2b9],
572 [0xf37d42c1, 0x7f54d6bd, 0xc2dca688, 0x60d64e8c],
573 [0x97a0e18b, 0xce9ea101, 0x56eea57c, 0xeb48e8b3],
574 [0x7043048c, 0x373fdf9d, 0x1b27d5b3, 0x3e4e9c7a]]));
575 }
576 #[test]
577 fn test_beam_video_16bit() {
578 let mut dmx_reg = RegisteredDemuxers::new();
579 game_register_all_demuxers(&mut dmx_reg);
580 let mut dec_reg = RegisteredDecoders::new();
581 game_register_all_decoders(&mut dec_reg);
582
583 // sample from Alien Earth
584 test_decoding("siff", "beam-video", "assets/Game/siff/beamlogo.vbc", Some(500), &dmx_reg, &dec_reg,
585 ExpectedTestResult::MD5Frames(vec![
586 [0x90aeded9, 0x922cf894, 0x6ea78586, 0x35493724],
587 [0x90aeded9, 0x922cf894, 0x6ea78586, 0x35493724],
588 [0xa19daf5e, 0xa4858d72, 0x6700fc4d, 0x95c5e5dd],
589 [0x57d556af, 0x95c5c5f4, 0xe943f1e3, 0xb86941e1],
590 [0xa63351ae, 0x12ed21ee, 0x7246d1f4, 0xf3a5a79a],
591 [0x5930e388, 0x4891d37b, 0x619ea7ff, 0xc8dac16d],
592 [0x36eeaf75, 0x767e5e5e, 0x397e0100, 0xfa306811],
593 [0xb4722a6d, 0x61cc8483, 0xe4966f11, 0xa67cd0f4],
594 [0x62be7367, 0xcbeb77b2, 0xcb438480, 0xda39b47a],
595 [0x568b020d, 0x4992bc7f, 0xcbe20b5c, 0x697a35bc],
596 [0x35e5a6a8, 0x903c0d07, 0xbacf7734, 0xd1c54b6d],
597 [0xd3450588, 0xfae0a9ec, 0x72a8fce6, 0x7c57e3c1],
598 [0x90f0cc47, 0xd7e7d3ef, 0x46d81b4d, 0xf4495aa2]]));
599 }
600 }
601
602 const VB_PATTERNS: [u16; 64] = [
603 0x0660, 0xFF00, 0xCCCC, 0xF000, 0x8888, 0x000F, 0x1111, 0xFEC8,
604 0x8CEF, 0x137F, 0xF731, 0xC800, 0x008C, 0x0013, 0x3100, 0xCC00,
605 0x00CC, 0x0033, 0x3300, 0x0FF0, 0x6666, 0x00F0, 0x0F00, 0x2222,
606 0x4444, 0xF600, 0x8CC8, 0x006F, 0x1331, 0x318C, 0xC813, 0x33CC,
607 0x6600, 0x0CC0, 0x0066, 0x0330, 0xF900, 0xC88C, 0x009F, 0x3113,
608 0x6000, 0x0880, 0x0006, 0x0110, 0xCC88, 0xFC00, 0x00CF, 0x88CC,
609 0x003F, 0x1133, 0x3311, 0xF300, 0x6FF6, 0x0603, 0x08C6, 0x8C63,
610 0xC631, 0x6310, 0xC060, 0x0136, 0x136C, 0x36C8, 0x6C80, 0x324C
611 ];