]> git.nihav.org Git - nihav.git/blob - nihav-game/src/codecs/rbt.rs
Sierra RBT and SEQ formats support
[nihav.git] / nihav-game / src / codecs / rbt.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3 use nihav_core::compr::lz_copy;
4
5 const FRAME_HEADER: usize = 24;
6
7 struct RobotVideoDecoder {
8 info: NACodecInfoRef,
9 pal: [u8; 768],
10 frame: Vec<u8>,
11 cell_buf: Vec<u8>,
12 width: usize,
13 height: usize,
14 version: u8,
15 }
16
17 impl RobotVideoDecoder {
18 fn new() -> Self {
19 Self {
20 info: NACodecInfoRef::default(),
21 pal: [0; 768],
22 frame: Vec::new(),
23 cell_buf: Vec::new(),
24 width: 0,
25 height: 0,
26 version: 0,
27 }
28 }
29 }
30
31 struct BitReader<'a, 'b> {
32 br: &'a mut ByteReader<'b>,
33 end: u64,
34 bbuf: u32,
35 bits: u8,
36 }
37
38 impl<'a, 'b> BitReader<'a, 'b> {
39 fn new(br: &'a mut ByteReader<'b>, size: usize) -> Self {
40 let end = br.tell() + (size as u64);
41 Self {
42 br, end,
43 bbuf: 0,
44 bits: 0,
45 }
46 }
47 fn refill(&mut self) -> DecoderResult<()> {
48 while self.bits <= 24 && self.br.tell() < self.end {
49 self.bbuf |= u32::from(self.br.read_byte()?) << (24 - self.bits);
50 self.bits += 8;
51 }
52 Ok(())
53 }
54 fn read_bit(&mut self) -> DecoderResult<bool> {
55 self.refill()?;
56 if self.bits == 0 { return Err(DecoderError::ShortData); }
57 let bit = (self.bbuf >> 31) != 0;
58 self.bbuf <<= 1;
59 self.bits -= 1;
60 Ok(bit)
61 }
62 fn read(&mut self, nbits: u8) -> DecoderResult<u32> {
63 self.refill()?;
64 if self.bits < nbits { return Err(DecoderError::ShortData); }
65 let ret = self.bbuf >> (32 - nbits);
66 self.bbuf <<= nbits;
67 self.bits -= nbits;
68 Ok(ret)
69 }
70 }
71
72 fn lzs_unpack(br: &mut ByteReader, csize: usize, dst: &mut [u8]) -> DecoderResult<()> {
73 let mut br = BitReader::new(br, csize);
74
75 let mut dpos = 0;
76 loop {
77 if br.read_bit()? {
78 let offset = (if br.read_bit()? {
79 let off = br.read(7)?;
80 if off == 0 {
81 validate!(dpos == dst.len());
82 return Ok(());
83 }
84 off
85 } else {
86 br.read(11)?
87 }) as usize;
88
89 let mut len = br.read(2)?;
90 if len < 3 {
91 len += 2;
92 } else {
93 len = br.read(2)?;
94 if len < 3 {
95 len += 5;
96 } else {
97 len = 8;
98 loop {
99 let t = br.read(4)?;
100 len += t;
101 if t != 0xF {
102 break;
103 }
104 }
105 }
106 }
107 let len = len as usize;
108
109 validate!(offset <= dpos);
110 validate!(dpos + len <= dst.len());
111 lz_copy(dst, dpos, offset, len);
112 dpos += len;
113 } else {
114 dst[dpos] = br.read(8)? as u8;
115 dpos += 1;
116 }
117 }
118 }
119
120 fn unpack_cell(br: &mut ByteReader, cell_size: usize, nchunks: usize, dst: &mut Vec<u8>, limit: usize) -> DecoderResult<()> {
121 let mut data_left = cell_size;
122 dst.clear();
123 dst.reserve(limit);
124 for _ in 0..nchunks {
125 validate!(data_left >= 10);
126 let csize = br.read_u32le()? as usize;
127 validate!(csize <= data_left);
128 let rsize = br.read_u32le()? as usize;
129 validate!(rsize + dst.len() <= limit);
130 let method = br.read_u16le()?;
131
132 data_left -= 10;
133
134 let cur_size = dst.len();
135 dst.resize(cur_size + rsize, 0);
136 match method {
137 0 => { lzs_unpack(br, csize, &mut dst[cur_size..])?; },
138 2 => {
139 validate!(rsize == csize);
140 br.read_buf(&mut dst[cur_size..])?;
141 },
142 _ => return Err(DecoderError::NotImplemented),
143 }
144 data_left -= csize;
145 }
146 Ok(())
147 }
148
149 fn blit(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize) {
150 for (dline, sline) in dst.chunks_mut(dstride).zip(src.chunks(sstride)) {
151 dline[..sstride].copy_from_slice(sline);
152 }
153 }
154
155 fn blit_scaled(dst: &mut [u8], dstride: usize, src: &[u8], sstride: usize, scale: u8) {
156 let mut slines = src.chunks(sstride);
157 let mut acc = 0;
158
159 let mut cur_line = slines.next().unwrap();
160
161 for dline in dst.chunks_mut(dstride) {
162 dline[..sstride].copy_from_slice(cur_line);
163 acc += scale;
164 if acc >= 100 {
165 acc -= 100;
166 if let Some(line) = slines.next() {
167 cur_line = line;
168 } else {
169 break;
170 }
171 }
172 }
173 }
174
175 impl NADecoder for RobotVideoDecoder {
176 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
177 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
178 self.width = vinfo.get_width();
179 self.height = vinfo.get_height();
180 self.frame.resize(self.width * self.height, 0);
181 if let Some(ref edata) = info.get_extradata() {
182 validate!(edata.len() > 2);
183 self.version = edata[0];
184 validate!(self.version >= 4 && self.version <= 6);
185 if edata[1] != 0 {
186 validate!(edata.len() > 39);
187 let pal_start = read_u16le(&edata[25+2..])? as usize;
188 let pal_len = read_u16le(&edata[29+2..])? as usize;
189 validate!(pal_len > 0 && pal_start + pal_len <= 256);
190 match edata[32+2] {
191 0 => {
192 let dpal = self.pal[pal_start * 3..].chunks_exact_mut(3);
193 for (dst, quad) in dpal.zip(edata[37+2..].chunks_exact(4)) {
194 dst.copy_from_slice(&quad[1..]);
195 }
196 },
197 1 => self.pal[pal_start * 3..][..pal_len * 3].copy_from_slice(&edata[37+2..][..pal_len * 3]),
198 _ => return Err(DecoderError::NotImplemented),
199 }
200 } else {
201 for (i, entry) in self.pal.chunks_exact_mut(3).enumerate() {
202 entry[0] = i as u8;
203 entry[1] = i as u8;
204 entry[2] = i as u8;
205 }
206 }
207 } else {
208 return Err(DecoderError::InvalidData);
209 }
210 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, false, PAL8_FORMAT));
211 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
212
213 Ok(())
214 } else {
215 Err(DecoderError::InvalidData)
216 }
217 }
218 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
219 let src = pkt.get_buffer();
220 validate!(src.len() > FRAME_HEADER);
221
222 let mut mr = MemoryReader::new_read(&src);
223 let mut br = ByteReader::new(&mut mr);
224
225 let ncells = br.read_u16le()? as usize;
226 validate!(ncells > 0 && ncells <= 10);
227 for el in self.frame.iter_mut() { *el = 0xFF; }
228 for _ in 0..ncells {
229 br.read_byte()?;
230 let scale = br.read_byte()?;
231 let width = br.read_u16le()? as usize;
232 let height = br.read_u16le()? as usize;
233 br.read_skip(4)?;
234 let xoff = br.read_u16le()? as usize;
235 let yoff = br.read_u16le()? as usize;
236 validate!(xoff + width <= self.width && yoff + height <= self.height);
237 let mut cell_size = br.read_u16le()? as usize;
238 // hack
239 if self.version == 6 && ncells == 1 && (src.len() - 18 >= 0x10000) {
240 cell_size += (src.len() - 18) & !0xFFFF;
241 }
242 if self.version > 4 {
243 let nchunks = br.read_u16le()? as usize;
244 validate!(nchunks > 0);
245 br.read_skip(4)?;
246
247 unpack_cell(&mut br, cell_size, nchunks, &mut self.cell_buf, width * height)?;
248 } else {
249 br.read_skip(6)?;
250 self.cell_buf.resize(width * height, 0);
251 lzs_unpack(&mut br, cell_size, &mut self.cell_buf)?;
252 }
253 if scale == 100 {
254 blit(&mut self.frame[xoff + yoff * self.width..], self.width, &self.cell_buf, width);
255 } else {
256 blit_scaled(&mut self.frame[xoff + yoff * self.width..], self.width, &self.cell_buf, width, scale);
257 }
258 }
259
260 let buf = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?;
261 let mut vbuf = buf.get_vbuf().unwrap();
262 let paloff = vbuf.get_offset(1);
263 let stride = vbuf.get_stride(0);
264 let data = vbuf.get_data_mut().unwrap();
265
266 blit(data, stride, &self.frame, self.width);
267 data[paloff..][..768].copy_from_slice(&self.pal);
268
269 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buf);
270 let ftype = if pkt.keyframe { FrameType::I } else { FrameType::P };
271 frm.set_frame_type(ftype);
272 Ok(frm.into_ref())
273 }
274 fn flush(&mut self) {
275 }
276 }
277
278 impl NAOptionHandler for RobotVideoDecoder {
279 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
280 fn set_options(&mut self, _options: &[NAOption]) { }
281 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
282 }
283
284 pub fn get_decoder() -> Box<dyn NADecoder + Send> {
285 Box::new(RobotVideoDecoder::new())
286 }
287
288 struct RobotAudioDecoder {
289 info: NACodecInfoRef,
290 ainfo: NAAudioInfo,
291 chmap: NAChannelMap,
292 }
293
294 impl RobotAudioDecoder {
295 fn new() -> Self {
296 Self {
297 ainfo: NAAudioInfo::new(11025, 1, SND_S16_FORMAT, 1),
298 info: NACodecInfo::new_dummy(),
299 chmap: NAChannelMap::from_str("C").unwrap(),
300 }
301 }
302 fn pred16(pred: i32, val: u8) -> i32 {
303 if (val & 0x80) != 0 {
304 pred - i32::from(SOL_AUD_STEPS16[(val & 0x7F) as usize])
305 } else {
306 pred + i32::from(SOL_AUD_STEPS16[(val & 0x7F) as usize])
307 }
308 }
309 }
310
311 impl NADecoder for RobotAudioDecoder {
312 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
313 if let NACodecTypeInfo::Audio(_ainfo) = info.get_properties() {
314 self.info = info.replace_info(NACodecTypeInfo::Audio(self.ainfo));
315
316 Ok(())
317 } else {
318 Err(DecoderError::InvalidData)
319 }
320 }
321 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
322 let src = pkt.get_buffer();
323 validate!(src.len() > 1);
324
325 let samples = match src[0] {
326 0 => src.len() - 1,
327 1 => src.len() - 1 - 8,
328 _ => return Err(DecoderError::InvalidData),
329 };
330
331 let abuf = alloc_audio_buffer(self.ainfo, samples / 2, self.chmap.clone())?;
332 let mut adata = abuf.get_abuf_i16().unwrap();
333 let dst = adata.get_data_mut().unwrap();
334
335 match src[0] {
336 0 => {
337 let mut pred = 0;
338 for (dst, &b) in dst.iter_mut().zip(src[1..][..src.len()/2].iter()) {
339 pred = Self::pred16(pred, b);
340 *dst = pred as i16;
341 }
342 },
343 1 => {
344 validate!(src.len() > 8);
345 let mut pred = 0;
346 for &b in src[1..9].iter() {
347 pred = Self::pred16(pred, b);
348 }
349 for (dst, &b) in dst.iter_mut().zip(src[9..].iter()) {
350 pred = Self::pred16(pred, b);
351 *dst = pred as i16;
352 }
353 },
354 _ => unreachable!(),
355 }
356
357 let frm = NAFrame::new_from_pkt(pkt, self.info.clone(), abuf);
358 Ok(frm.into_ref())
359 }
360 fn flush(&mut self) {
361 }
362 }
363
364 impl NAOptionHandler for RobotAudioDecoder {
365 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
366 fn set_options(&mut self, _options: &[NAOption]) { }
367 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
368 }
369
370 pub fn get_decoder_audio() -> Box<dyn NADecoder + Send> {
371 Box::new(RobotAudioDecoder::new())
372 }
373
374 #[cfg(test)]
375 mod test {
376 use nihav_core::codecs::RegisteredDecoders;
377 use nihav_core::demuxers::RegisteredDemuxers;
378 use nihav_codec_support::test::dec_video::*;
379 use crate::*;
380 #[test]
381 fn test_rbt_v4() {
382 let mut dmx_reg = RegisteredDemuxers::new();
383 game_register_all_demuxers(&mut dmx_reg);
384 let mut dec_reg = RegisteredDecoders::new();
385 game_register_all_decoders(&mut dec_reg);
386
387 // sample from SWAT demo
388 test_decoding("sierra-rbt", "rbt-video", "assets/Game/sierra/12.rbt",
389 Some(2), &dmx_reg, &dec_reg,
390 ExpectedTestResult::MD5Frames(vec![
391 [0x2a00775d, 0xef8da06a, 0x015b6f06, 0xa22d0158],
392 [0xf2acb558, 0x0d9c5c54, 0x32c43af4, 0xd9776b68],
393 [0x386e02e9, 0x76dbd5a6, 0x4e9da3d7, 0xa47fdca3]]));
394 }
395 #[test]
396 fn test_rbt_v5() {
397 let mut dmx_reg = RegisteredDemuxers::new();
398 game_register_all_demuxers(&mut dmx_reg);
399 let mut dec_reg = RegisteredDecoders::new();
400 game_register_all_decoders(&mut dec_reg);
401
402 // sample from Phantasmagora (with scaling)
403 test_decoding("sierra-rbt", "rbt-video", "assets/Game/sierra/162.RBT",
404 None, &dmx_reg, &dec_reg,
405 ExpectedTestResult::MD5([0x4912fa8f, 0xae201d9e, 0x59707ea0, 0xc50bf0e2]));
406 }
407 #[test]
408 fn test_rbt_v6() {
409 let mut dmx_reg = RegisteredDemuxers::new();
410 game_register_all_demuxers(&mut dmx_reg);
411 let mut dec_reg = RegisteredDecoders::new();
412 game_register_all_decoders(&mut dec_reg);
413
414 // sample from Rama
415 test_decoding("sierra-rbt", "rbt-video", "assets/Game/sierra/7531.RBT",
416 Some(2), &dmx_reg, &dec_reg,
417 ExpectedTestResult::MD5Frames(vec![
418 [0x49db87f3, 0x57881095, 0x676d1600, 0x5ddaa50b],
419 [0xa75ff558, 0xb6815b27, 0x5f9d872f, 0xd7f56470],
420 [0x60bca745, 0xc47d6882, 0xc193fe70, 0x7b8738c9]]));
421 }
422 #[test]
423 fn test_rbt_audio() {
424 let mut dmx_reg = RegisteredDemuxers::new();
425 game_register_all_demuxers(&mut dmx_reg);
426 let mut dec_reg = RegisteredDecoders::new();
427 game_register_all_decoders(&mut dec_reg);
428
429 // sample from Space Quest 6
430 test_decoding("sierra-rbt", "rbt-audio", "assets/Game/sierra/410.rbt",
431 Some(2), &dmx_reg, &dec_reg,
432 ExpectedTestResult::MD5([0xdd44e3ca, 0x6cfc1bc1, 0xdcd4214a, 0x443cf5ed]));
433 }
434 }
435
436 const SOL_AUD_STEPS16: [i16; 128] = [
437 0x00, 0x08, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60,
438 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, 0xD0, 0xE0,
439 0xF0, 0x100, 0x110, 0x120, 0x130, 0x140, 0x150, 0x160,
440 0x170, 0x180, 0x190, 0x1A0, 0x1B0, 0x1C0, 0x1D0, 0x1E0,
441 0x1F0, 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230,
442 0x238, 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270,
443 0x278, 0x280, 0x288, 0x290, 0x298, 0x2A0, 0x2A8, 0x2B0,
444 0x2B8, 0x2C0, 0x2C8, 0x2D0, 0x2D8, 0x2E0, 0x2E8, 0x2F0,
445 0x2F8, 0x300, 0x308, 0x310, 0x318, 0x320, 0x328, 0x330,
446 0x338, 0x340, 0x348, 0x350, 0x358, 0x360, 0x368, 0x370,
447 0x378, 0x380, 0x388, 0x390, 0x398, 0x3A0, 0x3A8, 0x3B0,
448 0x3B8, 0x3C0, 0x3C8, 0x3D0, 0x3D8, 0x3E0, 0x3E8, 0x3F0,
449 0x3F8, 0x400, 0x440, 0x480, 0x4C0, 0x500, 0x540, 0x580,
450 0x5C0, 0x600, 0x640, 0x680, 0x6C0, 0x700, 0x740, 0x780,
451 0x7C0, 0x800, 0x900, 0xA00, 0xB00, 0xC00, 0xD00, 0xE00,
452 0xF00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000
453 ];