]> git.nihav.org Git - nihav.git/blob - nihav-game/src/demuxers/rbt.rs
Sierra RBT and SEQ formats support
[nihav.git] / nihav-game / src / demuxers / rbt.rs
1 use nihav_core::frame::*;
2 use nihav_core::demuxers::*;
3
4 const AFRAME_HDR_SIZE: usize = 16;
5
6 #[allow(dead_code)]
7 struct RobotDemuxer<'a> {
8 src: &'a mut ByteReader<'a>,
9 version: u16,
10 vpts: u64,
11 apts: u64,
12 pkt_no: usize,
13 audio: bool,
14 has_audio: bool,
15 initial: Vec<u8>,
16 a_id: Option<usize>,
17 v_id: Option<usize>,
18 nframes: usize,
19 vframe_len: Vec<u32>,
20 pkt_len: Vec<u32>,
21 }
22
23 impl<'a> DemuxCore<'a> for RobotDemuxer<'a> {
24 #[allow(unused_variables)]
25 fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
26 let mut hdr = [0; 60];
27 self.src.read_buf(&mut hdr)?;
28 validate!(hdr[0] == 0x16 || hdr[0] == 0x3D);
29 validate!(&hdr[2..6] == b"SOL\0");
30 self.version = read_u16le(&hdr[6..])?;
31 let aframe_size = read_u16le(&hdr[8..])? as usize;
32 validate!(self.version >= 4 && self.version <= 6);
33
34 self.nframes = read_u16le(&hdr[14..])? as usize;
35 validate!(self.nframes > 0);
36 let pal_size = read_u16le(&hdr[16..])? as usize;
37 let audio_pre_size = read_u16le(&hdr[18..])? as usize;
38
39 let mut width = read_u16le(&hdr[20..])? as usize;
40 if width == 0 { width = 640; }
41 let mut height = read_u16le(&hdr[22..])? as usize;
42 if height == 0 { height = 480; }
43 let has_pal = hdr[24] != 0;
44 self.has_audio = hdr[25] != 0;
45 let fps = read_u16le(&hdr[28..])?;
46 if !self.has_audio || audio_pre_size == 0 {
47 self.src.read_skip(audio_pre_size)?;
48 } else {
49 let end_pos = self.src.tell() + (audio_pre_size as u64);
50 validate!(audio_pre_size >= 12);
51 validate!(aframe_size > AFRAME_HDR_SIZE);
52 let pre_size = self.src.read_u32le()? as usize;
53 validate!(pre_size <= audio_pre_size - 14);
54 let method = self.src.read_u16le()?;
55 validate!(method == 0);
56 let size1 = self.src.read_u32le()? as usize;
57 let size2 = self.src.read_u32le()? as usize;
58 validate!(size1 + size2 <= pre_size);
59 let to_skip = (aframe_size - AFRAME_HDR_SIZE) / 2;
60 if size1 + size2 > to_skip {
61 self.initial.resize(size1 + size2 + 1 - to_skip, 0);
62 self.initial[0] = 0;
63 self.src.read_buf(&mut self.initial[1..])?;
64 }
65 self.src.seek(SeekFrom::Start(end_pos))?;
66 }
67 let mut pal = vec![0; pal_size + 2];
68 pal[0] = self.version as u8;
69 pal[1] = has_pal as u8;
70 self.src.read_buf(&mut pal[2..])?;
71 self.vframe_len.clear();
72 self.vframe_len.reserve(self.nframes);
73 if self.version < 6 {
74 for _ in 0..self.nframes {
75 let size = self.src.read_u16le()?;
76 self.vframe_len.push(u32::from(size));
77 }
78 } else {
79 for _ in 0..self.nframes {
80 let size = self.src.read_u32le()?;
81 self.vframe_len.push(size);
82 }
83 }
84
85 self.pkt_len.clear();
86 self.pkt_len.reserve(self.nframes);
87 if self.version < 6 {
88 for _ in 0..self.nframes {
89 let size = self.src.read_u16le()?;
90 self.pkt_len.push(u32::from(size));
91 }
92 } else {
93 for _ in 0..self.nframes {
94 let size = self.src.read_u32le()?;
95 self.pkt_len.push(size);
96 }
97 }
98 self.src.read_skip(256 * 4)?; // cues
99 self.src.read_skip(256 * 2)?; // smth
100 let pos = (self.src.tell() & 0x7FF) as usize;
101 if pos != 0 {
102 self.src.read_skip(0x800 - pos)?;
103 }
104
105 let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT);
106 let vci = NACodecTypeInfo::Video(vhdr);
107 let vinfo = NACodecInfo::new("rbt-video", vci, Some(pal));
108 self.v_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps.into(), self.nframes as u64));
109 if self.has_audio {
110 let ahdr = NAAudioInfo::new(11025, 2, SND_S16_FORMAT, 1);
111 let ainfo = NACodecInfo::new("rbt-audio", NACodecTypeInfo::Audio(ahdr), None);
112 self.a_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050, 2));
113 }
114 self.apts = 0;
115 self.vpts = 0;
116 self.pkt_no = 0;
117 self.audio = false;
118
119 Ok(())
120 }
121
122 fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
123 if self.has_audio && !self.initial.is_empty() {
124 let mut buf = Vec::new();
125 std::mem::swap(&mut self.initial, &mut buf);
126 if let Some(a_id) = self.a_id {
127 let stream = strmgr.get_stream(a_id).unwrap();
128 let ts = stream.make_ts(Some(0), None, None);
129 self.apts += (buf.len() - 1) as u64;
130 return Ok(NAPacket::new(stream, ts, true, buf));
131 }
132 }
133 loop {
134 if self.pkt_no >= self.nframes {
135 return Err(DemuxerError::EOF);
136 }
137
138 if !self.audio {
139 let stream = strmgr.get_stream(self.v_id.unwrap_or(0)).unwrap();
140 let ts = stream.make_ts(Some(self.vpts), None, None);
141 self.vpts += 1;
142
143 let mut buf = vec![0; self.vframe_len[self.pkt_no] as usize];
144 self.src.read_buf(&mut buf)?;
145
146 if self.has_audio {
147 self.audio = true;
148 } else {
149 self.src.read_skip((self.pkt_len[self.pkt_no] - self.vframe_len[self.pkt_no]) as usize)?;
150 self.pkt_no += 1;
151 }
152
153 return Ok(NAPacket::new(stream, ts, self.vpts == 1, buf));
154 } else {
155 let asize = (self.pkt_len[self.pkt_no] - self.vframe_len[self.pkt_no]) as usize;
156 validate!(asize >= AFRAME_HDR_SIZE);
157 self.audio = false;
158 self.pkt_no += 1;
159
160 let _ref_apts = u64::from(self.src.read_u32le()?);
161 let ref_asize = self.src.read_u32le()? as usize;
162 validate!(asize == ref_asize + 8);
163
164 if let Some(a_id) = self.a_id {
165 let stream = strmgr.get_stream(a_id).unwrap();
166 let ts = stream.make_ts(Some(self.apts), None, None);
167 self.apts += (ref_asize - 8) as u64;
168 let mut buf = vec![0; ref_asize + 1];
169 buf[0] = 1;
170 self.src.read_buf(&mut buf[1..])?;
171 return Ok(NAPacket::new(stream, ts, true, buf));
172 } else {
173 self.src.read_skip(ref_asize)?;
174 }
175 }
176 }
177 }
178
179 fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
180 Err(DemuxerError::NotPossible)
181 }
182 fn get_duration(&self) -> u64 { 0 }
183 }
184 impl<'a> NAOptionHandler for RobotDemuxer<'a> {
185 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
186 fn set_options(&mut self, _options: &[NAOption]) { }
187 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
188 }
189 impl<'a> RobotDemuxer<'a> {
190 fn new(io: &'a mut ByteReader<'a>) -> Self {
191 RobotDemuxer {
192 src: io,
193 vpts: 0,
194 apts: 0,
195 a_id: None,
196 v_id: None,
197 nframes: 0,
198 pkt_no: 0,
199 audio: false,
200 has_audio: false,
201 initial: Vec::new(),
202 version: 0,
203 vframe_len: Vec::new(),
204 pkt_len: Vec::new(),
205 }
206 }
207 }
208
209 pub struct RobotDemuxerCreator { }
210
211 impl DemuxerCreator for RobotDemuxerCreator {
212 fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
213 Box::new(RobotDemuxer::new(br))
214 }
215 fn get_name(&self) -> &'static str { "sierra-rbt" }
216 }
217
218 #[cfg(test)]
219 mod test {
220 use super::*;
221 use std::fs::File;
222
223 #[test]
224 fn test_rbt_v4() {
225 // sample from SWAT demo
226 let mut file = File::open("assets/Game/sierra/12.rbt").unwrap();
227 let mut fr = FileReader::new_read(&mut file);
228 let mut br = ByteReader::new(&mut fr);
229 let mut dmx = RobotDemuxer::new(&mut br);
230 let mut sm = StreamManager::new();
231 let mut si = SeekIndex::new();
232 dmx.open(&mut sm, &mut si).unwrap();
233 loop {
234 let pktres = dmx.get_frame(&mut sm);
235 if let Err(e) = pktres {
236 if (e as i32) == (DemuxerError::EOF as i32) { break; }
237 panic!("error");
238 }
239 let pkt = pktres.unwrap();
240 println!("Got {}", pkt);
241 }
242 }
243 #[test]
244 fn test_rbt_v5() {
245 // sample from Space Quest 6
246 let mut file = File::open("assets/Game/sierra/410.rbt").unwrap();
247 let mut fr = FileReader::new_read(&mut file);
248 let mut br = ByteReader::new(&mut fr);
249 let mut dmx = RobotDemuxer::new(&mut br);
250 let mut sm = StreamManager::new();
251 let mut si = SeekIndex::new();
252 dmx.open(&mut sm, &mut si).unwrap();
253 loop {
254 let pktres = dmx.get_frame(&mut sm);
255 if let Err(e) = pktres {
256 if (e as i32) == (DemuxerError::EOF as i32) { break; }
257 panic!("error");
258 }
259 let pkt = pktres.unwrap();
260 println!("Got {}", pkt);
261 }
262 }
263 #[test]
264 fn test_rbt_v6() {
265 // sample from Rama
266 let mut file = File::open("assets/Game/sierra/7531.RBT").unwrap();
267 let mut fr = FileReader::new_read(&mut file);
268 let mut br = ByteReader::new(&mut fr);
269 let mut dmx = RobotDemuxer::new(&mut br);
270 let mut sm = StreamManager::new();
271 let mut si = SeekIndex::new();
272 dmx.open(&mut sm, &mut si).unwrap();
273 loop {
274 let pktres = dmx.get_frame(&mut sm);
275 if let Err(e) = pktres {
276 if (e as i32) == (DemuxerError::EOF as i32) { break; }
277 panic!("error");
278 }
279 let pkt = pktres.unwrap();
280 println!("Got {}", pkt);
281 }
282 }
283 }