| 1 | use nihav_core::frame::*; |
| 2 | use nihav_core::demuxers::*; |
| 3 | |
| 4 | struct 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 | |
| 18 | fn 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 | |
| 62 | impl<'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 | |
| 446 | impl<'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 | |
| 484 | impl<'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 | |
| 490 | pub struct SmushDemuxerCreator { } |
| 491 | |
| 492 | impl 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)] |
| 500 | mod 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 | } |