]>
Commit | Line | Data |
---|---|---|
561d0f79 KS |
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 | ]; |