[features]
default = ["all_decoders", "all_demuxers"]
demuxers = []
-all_demuxers = ["demuxer_bmv", "demuxer_bmv3", "demuxer_fcmp", "demuxer_fst", "demuxer_gdv", "demuxer_vmd", "demuxer_vx"]
+all_demuxers = ["demuxer_bmv", "demuxer_bmv3", "demuxer_fcmp", "demuxer_fst", "demuxer_gdv", "demuxer_imax", "demuxer_vmd", "demuxer_vx"]
demuxer_bmv = ["demuxers"]
demuxer_bmv3 = ["demuxers"]
demuxer_fcmp = ["demuxers"]
demuxer_fst = ["demuxers"]
demuxer_gdv = ["demuxers"]
+demuxer_imax = ["demuxers"]
demuxer_vmd = ["demuxers"]
demuxer_vx = ["demuxers"]
all_decoders = ["all_video_decoders", "all_audio_decoders"]
decoders = []
-all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_fstvid", "decoder_gdvvid", "decoder_midivid", "decoder_midivid3", "decoder_vmd", "decoder_vx"]
+all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_fstvid", "decoder_gdvvid", "decoder_imax", "decoder_midivid", "decoder_midivid3", "decoder_vmd", "decoder_vx"]
decoder_bmv = ["decoders"]
decoder_bmv3 = ["decoders"]
decoder_fstvid = ["decoders"]
decoder_gdvvid = ["decoders"]
+decoder_imax = ["decoders"]
decoder_midivid = ["decoders"]
decoder_midivid3 = ["decoders"]
decoder_vmd = ["decoders"]
--- /dev/null
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+
+const FRAME_W: usize = 320;
+const FRAME_H: usize = 160;
+
+struct IMAXDecoder {
+ info: NACodecInfoRef,
+ pal: [u8; 768],
+ frame: [u8; FRAME_W * FRAME_H],
+ hist: [u8; 32768],
+ hist_pos: usize,
+}
+
+impl IMAXDecoder {
+ fn new() -> Self {
+ Self {
+ info: NACodecInfoRef::default(),
+ pal: [0; 768],
+ frame: [0; FRAME_W * FRAME_H],
+ hist: [0; 32768],
+ hist_pos: 0,
+ }
+ }
+}
+
+impl NADecoder for IMAXDecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() {
+ /*let fmt = NAPixelFormaton::new(ColorModel::RGB(RGBSubmodel::RGB),
+ Some(NAPixelChromaton::new(0, 0, true, 8, 0, 0, 3)),
+ Some(NAPixelChromaton::new(0, 0, true, 8, 0, 1, 3)),
+ Some(NAPixelChromaton::new(0, 0, true, 8, 0, 2, 3)),
+ None, None,
+ FORMATON_FLAG_PALETTE, 3);*/
+ let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(FRAME_W, FRAME_H, false, PAL8_FORMAT));
+ self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
+
+ Ok(())
+ } else {
+ Err(DecoderError::InvalidData)
+ }
+ }
+ fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+ let src = pkt.get_buffer();
+ validate!(src.len() > 0);
+
+ for sd in pkt.side_data.iter() {
+ if let NASideData::Palette(true, ref pal) = sd {
+ for (dst, src) in self.pal.chunks_mut(3).zip(pal.chunks(4)) {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[2];
+ }
+ break;
+ }
+ }
+
+ let mut mr = MemoryReader::new_read(&src);
+ let mut br = ByteReader::new(&mut mr);
+
+ let mut is_intra = true;
+ let mut is_skip = true;
+ let mut idx = 0;
+ while idx < self.frame.len() {
+ let v = br.read_byte()?;
+ let op = v >> 6;
+ let len = (v & 0x3F) as usize;
+ match op {
+ 0 => {
+ validate!(idx + len <= self.frame.len());
+ idx += len;
+ is_intra = false;
+ },
+ 1 => {
+ if len == 0 {
+ let off = br.read_u16le()? as usize;
+ let len = br.read_byte()? as usize;
+ validate!(idx + len <= self.frame.len());
+ validate!(off + len <= self.hist.len());
+ self.frame[idx..][..len].copy_from_slice(&self.hist[off..][..len]);
+ } else {
+ validate!(idx + len <= self.frame.len());
+ br.read_buf(&mut self.frame[idx..][..len])?;
+ if self.hist_pos + len <= self.hist.len() {
+ self.hist[self.hist_pos..][..len].copy_from_slice(&self.frame[idx..][..len]);
+ self.hist_pos += len;
+ }
+ idx += len;
+ }
+ is_skip = false;
+ },
+ 2 => {
+ let pix = br.read_byte()?;
+ validate!(idx + len <= self.frame.len());
+ for _ in 0..len {
+ self.frame[idx] = pix;
+ idx += 1;
+ }
+ is_skip = false;
+ },
+ _ => {
+ let len2 = br.read_byte()? as usize;
+ let skip_len = len * 64 + len2;
+ validate!(idx + skip_len <= self.frame.len());
+ idx += skip_len;
+ is_intra = false;
+ },
+ };
+ }
+
+ let bufinfo = if !is_skip {
+ let binfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?;
+ let mut vbuf = binfo.get_vbuf().unwrap();
+ let paloff = vbuf.get_offset(1);
+ let stride = vbuf.get_stride(0);
+ let data = vbuf.get_data_mut().unwrap();
+ for (drow, srow) in data.chunks_mut(stride).zip(self.frame.chunks(FRAME_W)) {
+ drow[..FRAME_W].copy_from_slice(srow);
+ }
+ data[paloff..][..768].copy_from_slice(&self.pal);
+ binfo
+ } else {
+ NABufferType::None
+ };
+
+ let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+ frm.set_keyframe(is_intra);
+ let ftype = if is_skip { FrameType::Skip } else if is_intra { FrameType::I } else { FrameType::P };
+ frm.set_frame_type(ftype);
+ Ok(frm.into_ref())
+ }
+ fn flush(&mut self) {
+ }
+}
+
+impl NAOptionHandler for IMAXDecoder {
+ fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+ fn set_options(&mut self, _options: &[NAOption]) { }
+ fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+ Box::new(IMAXDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+ use nihav_core::codecs::RegisteredDecoders;
+ use nihav_core::demuxers::RegisteredDemuxers;
+ use nihav_codec_support::test::dec_video::*;
+ use crate::game_register_all_decoders;
+ use crate::game_register_all_demuxers;
+ #[test]
+ fn test_imax_video() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ game_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ game_register_all_decoders(&mut dec_reg);
+
+ test_decoding("fable-imax", "fable-imax", "assets/Game/present.imx",
+ Some(64), &dmx_reg, &dec_reg,
+ ExpectedTestResult::MD5([0x775e1326, 0x7aa63674, 0x9b8aec54, 0x5caee2e3]));
+ }
+}
pub mod futurevision;
#[cfg(feature="decoder_gdvvid")]
pub mod gremlinvideo;
+#[cfg(feature="decoder_imax")]
+pub mod imax;
#[cfg(feature="decoder_lhst500f22")]
pub mod lhst500f22;
#[cfg(feature="decoder_midivid")]
DecoderInfo { name: "fst-audio", get_decoder: futurevision::get_decoder_audio },
#[cfg(feature="decoder_fstvid")]
DecoderInfo { name: "fst-video", get_decoder: futurevision::get_decoder_video },
+#[cfg(feature="decoder_imax")]
+ DecoderInfo { name: "fable-imax", get_decoder: imax::get_decoder },
#[cfg(feature="decoder_vmd")]
DecoderInfo { name: "vmd-audio", get_decoder: vmd::get_decoder_audio },
#[cfg(feature="decoder_vmd")]
--- /dev/null
+use nihav_core::frame::*;
+use nihav_core::demuxers::*;
+use std::sync::Arc;
+
+#[allow(dead_code)]
+struct IMAXDemuxer<'a> {
+ src: &'a mut ByteReader<'a>,
+ cur_frame: u64,
+ apos: u64,
+ a_id: usize,
+ v_id: usize,
+ pal: Arc<[u8; 1024]>,
+ pal_change: bool,
+}
+
+impl<'a> DemuxCore<'a> for IMAXDemuxer<'a> {
+ #[allow(unused_variables)]
+ fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+ let src = &mut self.src;
+
+ let magic = src.read_tag()?;
+ validate!(&magic == b"IMAX");
+ let nframes = u64::from(src.read_u32le()?);
+ let fps = u32::from(src.read_u16le()?);
+ let magic2 = src.read_u16le()?;
+ validate!(magic2 == 0x102);
+ let _zero = src.read_u16le()?;
+ let _max_vframe_size = src.read_u32le()?;
+ let _buffering_size = src.read_u32le()?;
+
+ let vhdr = NAVideoInfo::new(320, 160, false, PAL8_FORMAT);
+ let vci = NACodecTypeInfo::Video(vhdr);
+ let vinfo = NACodecInfo::new("fable-imax", vci, None);
+ self.v_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps, nframes)).unwrap();
+ let ahdr = NAAudioInfo::new(22050, 1, SND_U8_FORMAT, 2);
+ let ainfo = NACodecInfo::new("pcm", NACodecTypeInfo::Audio(ahdr), None);
+ self.a_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050, 2)).unwrap();
+ self.cur_frame = 0;
+ self.apos = 0;
+ Ok(())
+ }
+
+ #[allow(unused_variables)]
+ fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+ loop {
+ let fsize = self.src.read_u32le()? as usize;
+ let ftype = self.src.read_u32le()?;
+
+ match ftype {
+ 0xAA97 => {
+ let str = strmgr.get_stream(self.v_id).unwrap();
+ let (tb_num, tb_den) = str.get_timebase();
+ let ts = NATimeInfo::new(Some(self.cur_frame), None, None, tb_num, tb_den);
+ self.cur_frame += 1;
+ let mut pkt = self.src.read_packet(str, ts, true, fsize)?;
+ pkt.add_side_data(NASideData::Palette(self.pal_change, self.pal.clone()));
+ self.pal_change = false;
+ return Ok(pkt);
+ },
+ 0xAA98 => {
+ validate!(fsize == 768);
+ let mut pal = [0u8; 1024];
+ for chunk in pal.chunks_mut(4) {
+ let r = self.src.read_byte()?;
+ let g = self.src.read_byte()?;
+ let b = self.src.read_byte()?;
+ chunk[0] = (r << 2) | (r >> 4);
+ chunk[1] = (g << 2) | (g >> 4);
+ chunk[2] = (b << 2) | (b >> 4);
+ }
+ self.pal = Arc::new(pal);
+ self.pal_change = true;
+ },
+ 0xAA99 => {
+ let str = strmgr.get_stream(self.a_id).unwrap();
+ let (tb_num, tb_den) = str.get_timebase();
+ let ts = NATimeInfo::new(Some(self.apos), None, None, tb_num, tb_den);
+ self.apos += fsize as u64;
+ return self.src.read_packet(str, ts, true, fsize);
+ },
+ 0xAAFF => return Err(DemuxerError::EOF),
+ _ => return Err(DemuxerError::InvalidData),
+ }
+ }
+ }
+
+ fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+ Err(DemuxerError::NotPossible)
+ }
+ fn get_duration(&self) -> u64 { 0 }
+}
+impl<'a> NAOptionHandler for IMAXDemuxer<'a> {
+ fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+ fn set_options(&mut self, _options: &[NAOption]) { }
+ fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+impl<'a> IMAXDemuxer<'a> {
+ fn new(io: &'a mut ByteReader<'a>) -> Self {
+ IMAXDemuxer {
+ src: io,
+ cur_frame: 0,
+ apos: 0,
+ a_id: 0,
+ v_id: 0,
+ pal: Arc::new([0; 1024]),
+ pal_change: false,
+ }
+ }
+}
+
+pub struct IMAXDemuxerCreator { }
+
+impl DemuxerCreator for IMAXDemuxerCreator {
+ fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+ Box::new(IMAXDemuxer::new(br))
+ }
+ fn get_name(&self) -> &'static str { "fable-imax" }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn test_imax_demux() {
+ let mut file = File::open("assets/Game/present.imx").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = IMAXDemuxer::new(&mut br);
+ let mut sm = StreamManager::new();
+ let mut si = SeekIndex::new();
+ dmx.open(&mut sm, &mut si).unwrap();
+ loop {
+ let pktres = dmx.get_frame(&mut sm);
+ if let Err(e) = pktres {
+ if (e as i32) == (DemuxerError::EOF as i32) { break; }
+ panic!("error");
+ }
+ let pkt = pktres.unwrap();
+ println!("Got {}", pkt);
+ }
+ }
+}
mod fst;
#[cfg(feature="demuxer_gdv")]
mod gdv;
+#[cfg(feature="demuxer_imax")]
+mod imax;
#[cfg(feature="demuxer_vmd")]
mod vmd;
#[cfg(feature="demuxer_vx")]
&fst::FSTDemuxerCreator {},
#[cfg(feature="demuxer_gdv")]
&gdv::GDVDemuxerCreator {},
+#[cfg(feature="demuxer_imax")]
+ &imax::IMAXDemuxerCreator {},
#[cfg(feature="demuxer_vmd")]
&vmd::VMDDemuxerCreator {},
#[cfg(feature="demuxer_vx")]
extensions: ".gdv",
conditions: &[CheckItem{offs: 0, cond: &CC::Eq(Arg::U32LE(0x29111994))}],
},
+ DetectConditions {
+ demux_name: "fable-imax",
+ extensions: ".imx",
+ conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"IMAX") },
+ CheckItem{offs: 10, cond: &CC::Eq(Arg::U16LE(0x102)) }],
+ },
DetectConditions {
demux_name: "realaudio",
extensions: ".ra,.ram",
desc!(audio; "bmv-audio", "BMV audio"),
desc!(video; "bmv3-video", "DW Noir BMV video"),
desc!(audio; "bmv3-audio", "DW Noir BMV audio"),
+ desc!(video; "fable-imax", "Fable IMAX video"),
desc!(video; "fst-video", "FutureVision video"),
desc!(audio; "fst-audio", "FutureVision audio"),
desc!(video; "midivid", "MidiVid"),