]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::frame::*; | |
2 | use nihav_core::demuxers::*; | |
3 | ||
4 | const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, | |
5 | comp_info: [ | |
6 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }), | |
7 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 1, next_elem: 2 }), | |
8 | Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 2, next_elem: 2 }), | |
9 | None, None], | |
10 | elem_size: 2, be: false, alpha: false, palette: false }; | |
11 | struct SGADemuxer<'a> { | |
12 | src: &'a mut ByteReader<'a>, | |
13 | subtype: u8, | |
14 | apts: u64, | |
15 | abuf: Vec<u8>, | |
16 | abuf2: Vec<u8>, | |
17 | asize: usize, | |
18 | no_ts_in_f9: bool, | |
19 | ntsc: bool, | |
20 | } | |
21 | ||
22 | impl<'a> SGADemuxer<'a> { | |
23 | fn new(io: &'a mut ByteReader<'a>) -> Self { | |
24 | Self { | |
25 | src: io, | |
26 | subtype: 0, | |
27 | apts: 0, | |
28 | abuf: Vec::new(), | |
29 | abuf2: Vec::new(), | |
30 | asize: 0, | |
31 | no_ts_in_f9: false, | |
32 | ntsc: false, | |
33 | } | |
34 | } | |
35 | } | |
36 | ||
37 | fn parse_smpte_time(src: &[u8], ntsc: bool) -> DemuxerResult<u64> { | |
38 | validate!(src.len() >= 4); | |
39 | let hours = src[0]; | |
40 | let minutes = src[1]; | |
41 | validate!(minutes < 60); | |
42 | let seconds = src[2]; | |
43 | validate!(seconds < 60); | |
44 | let frame = src[3]; | |
45 | if ntsc { | |
46 | validate!(frame < 60); | |
47 | } else { | |
48 | validate!(frame < 30); | |
49 | } | |
50 | ||
51 | let tot_min = u64::from(hours) * 60 + u64::from(minutes); | |
52 | let tot_sec = tot_min * 60 + u64::from(seconds); | |
53 | Ok(tot_sec * if ntsc { 60 } else { 30 } + u64::from(frame)) | |
54 | } | |
55 | ||
56 | fn get_smpte_time(src: &mut ByteReader, ntsc: bool) -> DemuxerResult<u64> { | |
57 | let mut buf = [0; 4]; | |
58 | src.read_buf(&mut buf)?; | |
59 | parse_smpte_time(&buf, ntsc) | |
60 | } | |
61 | ||
62 | impl<'a> DemuxCore<'a> for SGADemuxer<'a> { | |
63 | fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> { | |
64 | let mut subtype = self.src.read_byte()?; | |
65 | match subtype { | |
66 | 0xF1 => { | |
67 | self.src.read_skip(3)?; | |
68 | subtype = self.src.read_byte()?; | |
69 | }, | |
70 | 0xF4 => { | |
71 | self.src.read_skip(1)?; | |
72 | let csize = self.src.read_u16be()?; | |
73 | self.src.read_skip(usize::from(csize))?; | |
74 | subtype = self.src.read_byte()?; | |
75 | }, | |
76 | 0xF9 => { | |
77 | self.src.read_skip(3)?; | |
78 | if (self.src.peek_byte()? & 0x80) == 0 { | |
79 | self.src.read_skip(4)?; | |
80 | } else { | |
81 | self.no_ts_in_f9 = true; | |
82 | } | |
83 | subtype = self.src.read_byte()?; | |
84 | }, | |
85 | _ => {}, | |
86 | }; | |
87 | validate!(subtype >= 0x80); | |
88 | if !matches!(subtype, 0x81 | 0x85 | 0x86 | 0x89 | 0x8A) { | |
89 | return Err(DemuxerError::NotImplemented); | |
90 | } | |
91 | self.subtype = subtype; | |
92 | match subtype { | |
93 | 0x81 | 0x8A => { | |
94 | self.src.read_skip(9)?; | |
95 | let tile_w = self.src.read_byte()?; | |
96 | let tile_h = self.src.read_byte()?; | |
97 | validate!(tile_w > 0 && tile_h > 0); | |
98 | self.src.seek(SeekFrom::Start(0))?; | |
99 | let vhdr = NAVideoInfo::new(usize::from(tile_w) * 8, usize::from(tile_h) * 8, false, RGB555_FORMAT); | |
100 | let vci = NACodecTypeInfo::Video(vhdr); | |
101 | let vinfo = NACodecInfo::new("dp-sga", vci, Some(vec![subtype])); | |
102 | if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 30, 0)).is_none() { | |
103 | return Err(DemuxerError::MemoryError); | |
104 | } | |
105 | }, | |
106 | 0x85 | 0x86 => { | |
107 | let mut edata = vec![0; 0x201]; | |
108 | edata[0] = subtype; | |
109 | self.src.read_byte()?; | |
110 | let tile_w = self.src.read_byte()?; | |
111 | let tile_h = self.src.read_byte()?; | |
112 | validate!(tile_w > 0 && tile_h > 0); | |
113 | self.src.read_skip(8)?; | |
114 | self.src.read_buf(&mut edata[1..])?; | |
115 | if self.subtype == 0x85 { | |
116 | self.asize = usize::from(self.src.read_u16be()?); | |
117 | } | |
118 | ||
119 | let vhdr = NAVideoInfo::new(usize::from(tile_w) * 8, usize::from(tile_h) * 8, false, RGB555_FORMAT); | |
120 | let vci = NACodecTypeInfo::Video(vhdr); | |
121 | let vinfo = NACodecInfo::new("dp-sga", vci, Some(edata)); | |
122 | if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 30, 0)).is_none() { | |
123 | return Err(DemuxerError::MemoryError); | |
124 | } | |
125 | ||
126 | let srate = 16000; | |
127 | let ahdr = NAAudioInfo::new(srate, 1, SND_U8_FORMAT, 1); | |
128 | let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None); | |
129 | if strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate, 0)).is_none() { | |
130 | return Err(DemuxerError::MemoryError); | |
131 | } | |
132 | }, | |
133 | 0x89 => { | |
134 | self.src.seek(SeekFrom::Start(0))?; | |
135 | let vhdr = NAVideoInfo::new(256, 160, false, RGB555_FORMAT); | |
136 | let vci = NACodecTypeInfo::Video(vhdr); | |
137 | let vinfo = NACodecInfo::new("dp-sga", vci, Some(vec![subtype])); | |
138 | if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 30, 0)).is_none() { | |
139 | return Err(DemuxerError::MemoryError); | |
140 | } | |
141 | ||
142 | let srate = 22050; | |
143 | let ahdr = NAAudioInfo::new(srate, 1, SND_U8_FORMAT, 1); | |
144 | let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None); | |
145 | if strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate, 0)).is_none() { | |
146 | return Err(DemuxerError::MemoryError); | |
147 | } | |
148 | let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None); | |
149 | if strmgr.add_stream(NAStream::new(StreamType::Audio, 2, ainfo, 1, srate, 0)).is_none() { | |
150 | return Err(DemuxerError::MemoryError); | |
151 | } | |
152 | }, | |
153 | _ => unreachable!(), | |
154 | }; | |
155 | ||
156 | Ok(()) | |
157 | } | |
158 | ||
159 | fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> { | |
160 | if !self.abuf.is_empty() { | |
161 | let mut buf = Vec::new(); | |
162 | std::mem::swap(&mut buf, &mut self.abuf); | |
163 | ||
164 | if let Some(stream) = strmgr.get_stream(1) { | |
165 | let ts = stream.make_ts(Some(self.apts), None, None); | |
166 | self.apts += buf.len() as u64; | |
167 | return Ok(NAPacket::new(stream, ts, true, buf)); | |
168 | } | |
169 | } | |
170 | if !self.abuf2.is_empty() { | |
171 | let mut buf = Vec::new(); | |
172 | std::mem::swap(&mut buf, &mut self.abuf2); | |
173 | ||
174 | if let Some(stream) = strmgr.get_stream(2) { | |
175 | let ts = stream.make_ts(Some(self.apts), None, None); | |
176 | self.apts += buf.len() as u64; | |
177 | return Ok(NAPacket::new(stream, ts, true, buf)); | |
178 | } | |
179 | } | |
180 | match self.subtype { | |
181 | 0x81 | 0x8A => { | |
182 | let mut hdr = [0; 4]; | |
183 | loop { | |
184 | match self.src.read_buf(&mut hdr) { | |
185 | Ok(_) => {}, | |
186 | Err(ByteIOError::ReadError) | | |
187 | Err(ByteIOError::EOF) => return Err(DemuxerError::EOF), | |
188 | Err(err) => return Err(err.into()), | |
189 | }; | |
190 | let chunk_size = usize::from(read_u16le(&hdr[2..])?); | |
191 | validate!(chunk_size > 8); | |
192 | match hdr[0] { | |
193 | 0x81 | 0x8A => { | |
194 | let mut buf = vec![0; chunk_size + 4]; | |
195 | buf[..4].copy_from_slice(&hdr); | |
196 | self.src.read_buf(&mut buf[4..])?; | |
197 | let ts = parse_smpte_time(&buf[4..], self.ntsc)?; | |
198 | let stream = strmgr.get_stream(0).unwrap(); | |
199 | let ts = NATimeInfo::new(Some(ts), None, None, stream.tb_num, stream.tb_den); | |
200 | return Ok(NAPacket::new(stream, ts, false, buf)); | |
201 | }, | |
202 | 0xF1 => {}, | |
203 | 0xF9 => { | |
204 | if !self.no_ts_in_f9 { | |
205 | self.src.read_skip(4)?; | |
206 | } | |
207 | }, | |
208 | _ => self.src.read_skip(chunk_size)?, | |
209 | }; | |
210 | if (self.src.tell() & 1) != 0 { | |
211 | self.src.read_skip(1)?; | |
212 | } | |
213 | } | |
214 | }, | |
215 | 0x85 => { | |
216 | let ts = match get_smpte_time(self.src, self.ntsc) { | |
217 | Ok(val) => val, | |
218 | Err(DemuxerError::IOError) => return Err(DemuxerError::EOF), | |
219 | Err(err) => return Err(err), | |
220 | }; | |
221 | let pal_size = self.src.read_u16be()?; | |
222 | let asize = usize::from(self.src.read_u16be()?); | |
223 | validate!(asize >= self.asize); | |
224 | let full_size = usize::from(self.src.read_u16be()?); | |
225 | validate!(full_size >= asize); | |
226 | let pal_off = self.src.read_u16be()?; | |
227 | let vsize = full_size - self.asize; | |
228 | let offset = (asize - self.asize) as u16; | |
229 | let mut buf = vec![0; vsize + 6]; | |
230 | if asize > 0 { | |
231 | self.abuf.resize(self.asize - 1, 0); | |
232 | self.src.read_buf(&mut self.abuf)?; | |
233 | self.src.read_byte()?; | |
234 | } | |
235 | write_u16be(&mut buf, pal_off)?; | |
236 | write_u16be(&mut buf[2..], pal_size)?; | |
237 | write_u16be(&mut buf[4..], offset)?; | |
238 | self.src.read_buf(&mut buf[6..])?; | |
239 | ||
240 | let stream = strmgr.get_stream(0).unwrap(); | |
241 | let ts = NATimeInfo::new(Some(ts), None, None, stream.tb_num, stream.tb_den); | |
242 | Ok(NAPacket::new(stream, ts, false, buf)) | |
243 | }, | |
244 | 0x86 => { | |
245 | let ts = match get_smpte_time(self.src, self.ntsc) { | |
246 | Ok(val) => val, | |
247 | Err(DemuxerError::IOError) => return Err(DemuxerError::EOF), | |
248 | Err(err) => return Err(err), | |
249 | }; | |
250 | let asize = usize::from(self.src.read_u16be()?); | |
251 | let vsize = usize::from(self.src.read_u16be()?); | |
252 | let pal_off = self.src.read_u16be()?; | |
253 | let pal_size = self.src.read_u16be()?; | |
254 | let offset = self.src.read_u16be()?; | |
255 | let mut buf = vec![0; vsize + 6]; | |
256 | if asize > 0 { | |
257 | self.abuf.resize(asize, 0); | |
258 | self.src.read_buf(&mut self.abuf)?; | |
259 | } | |
260 | write_u16be(&mut buf, pal_off)?; | |
261 | write_u16be(&mut buf[2..], pal_size)?; | |
262 | write_u16be(&mut buf[4..], offset)?; | |
263 | self.src.read_buf(&mut buf[6..])?; | |
264 | ||
265 | let stream = strmgr.get_stream(0).unwrap(); | |
266 | let ts = NATimeInfo::new(Some(ts), None, None, stream.tb_num, stream.tb_den); | |
267 | Ok(NAPacket::new(stream, ts, false, buf)) | |
268 | }, | |
269 | 0x89 => { | |
270 | let mut hdr = [0; 4]; | |
271 | loop { | |
272 | match self.src.read_buf(&mut hdr) { | |
273 | Ok(_) => {}, | |
274 | Err(ByteIOError::ReadError) | | |
275 | Err(ByteIOError::EOF) => return Err(DemuxerError::EOF), | |
276 | Err(err) => return Err(err.into()), | |
277 | }; | |
278 | let chunk_size = usize::from(read_u16be(&hdr[2..])?); | |
279 | validate!((hdr[0] & 0x80) != 0); | |
280 | validate!(chunk_size > 8); | |
281 | let end = self.src.tell() + (chunk_size as u64); | |
282 | match hdr[0] { | |
283 | 0x89 => { | |
284 | let ts = get_smpte_time(self.src, self.ntsc)?; | |
285 | let asize = usize::from(self.src.read_u16be()?); | |
286 | let vsize = usize::from(self.src.read_u16be()?); | |
287 | validate!((asize & 0x7FFF) + vsize + 16 <= chunk_size); | |
288 | let pal_size = self.src.read_u16be()?; | |
289 | let offset = self.src.read_u16be()?; | |
290 | validate!(usize::from(offset) <= vsize); | |
291 | let mut buf = vec![0; vsize + 6]; | |
292 | if asize > 0 { | |
293 | if (asize & 0x8000) == 0 { | |
294 | self.abuf.resize(asize, 0); | |
295 | self.src.read_buf(&mut self.abuf)?; | |
296 | self.abuf2.resize(asize, 0); | |
297 | self.abuf2.copy_from_slice(&self.abuf); | |
298 | } else { | |
299 | let asize = asize & 0x7FFF; | |
300 | validate!((asize & 1) == 0); | |
301 | self.abuf.resize(asize / 2, 0); | |
302 | self.abuf2.resize(asize / 2, 0); | |
303 | self.src.read_buf(&mut self.abuf)?; | |
304 | self.src.read_buf(&mut self.abuf2)?; | |
305 | } | |
306 | } | |
307 | write_u16be(&mut buf, 1/*pal_off*/)?; | |
308 | write_u16be(&mut buf[2..], pal_size)?; | |
309 | write_u16be(&mut buf[4..], offset)?; | |
310 | self.src.read_buf(&mut buf[6..])?; | |
311 | validate!(self.src.tell() <= end); | |
312 | self.src.seek(SeekFrom::Start(end))?; | |
313 | ||
314 | let stream = strmgr.get_stream(0).unwrap(); | |
315 | let ts = NATimeInfo::new(Some(ts), None, None, stream.tb_num, stream.tb_den); | |
316 | return Ok(NAPacket::new(stream, ts, false, buf)); | |
317 | }, | |
318 | _ => self.src.read_skip(chunk_size)?, | |
319 | }; | |
320 | } | |
321 | }, | |
322 | _ => unreachable!(), | |
323 | } | |
324 | } | |
325 | ||
326 | fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { | |
327 | Err(DemuxerError::NotPossible) | |
328 | } | |
329 | fn get_duration(&self) -> u64 { 0 } | |
330 | } | |
331 | ||
332 | const DEMUXER_OPTS: &[NAOptionDefinition] = &[ | |
333 | NAOptionDefinition { | |
334 | name: "ntsc", description: "timestamps are 60fps instead of 30fps", | |
335 | opt_type: NAOptionDefinitionType::Bool }, | |
336 | ]; | |
337 | ||
338 | impl<'a> NAOptionHandler for SGADemuxer<'a> { | |
339 | fn get_supported_options(&self) -> &[NAOptionDefinition] { DEMUXER_OPTS } | |
340 | fn set_options(&mut self, options: &[NAOption]) { | |
341 | for option in options.iter() { | |
342 | for opt_def in DEMUXER_OPTS.iter() { | |
343 | if opt_def.check(option).is_ok() { | |
344 | if let ("name", NAValue::Bool(ref bval)) = (option.name, &option.value) { | |
345 | self.ntsc = *bval; | |
346 | } | |
347 | } | |
348 | } | |
349 | } | |
350 | } | |
351 | fn query_option_value(&self, name: &str) -> Option<NAValue> { | |
352 | match name { | |
353 | "ntsc" => Some(NAValue::Bool(self.ntsc)), | |
354 | _ => None, | |
355 | } | |
356 | } | |
357 | } | |
358 | ||
359 | pub struct SGADemuxerCreator { } | |
360 | ||
361 | impl DemuxerCreator for SGADemuxerCreator { | |
362 | fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> { | |
363 | Box::new(SGADemuxer::new(br)) | |
364 | } | |
365 | fn get_name(&self) -> &'static str { "sga" } | |
366 | } | |
367 | ||
368 | #[cfg(test)] | |
369 | mod test { | |
370 | use super::*; | |
371 | use std::fs::File; | |
372 | ||
373 | fn test_sga_demux(name: &str) { | |
374 | let mut file = File::open(name).unwrap(); | |
375 | let mut fr = FileReader::new_read(&mut file); | |
376 | let mut br = ByteReader::new(&mut fr); | |
377 | let mut dmx = SGADemuxer::new(&mut br); | |
378 | let mut sm = StreamManager::new(); | |
379 | let mut si = SeekIndex::new(); | |
380 | dmx.open(&mut sm, &mut si).unwrap(); | |
381 | loop { | |
382 | let pktres = dmx.get_frame(&mut sm); | |
383 | if let Err(e) = pktres { | |
384 | if e == DemuxerError::EOF { break; } | |
385 | panic!("error"); | |
386 | } | |
387 | let pkt = pktres.unwrap(); | |
388 | println!("Got {}", pkt); | |
389 | } | |
390 | } | |
391 | ||
392 | #[test] | |
393 | fn test_sga_demux_81() { | |
394 | // samples from Double Switch game | |
395 | test_sga_demux("assets/Game/sga/ALEXSTIL.AVC"); | |
396 | test_sga_demux("assets/Game/sga/DPLOGO.AVC"); | |
397 | } | |
398 | #[test] | |
399 | fn test_sga_demux_85() { | |
400 | // sample from Night Trap game | |
401 | test_sga_demux("assets/Game/sga/CRMOVIE"); | |
402 | } | |
403 | #[test] | |
404 | fn test_sga_demux_86() { | |
405 | // sample from Corpse Killer game | |
406 | test_sga_demux("assets/Game/sga/dplogo.dtv"); | |
407 | } | |
408 | } |