LucasArts SMUSH formats support
[nihav.git] / nihav-game / src / demuxers / smush.rs
CommitLineData
c17769db
KS
1use nihav_core::frame::*;
2use nihav_core::demuxers::*;
3
4struct SmushDemuxer<'a> {
5 src: &'a mut ByteReader<'a>,
6 old: bool,
7 size: u64,
8
9 nframes: usize,
10 chunks: Vec<u8>,
11
12 keyframe: bool,
13 cur_frame: usize,
14 frme_end: u64,
15 asize: u64,
16}
17
18fn parse_iact(br: &mut ByteReader, end: u64, arate: &mut u32, abits: &mut u8, chans: &mut u8) -> DemuxerResult<()> {
19 br.read_skip(14)?;
20 let tag = br.read_tag()?;
21 if &tag != b"iMUS" {
22 *arate = 22050;
23 *abits = 16;
24 *chans = 2;
25 return Ok(());
26 }
27 br.read_skip(4)?;
28 while br.tell() < end {
29 let tag = br.read_tag()?;
30 let size = u64::from(br.read_u32be()?);
31 match &tag {
32 b"MAP " => {
33 let cend = br.tell() + size;
34 while br.tell() < cend {
35 let tag = br.read_tag()?;
36 let size = u64::from(br.read_u32be()?);
37 match &tag {
38 b"FRMT" => {
39 validate!(size == 20);
40 br.read_u32be()?;
41 br.read_u32be()?;
42 let bits = br.read_u32be()?;
43 validate!(bits > 0 && bits <= 16);
44 *abits = bits as u8;
45 *arate = br.read_u32be()?;
46 let c = br.read_u32be()?;
47 validate!(c == 1 || c == 2);
48 *chans = c as u8;
49 return Ok(());
50 },
51 _ => br.read_skip(size as usize)?,
52 };
53 }
54 },
55 b"DATA" => return Err(DemuxerError::InvalidData),
56 _ => br.read_skip(size as usize)?,
57 };
58 }
59 Err(DemuxerError::InvalidData)
60}
61
62impl<'a> SmushDemuxer<'a> {
63 fn new(io: &'a mut ByteReader<'a>) -> Self {
64 SmushDemuxer {
65 src: io,
66
67 old: false,
68 size: 0,
69
70 nframes: 0,
71 chunks: Vec::new(),
72
73 keyframe: false,
74 cur_frame: 0,
75 frme_end: 0,
76 asize: 0,
77 }
78 }
79 fn parse_anim_header(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
80 let src = &mut self.src;
81
82 let tag = src.read_tag()?;
83 validate!(&tag == b"AHDR");
84 let size = u64::from(src.read_u32be()?);
85 validate!(size >= 768 + 6);
86 let end = src.tell() + size;
87 validate!(end < self.size);
88 let version = src.read_u16le()?;
89 validate!(version < 3);
90 self.nframes = src.read_u16le()? as usize;
91 validate!(self.nframes != 0);
92 src.read_skip(2)?; //max FRME size
93 let mut edata = vec![0; 768 + 1];
94 edata[0] = version as u8;
95 src.read_buf(&mut edata[1..][..768])?;
96 src.read_skip(size as usize - 768 - 6)?;
97
98 let start = src.tell();
99 let mut size = 0;
100 while size == 0 {
101 let tag = src.read_tag()?;
102 validate!(&tag == b"FRME");
103 size = u64::from(src.read_u32be()?);
104 }
105
106 let end = src.tell() + size;
107 validate!(end <= self.size + 8); // some NUTs feature slightly incorrect total size
108
109 let mut width = 0;
110 let mut height = 0;
111 let mut aname = "";
112 let mut arate = 0;
113 let mut abits = 0;
114 let mut chans = 0;
115
116 while src.tell() < end {
117 let tag = src.read_tag()?;
118 let size = u64::from(src.read_u32be()?);
119
120 let tend = src.tell() + size;
121 validate!(tend <= end);
122 match &tag {
123 b"FOBJ" => {
124 validate!(size >= 10);
125 let _codec = src.read_u16le()?;
126 let x = src.read_u16le()? as i16;
127 let y = src.read_u16le()? as i16;
128 if x == 0 && y == 0 && width == 0 && height == 0 {
129 width = src.read_u16le()? as usize;
130 height = src.read_u16le()? as usize;
131 } else {
132 let w = src.read_u16le()? as usize;
133 let h = src.read_u16le()? as usize;
134 if x == 0 && y == 0 && w >= width && h >= height {
135 width = w;
136 height = h;
137 }
138 }
139 src.read_skip((size - 10) as usize)?;
140 },
141 b"IACT" => {
142 validate!(size > 8);
143 let end = src.tell() + size;
144 let opcode = src.read_u16le()?;
145 let flags = src.read_u16le()?;
146 if (opcode == 8) && (flags == 0x2E) {
147 if parse_iact(src, end, &mut arate, &mut abits, &mut chans).is_ok() {
148 aname = "smush-iact";
149 }
150 validate!(src.tell() <= end);
151 }
152 src.seek(SeekFrom::Start(end))?;
153 },
154 b"PSAD" => {
155 aname = "pcm";
156 arate = 11025;
157 abits = 8;
158 chans = 2;
159 src.read_skip(size as usize)?;
160 },
161 _ => { src.read_skip(size as usize)?; },
162 };
163 if (src.tell() & 1) != 0 {
164 if let Ok(0) = src.peek_byte() {
165 src.read_skip(1)?;
166 }
167 }
168 }
169 // hack
170 width = width.max(320);
171 height = height.max(200);
172 src.seek(SeekFrom::Start(start))?;
173 self.frme_end = start;
174
175 let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT);
176 let vci = NACodecTypeInfo::Video(vhdr);
177 let vinfo = NACodecInfo::new("smushv1", vci, Some(edata));
178 if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 15, self.nframes as u64)).is_none() {
179 return Err(DemuxerError::MemoryError);
180 }
181
182 if !aname.is_empty() {
183 validate!(arate > 0);
184 let mut fmt = SND_S16_FORMAT;
185 match aname {
186 "pcm" => { fmt = SND_U8_FORMAT; },
187 "smush-iact" => { fmt.bits = abits; fmt.packed = true; },
188 _ => {},
189 };
190 let ahdr = NAAudioInfo::new(arate, chans, fmt, 0);
191 let ainfo = NACodecInfo::new(aname, NACodecTypeInfo::Audio(ahdr), None);
192 if strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, arate, 0)).is_none() {
193 return Err(DemuxerError::MemoryError);
194 }
195 }
196
197 Ok(())
198 }
199 fn parse_sanm_header(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
200 let src = &mut self.src;
201
202 let tag = src.read_tag()?;
203 validate!(&tag == b"SHDR");
204 let size = u64::from(src.read_u32be()?);
205 validate!(src.tell() + size <= self.size);
206 validate!(size >= 0x426);
207
208 let maj_ver = src.read_byte()?;
209 let min_ver = src.read_byte()?;
210 if maj_ver != 1 || min_ver != 0 {
211 return Err(DemuxerError::NotImplemented);
212 }
213 self.nframes = src.read_u16le()? as usize;
214 let _xoff = src.read_u16le()? as usize;
215 let _yoff = src.read_u16le()? as usize;
216 let width = src.read_u16le()? as usize;
217 let height = src.read_u16le()? as usize;
218 let _imgtype = src.read_u16le()?;
219 let frame_delay = src.read_u32le()?;
220 let _max_frame_size = src.read_u32le()?;
221 src.read_skip(1024)?; // palette
222 src.read_skip((size as usize) - 0x416)?;
223
224 let tag = src.read_tag()?;
225 validate!(&tag == b"FLHD");
226 let size = u64::from(src.read_u32be()?);
227 let end = src.tell() + size;
228
229 let mut arate = 0;
230 let mut chans = 0;
231 let mut alen = 0;
232 while src.tell() < end {
233 let tag = src.read_tag()?;
234 if src.tell() == end { break; }
235 let size = src.read_u32be()?;
236 match &tag {
237 b"Wave" => {
238 validate!(size >= 8);
239 arate = src.read_u32le()?;
240 let cc = src.read_u32le()?;
241 validate!(cc == 1 || cc == 2);
242 chans = cc as u8;
243 if size >= 12 {
244 alen = u64::from(src.read_u32le()? / cc / 2);
245 src.read_skip((size as usize) - 12)?;
246 }
247 },
248 _ => src.read_skip(size as usize)?,
249 };
250 }
251 validate!(src.tell() == end);
252
253 let vhdr = NAVideoInfo::new(width, height, false, RGB565_FORMAT);
254 let vci = NACodecTypeInfo::Video(vhdr);
255 let vinfo = NACodecInfo::new("smushv2", vci, None);
256 if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, frame_delay, 1000000, self.nframes as u64)).is_none() {
257 return Err(DemuxerError::MemoryError);
258 }
259 if arate != 0 {
260 let ahdr = NAAudioInfo::new(arate, chans, SND_S16P_FORMAT, 0);
261 let ainfo = NACodecInfo::new("smush-vima", NACodecTypeInfo::Audio(ahdr), None);
262 if strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, arate, alen)).is_none() {
263 return Err(DemuxerError::MemoryError);
264 }
265 }
266
267 Ok(())
268 }
269
270 fn queue_chunk(&mut self, tag: [u8; 4], size: usize) -> DemuxerResult<()> {
271 self.chunks.extend_from_slice(&tag);
272 let start = self.chunks.len();
273 let nlen = start + size + 4;
274 self.chunks.resize(nlen, 0);
275 write_u32be(&mut self.chunks[start..], size as u32).unwrap();
276 self.src.read_buf(&mut self.chunks[start + 4..])?;
277 Ok(())
278 }
279 fn get_frame_anim(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
280 loop {
281 if self.src.tell() >= self.frme_end {
282 if !self.chunks.is_empty() {
283 let mut buf = Vec::new();
284 std::mem::swap(&mut self.chunks, &mut buf);
285 let stream = strmgr.get_stream(0).unwrap();
286 let (tb_num, tb_den) = stream.get_timebase();
287 let ts = NATimeInfo::new(Some(self.cur_frame as u64 - 1), None, None, tb_num, tb_den);
288 return Ok(NAPacket::new(stream, ts, false, buf));
289 }
290 if self.cur_frame == self.nframes {
291 return Err(DemuxerError::EOF);
292 }
293 let tag = self.src.read_tag()?;
294 validate!(&tag == b"FRME");
295 let size = u64::from(self.src.read_u32be()?);
296 self.frme_end = self.src.tell() + size;
297
298 self.chunks.clear();
299 self.cur_frame += 1;
300 if size == 0 {
301 continue;
302 }
303 }
304 let tag = self.src.read_tag()?;
305 let size = u64::from(self.src.read_u32be()?);
306 let tend = self.src.tell() + size;
307 validate!(tend <= self.frme_end);
308 match &tag {
309 b"STOR" | b"FTCH" | b"NPAL" | b"XPAL" | b"FOBJ" => {
310 self.queue_chunk(tag, size as usize)?;
311 },
312 b"IACT" => {
313 validate!(size >= 4);
314 let opcode = self.src.read_u16le()?;
315 let flags = self.src.read_u16le()?;
316 if (opcode == 8) && (flags == 0x2E) {
317 if let Some(stream) = strmgr.get_stream(1) {
318 let (tb_num, tb_den) = stream.get_timebase();
319 let ts = NATimeInfo::new(None, None, None, tb_num, tb_den);
320
321 let mut buf = vec![0; size as usize];
322 write_u16le(&mut buf[0..2], opcode).unwrap();
323 write_u16le(&mut buf[2..4], flags).unwrap();
324 self.src.read_buf(&mut buf[4..])?;
325
326 if (self.src.tell() & 1) == 1 {
327 if let Ok(0) = self.src.peek_byte() {
328 self.src.read_skip(1)?;
329 }
330 }
331 return Ok(NAPacket::new(stream, ts, true, buf));
332 }
333 }
334 self.src.read_skip((size as usize) - 4)?;
335 },
336 b"PSAD" => {
337 if size > 0x30 {
338 self.src.read_skip(0x30)?;
339 if let Some(stream) = strmgr.get_stream(1) {
340 let (tb_num, tb_den) = stream.get_timebase();
341
342 let audio_size = size - 0x30;
343 let ts = NATimeInfo::new(Some(self.asize), None, None, tb_num, tb_den);
344 let pkt = self.src.read_packet(stream, ts, true, audio_size as usize)?;
345 self.asize += audio_size;
346 if (self.src.tell() & 1) == 1 {
347 if let Ok(0) = self.src.peek_byte() {
348 self.src.read_skip(1)?;
349 }
350 }
351 return Ok(pkt);
352 } else {
353 self.src.read_skip((size - 0x30) as usize)?;
354 }
355 } else {
356 self.src.read_skip(size as usize)?;
357 }
358 },
359 _ => {
360 self.src.read_skip(size as usize)?;
361 },
362 };
363 if (self.src.tell() & 1) == 1 {
364 if let Ok(0) = self.src.peek_byte() {
365 self.src.read_skip(1)?;
366 }
367 }
368 }
369 }
370 fn get_frame_sanm(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
371 loop {
372 if self.src.tell() >= self.frme_end {
373 if !self.chunks.is_empty() {
374 let mut buf = Vec::new();
375 std::mem::swap(&mut self.chunks, &mut buf);
376 let stream = strmgr.get_stream(0).unwrap();
377 let (tb_num, tb_den) = stream.get_timebase();
378 let ts = NATimeInfo::new(Some(self.cur_frame as u64 - 1), None, None, tb_num, tb_den);
379 return Ok(NAPacket::new(stream, ts, self.keyframe, buf));
380 }
381 if self.cur_frame == self.nframes {
382 return Err(DemuxerError::EOF);
383 }
384 let tag = self.src.read_tag()?;
385 let size = u64::from(self.src.read_u32be()?);
386 self.frme_end = self.src.tell() + size;
387 match &tag {
388 b"FLHD" => { self.keyframe = true; },
389 b"FRME" => { self.keyframe = false; },
390 _ => {
391 self.src.read_skip(size as usize)?;
392 continue;
393 },
394 };
395
396 self.chunks.clear();
397 self.cur_frame += 1;
398 if size == 0 {
399 continue;
400 }
401 }
402 let tag = self.src.read_tag()?;
403 if self.src.tell() >= self.frme_end { // happens after some Wave tags
404 continue;
405 }
406 let size = u64::from(self.src.read_u32be()?);
407 let tend = self.src.tell() + size;
408 validate!(tend <= self.frme_end);
409 match &tag {
410 b"Bl16" => {
411 self.queue_chunk(tag, size as usize)?;
412 },
413 b"Wave" => {
414 if let Some(stream) = strmgr.get_stream(1) {
415 let mut buf = [0; 12];
416 let mut nsamples = 0;
417 if size >= 12 {
418 self.src.peek_buf(&mut buf)?;
419 nsamples = read_u32be(&buf[0..])?;
420 if nsamples == 0xFFFFFFFF {
421 nsamples = read_u32be(&buf[8..])?;
422 }
423 }
424
425 let (tb_num, tb_den) = stream.get_timebase();
426 let mut ts = NATimeInfo::new(None, None, None, tb_num, tb_den);
427 if nsamples != 0 {
428 ts.pts = Some(self.asize);
429 self.asize += u64::from(nsamples);
430 }
431 let pkt = self.src.read_packet(stream, ts, true, size as usize)?;
432 return Ok(pkt);
433 } else {
434 self.src.read_skip(size as usize)?;
435 }
436 },
437 _ => {
438//println!("unknown tag {}{}{}{} size {:X} @ {:X}", tag[0] as char, tag[1] as char, tag[2] as char, tag[3] as char, size, self.src.tell() - 8);
439 self.src.read_skip(size as usize)?;
440 },
441 };
442 }
443 }
444}
445
446impl<'a> DemuxCore<'a> for SmushDemuxer<'a> {
447 fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
448 let magic = self.src.read_tag()?;
449 match &magic {
450 b"ANIM" => {
451 self.old = true;
452 },
453 b"SANM" => {
454 self.old = false;
455 },
456 _ => return Err(DemuxerError::InvalidData),
457 };
458 self.size = u64::from(self.src.read_u32be()?);
459 if self.old {
460 self.parse_anim_header(strmgr)?;
461 } else {
462 self.parse_sanm_header(strmgr)?;
463 }
464
465 self.cur_frame = 0;
466 Ok(())
467 }
468
469 fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
470 if self.cur_frame > self.nframes { return Err(DemuxerError::EOF); }
471 if self.old {
472 self.get_frame_anim(strmgr)
473 } else {
474 self.get_frame_sanm(strmgr)
475 }
476 }
477
478 fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
479 Err(DemuxerError::NotPossible)
480 }
481 fn get_duration(&self) -> u64 { 0 }
482}
483
484impl<'a> NAOptionHandler for SmushDemuxer<'a> {
485 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
486 fn set_options(&mut self, _options: &[NAOption]) { }
487 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
488}
489
490pub struct SmushDemuxerCreator { }
491
492impl DemuxerCreator for SmushDemuxerCreator {
493 fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
494 Box::new(SmushDemuxer::new(br))
495 }
496 fn get_name(&self) -> &'static str { "smush" }
497}
498
499#[cfg(test)]
500mod test {
501 use super::*;
502 use std::fs::File;
503
504 #[test]
505 fn test_smush_demux_anim_v1() {
506 // sample from Rebel Assault game
507 let mut file = File::open("assets/Game/smush/c1block.anm").unwrap();
508 let mut fr = FileReader::new_read(&mut file);
509 let mut br = ByteReader::new(&mut fr);
510 let mut dmx = SmushDemuxer::new(&mut br);
511 let mut sm = StreamManager::new();
512 let mut si = SeekIndex::new();
513 dmx.open(&mut sm, &mut si).unwrap();
514 loop {
515 let pktres = dmx.get_frame(&mut sm);
516 if let Err(e) = pktres {
517 if (e as i32) == (DemuxerError::EOF as i32) { break; }
518 panic!("error");
519 }
520 let pkt = pktres.unwrap();
521 println!("Got {}", pkt);
522 }
523 }
524 #[test]
525 fn test_smush_demux_anim_v2() {
526 // sample from The Dig
527 let mut file = File::open("assets/Game/smush/PIGOUT.SAN").unwrap();
528 let mut fr = FileReader::new_read(&mut file);
529 let mut br = ByteReader::new(&mut fr);
530 let mut dmx = SmushDemuxer::new(&mut br);
531 let mut sm = StreamManager::new();
532 let mut si = SeekIndex::new();
533 dmx.open(&mut sm, &mut si).unwrap();
534 loop {
535 let pktres = dmx.get_frame(&mut sm);
536 if let Err(e) = pktres {
537 if (e as i32) == (DemuxerError::EOF as i32) { break; }
538 panic!("error");
539 }
540 let pkt = pktres.unwrap();
541 println!("Got {}", pkt);
542 }
543 }
544 #[test]
545 fn test_smush_demux_sanm() {
546 // sample from Grim Fandango
547 let mut file = File::open("assets/Game/smush/lol.snm").unwrap();
548 let mut fr = FileReader::new_read(&mut file);
549 let mut br = ByteReader::new(&mut fr);
550 let mut dmx = SmushDemuxer::new(&mut br);
551 let mut sm = StreamManager::new();
552 let mut si = SeekIndex::new();
553 dmx.open(&mut sm, &mut si).unwrap();
554 loop {
555 let pktres = dmx.get_frame(&mut sm);
556 if let Err(e) = pktres {
557 if (e as i32) == (DemuxerError::EOF as i32) { break; }
558 panic!("error");
559 }
560 let pkt = pktres.unwrap();
561 println!("Got {}", pkt);
562 }
563 }
564}