game: add Discworld Noir BMV demuxer and audio decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 2 Apr 2019 09:59:58 +0000 (11:59 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Tue, 2 Apr 2019 09:59:58 +0000 (11:59 +0200)
nihav-core/src/detect.rs
nihav-core/src/register.rs
nihav-game/Cargo.toml
nihav-game/src/codecs/bmv3.rs [new file with mode: 0644]
nihav-game/src/codecs/mod.rs
nihav-game/src/demuxers/bmv.rs
nihav-game/src/demuxers/mod.rs

index 17b1411df7810fb8be38a9e4a37346206e178d32..caf590036dc43b245dd0d80b6c1e299ec2a464ca 100644 (file)
@@ -211,6 +211,12 @@ const DETECTORS: &[DetectConditions] = &[
         extensions: ".bmv",
         conditions: &[],
     },
+    DetectConditions {
+        demux_name: "bmv3",
+        extensions: ".bmv",
+        conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"BMVi") },
+                      CheckItem{offs: 32, cond: &CC::Str(b"DATA")}],
+    },
     DetectConditions {
         demux_name: "vmd",
         extensions: ".vmd",
index 56d2cfc99a2fa67bfd019887ada07161b6375e11..a03d7ca3477121eec288b63eb52d33709a5d7d67 100644 (file)
@@ -165,6 +165,8 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[
     desc!(audio;    "gdv-audio",     "Gremlin Digital Video - audio"),
     desc!(video;    "bmv-video",     "BMV video"),
     desc!(audio;    "bmv-audio",     "BMV audio"),
+    desc!(video;    "bmv3-video",    "DW Noir BMV video"),
+    desc!(audio;    "bmv3-audio",    "DW Noir BMV audio"),
     desc!(video;    "vmd-video",     "VMD video"),
     desc!(audio;    "vmd-audio",     "VMD audio"),
 
index 1da57042adadfcdae39b536a674ed376559063b3..3907a6f89b6b04fde7e9a0160a8ecc98cd2409c0 100644 (file)
@@ -11,16 +11,18 @@ features = []
 [features]
 default = ["all_decoders", "all_demuxers"]
 demuxers = []
-all_demuxers = ["demuxer_bmv", "demuxer_gdv", "demuxer_vmd"]
+all_demuxers = ["demuxer_bmv", "demuxer_bmv3", "demuxer_gdv", "demuxer_vmd"]
 demuxer_bmv = ["demuxers"]
+demuxer_bmv3 = ["demuxers"]
 demuxer_gdv = ["demuxers"]
 demuxer_vmd = ["demuxers"]
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 decoders = []
 
-all_video_decoders = ["decoder_bmv", "decoder_gdvvid", "decoder_vmd"]
+all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_gdvvid", "decoder_vmd"]
 decoder_bmv = ["decoders"]
+decoder_bmv3 = ["decoders"]
 decoder_gdvvid = ["decoders"]
 decoder_vmd = ["decoders"]
 
diff --git a/nihav-game/src/codecs/bmv3.rs b/nihav-game/src/codecs/bmv3.rs
new file mode 100644 (file)
index 0000000..2edd8c2
--- /dev/null
@@ -0,0 +1,211 @@
+use nihav_core::formats;
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use std::str::FromStr;
+
+
+pub fn get_decoder_video() -> Box<NADecoder> {
+    unimplemented!();
+}
+
+struct BMV3AudioDecoder {
+    ainfo:      NAAudioInfo,
+    chmap:      NAChannelMap,
+    pred:       [i16; 2],
+    nframes:    usize,
+}
+
+impl BMV3AudioDecoder {
+    fn new() -> Self {
+        Self {
+            ainfo:      NAAudioInfo::new(0, 1, formats::SND_S16P_FORMAT, 0),
+            chmap:      NAChannelMap::new(),
+            pred:       [0; 2],
+            nframes:    0,
+        }
+    }
+}
+
+fn decode_block(mode: u8, src: &[u8], dst: &mut [i16], mut pred: i16) -> i16 {
+    let steps = &BMV_AUDIO_STEPS[mode as usize];
+    let mut val2 = 0;
+    for i in 0..10 {
+        let val = (src[i * 2 + 0] as usize) + (src[i * 2 + 1] as usize) * 256;
+        pred = pred.wrapping_add(steps[(val >> 10) & 0x1F]);
+        dst[i * 3 + 0] = pred;
+        pred = pred.wrapping_add(steps[(val >>  5) & 0x1F]);
+        dst[i * 3 + 1] = pred;
+        pred = pred.wrapping_add(steps[(val >>  0) & 0x1F]);
+        dst[i * 3 + 2] = pred;
+        val2 = (val2 << 1) | (val >> 15);
+    }
+    pred = pred.wrapping_add(steps[(val2 >> 5) & 0x1F]);
+    dst[3 * 10 + 0] = pred;
+    pred = pred.wrapping_add(steps[(val2 >> 0) & 0x1F]);
+    dst[3 * 10 + 1] = pred;
+    pred
+}
+
+impl NADecoder for BMV3AudioDecoder {
+    fn init(&mut self, info: Rc<NACodecInfo>) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
+            self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), formats::SND_S16P_FORMAT, 32);
+            self.chmap = NAChannelMap::from_str("L,R").unwrap();
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let info = pkt.get_stream().get_info();
+        if let NACodecTypeInfo::Audio(_) = info.get_properties() {
+            let pktbuf = pkt.get_buffer();
+            validate!(pktbuf.len() > 1);
+            let samples = (pktbuf.len() / 41) * 32;
+            let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?;
+            let mut adata = abuf.get_abuf_i16().unwrap();
+            let off1 = adata.get_offset(1);
+            let mut dst = adata.get_data_mut();
+            let mut first = pktbuf[0] == 0;
+            let psrc = &pktbuf[1..];
+            for (n, src) in psrc.chunks_exact(41).enumerate() {
+                let aoff0 = n * 32;
+                let aoff1 = aoff0 + off1;
+                if first {
+                    let mode = src[40];
+                    self.pred[0] = decode_block(mode >> 4, &src[0..], &mut dst[aoff0..], self.pred[0]);
+                    self.pred[1] = decode_block(mode & 0xF, &src[20..], &mut dst[aoff1..], self.pred[1]);
+                } else {
+                    let mode = src[0];
+                    self.pred[0] = decode_block(mode >> 4, &src[1..], &mut dst[aoff0..], self.pred[0]);
+                    self.pred[1] = decode_block(mode & 0xF, &src[21..], &mut dst[aoff1..], self.pred[1]);
+                }
+                first = !first;
+            }
+            self.nframes += 1;
+            let mut frm = NAFrame::new_from_pkt(pkt, info, abuf);
+            frm.set_duration(Some(samples as u64));
+            frm.set_keyframe(false);
+            Ok(Rc::new(RefCell::new(frm)))
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+}
+
+pub fn get_decoder_audio() -> Box<NADecoder> {
+    Box::new(BMV3AudioDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_core::test::dec_video::*;
+    use crate::codecs::game_register_all_codecs;
+    use crate::demuxers::game_register_all_demuxers;
+    #[test]
+    fn test_bmv_video() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        game_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        game_register_all_codecs(&mut dec_reg);
+
+        let file = "assets/Game/DW3-Loffnote.bmv";
+        test_file_decoding("bmv3", file, Some(40), true, false, None, &dmx_reg, &dec_reg);
+    }
+    #[test]
+    fn test_bmv_audio() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        game_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        game_register_all_codecs(&mut dec_reg);
+
+        let file = "assets/Game/DW3-Loffnote.bmv";
+        test_decode_audio("bmv3", file, None, "bmv3", &dmx_reg, &dec_reg);
+    }
+}
+
+const BMV_AUDIO_STEPS: [[i16; 32]; 16] = [
+    [
+         0x0000,  0x0400,  0x0800,  0x0C00,  0x1000,  0x1400,  0x1800,  0x1C00,
+         0x2000,  0x2400,  0x2800,  0x2C00,  0x3000,  0x3400,  0x3800,  0x3C00,
+        -0x4000, -0x3C00, -0x3800, -0x3400, -0x3000, -0x2C00, -0x2800, -0x2400,
+        -0x2000, -0x1C00, -0x1800, -0x1400, -0x1000, -0x0C00, -0x0800, -0x0400
+    ], [
+         0x0000,  0x0200,  0x0400,  0x0600,  0x0800,  0x0A00,  0x0C00,  0x0E00,
+         0x1000,  0x1200,  0x1400,  0x1600,  0x1800,  0x1A00,  0x1C00,  0x1E00,
+        -0x2000, -0x1E00, -0x1C00, -0x1A00, -0x1800, -0x1600, -0x1400, -0x1200,
+        -0x1000, -0x0E00, -0x0C00, -0x0A00, -0x0800, -0x0600, -0x0400, -0x0200
+    ], [
+         0x0000,  0x0100,  0x0200,  0x0300,  0x0400,  0x0500,  0x0600,  0x0700,
+         0x0800,  0x0900,  0x0A00,  0x0B00,  0x0C00,  0x0D00,  0x0E00,  0x0F00,
+        -0x1000, -0x0F00, -0x0E00, -0x0D00, -0x0C00, -0x0B00, -0x0A00, -0x0900,
+        -0x0800, -0x0700, -0x0600, -0x0500, -0x0400, -0x0300, -0x0200, -0x0100
+    ], [
+         0x000,  0x080,  0x100,  0x180,  0x200,  0x280,  0x300,  0x380,
+         0x400,  0x480,  0x500,  0x580,  0x600,  0x680,  0x700,  0x780,
+        -0x800, -0x780, -0x700, -0x680, -0x600, -0x580, -0x500, -0x480,
+        -0x400, -0x380, -0x300, -0x280, -0x200, -0x180, -0x100, -0x080
+    ], [
+         0x000,  0x048,  0x090,  0x0D8,  0x120,  0x168,  0x1B0,  0x1F8,
+         0x240,  0x288,  0x2D0,  0x318,  0x360,  0x3A8,  0x3F0,  0x438,
+        -0x480, -0x438, -0x3F0, -0x3A8, -0x360, -0x318, -0x2D0, -0x288,
+        -0x240, -0x1F8, -0x1B0, -0x168, -0x120, -0x0D8, -0x090, -0x048
+    ], [
+         0x000,  0x030,  0x060,  0x090,  0x0C0,  0x0F0,  0x120,  0x150,
+         0x180,  0x1B0,  0x1E0,  0x210,  0x240,  0x270,  0x2A0,  0x2D0,
+        -0x300, -0x2D0, -0x2A0, -0x270, -0x240, -0x210, -0x1E0, -0x1B0,
+        -0x180, -0x150, -0x120, -0x0F0, -0x0C0, -0x090, -0x060, -0x030
+    ], [
+         0x000,  0x020,  0x040,  0x060,  0x080,  0x0A0,  0x0C0,  0x0E0,
+         0x100,  0x120,  0x140,  0x160,  0x180,  0x1A0,  0x1C0,  0x1E0,
+        -0x200, -0x1E0, -0x1C0, -0x1A0, -0x180, -0x160, -0x140, -0x120,
+        -0x100, -0x0E0, -0x0C0, -0x0A0, -0x080, -0x060, -0x040, -0x020
+    ], [
+         0x000,  0x016,  0x02C,  0x042,  0x058,  0x06E,  0x084,  0x09A,
+         0x0B0,  0x0C6,  0x0DC,  0x0F2,  0x108,  0x11E,  0x134,  0x14A,
+        -0x160, -0x14A, -0x134, -0x11E, -0x108, -0x0F2, -0x0DC, -0x0C6,
+        -0x0B0, -0x09A, -0x084, -0x06E, -0x058, -0x042, -0x02C, -0x016
+    ], [
+         0x000,  0x010,  0x020,  0x030,  0x040,  0x050,  0x060,  0x070,
+         0x080,  0x090,  0x0A0,  0x0B0,  0x0C0,  0x0D0,  0x0E0,  0x0F0,
+        -0x100, -0x0F0, -0x0E0, -0x0D0, -0x0C0, -0x0B0, -0x0A0, -0x090,
+        -0x080, -0x070, -0x060, -0x050, -0x040, -0x030, -0x020, -0x010
+    ], [
+         0x00,  0x0B,  0x16,  0x21,  0x2C,  0x37,  0x42,  0x4D,
+         0x58,  0x63,  0x6E,  0x79,  0x84,  0x8F,  0x9A,  0xA5,
+        -0xB0, -0xA5, -0x9A, -0x8F, -0x84, -0x79, -0x6E, -0x63,
+        -0x58, -0x4D, -0x42, -0x37, -0x2C, -0x21, -0x16, -0x0B
+    ], [
+         0x00,  0x08,  0x10,  0x18,  0x20,  0x28,  0x30,  0x38,
+         0x40,  0x48,  0x50,  0x58,  0x60,  0x68,  0x70,  0x78,
+        -0x80, -0x78, -0x70, -0x68, -0x60, -0x58, -0x50, -0x48,
+        -0x40, -0x38, -0x30, -0x28, -0x20, -0x18, -0x10, -0x08
+    ], [
+         0x00,  0x06,  0x0C,  0x12,  0x18,  0x1E,  0x24,  0x2A,
+         0x30,  0x36,  0x3C,  0x42,  0x48,  0x4E,  0x54,  0x5A,
+        -0x60, -0x5A, -0x54, -0x4E, -0x48, -0x42, -0x3C, -0x36,
+        -0x30, -0x2A, -0x24, -0x1E, -0x18, -0x12, -0x0C, -0x06
+    ], [
+         0x00,  0x04,  0x08,  0x0C,  0x10,  0x14,  0x18,  0x1C,
+         0x20,  0x24,  0x28,  0x2C,  0x30,  0x34,  0x38,  0x3C,
+        -0x40, -0x3C, -0x38, -0x34, -0x30, -0x2C, -0x28, -0x24,
+        -0x20, -0x1C, -0x18, -0x14, -0x10, -0x0C, -0x08, -0x04
+    ], [
+         0x00,  0x02,  0x05,  0x08,  0x0B,  0x0D,  0x10,  0x13,
+         0x16,  0x18,  0x1B,  0x1E,  0x21,  0x23,  0x26,  0x29,
+        -0x2C, -0x2A, -0x27, -0x24, -0x21, -0x1F, -0x1C, -0x19,
+        -0x16, -0x14, -0x11, -0x0E, -0x0B, -0x09, -0x06, -0x03
+    ], [
+         0x00,  0x01,  0x03,  0x05,  0x07,  0x08,  0x0A,  0x0C,
+         0x0E,  0x0F,  0x11,  0x13,  0x15,  0x16,  0x18,  0x1A,
+        -0x1C, -0x1B, -0x19, -0x17, -0x15, -0x14, -0x12, -0x10,
+        -0x0E, -0x0D, -0x0B, -0x09, -0x07, -0x06, -0x04, -0x02
+    ], [
+         0x00,  0x01,  0x02,  0x03,  0x04,  0x05,  0x06,  0x07,
+         0x08,  0x09,  0x0A,  0x0B,  0x0C,  0x0D,  0x0E,  0x0F,
+        -0x10, -0x0F, -0x0E, -0x0D, -0x0C, -0x0B, -0x0A, -0x09,
+        -0x08, -0x07, -0x06, -0x05, -0x04, -0x03, -0x02, -0x01
+    ]
+];
index fde660144eca543b3bbad99eadee77b57828d8f3..0cd5f3587853328e3823789c865106ad076e6823 100644 (file)
@@ -6,6 +6,8 @@ macro_rules! validate {
 
 #[cfg(feature="decoder_bmv")]
 pub mod bmv;
+#[cfg(feature="decoder_bmv3")]
+pub mod bmv3;
 #[cfg(feature="decoder_gdvvid")]
 pub mod gremlinvideo;
 #[cfg(feature="decoder_vmd")]
@@ -20,6 +22,10 @@ const GAME_CODECS: &[DecoderInfo] = &[
     DecoderInfo { name: "bmv-audio", get_decoder: bmv::get_decoder_audio },
 #[cfg(feature="decoder_bmv")]
     DecoderInfo { name: "bmv-video", get_decoder: bmv::get_decoder_video },
+#[cfg(feature="decoder_bmv3")]
+    DecoderInfo { name: "bmv3-audio", get_decoder: bmv3::get_decoder_audio },
+#[cfg(feature="decoder_bmv3")]
+    DecoderInfo { name: "bmv3-video", get_decoder: bmv3::get_decoder_video },
 #[cfg(feature="decoder_vmd")]
     DecoderInfo { name: "vmd-audio", get_decoder: vmd::get_decoder_audio },
 #[cfg(feature="decoder_vmd")]
index 8baa6e4eca3fbf77735e4f24f27c55b78a95193f..7553a94865b96b60479e744711c278a41f9919b2 100644 (file)
@@ -99,6 +99,145 @@ impl DemuxerCreator for BMVDemuxerCreator {
     fn get_name(&self) -> &'static str { "bmv" }
 }
 
+struct BMV3Demuxer<'a> {
+    src:        &'a mut ByteReader<'a>,
+    vid_id:     usize,
+    aud_id:     usize,
+    vpos:       u64,
+    apos:       u64,
+    asize:      usize,
+    ablob:      usize,
+    pkt_buf:    Vec<NAPacket>,
+}
+
+impl<'a> DemuxCore<'a> for BMV3Demuxer<'a> {
+    #[allow(unused_variables)]
+    fn open(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
+        let src = &mut self.src;
+
+        let mut magic = [0u8; 4];
+                                            src.read_buf(&mut magic)?;
+        validate!(&magic[0..] == b"BMVi");
+        let size                            = src.read_u32le()?;
+        validate!(size == 24);
+        let _slot_size                      = src.read_u32le()? as usize;
+        let nframes                         = src.read_u32le()? as usize;
+        let _prefetch_slots                 = src.read_u16le()?;
+        let _cache_size                     = src.read_u16le()?;
+        let fps                             = src.read_u16le()? as u32;
+        let audio_size                      = src.read_u16le()? as usize;
+        let audio_blob_size                 = src.read_u16le()? as usize;
+        let _audio_id                       = src.read_byte()?;
+        let _video_id                       = src.read_byte()?;
+        let width                           = src.read_u16le()? as usize;
+        let height                          = src.read_u16le()? as usize;
+        validate!(nframes > 0);
+        validate!((width > 0) && (width <= 640));
+        validate!((height > 0) && (height <= 432));
+        validate!((audio_size > audio_blob_size) && (audio_blob_size > 0) && (audio_size % audio_blob_size == 0));
+        let mut dta = [0u8; 4];
+                                            src.read_buf(&mut dta)?;
+        validate!(&dta[0..] == b"DATA");
+        let data_size                       = src.read_u32le()? as usize;
+        validate!(data_size > 0);
+        self.asize = audio_size;
+        self.ablob = audio_blob_size;
+
+        let vhdr = NAVideoInfo::new(width, height, false, RGB565_FORMAT);
+        let vci = NACodecTypeInfo::Video(vhdr);
+        let vinfo = NACodecInfo::new("bmv3-video", vci, None);
+        self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 256, fps)).unwrap();
+
+        let ahdr = NAAudioInfo::new(22050, 2, SND_S16_FORMAT, audio_blob_size);
+        let ainfo = NACodecInfo::new("bmv3-audio", NACodecTypeInfo::Audio(ahdr), None);
+        self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050)).unwrap();
+
+        self.vpos       = 0;
+        self.apos       = 0;
+        Ok(())
+    }
+
+    fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+        if self.pkt_buf.len() > 0 {
+            return Ok(self.pkt_buf.pop().unwrap());
+        }
+
+        loop {
+            let ctype                       = self.src.read_byte()?;
+            if ctype == 0 { // NOP chunk
+                continue;
+            }
+            if ctype == 1 { return Err(DemuxerError::EOF); }
+            let size                    = self.src.read_u24le()? as usize;
+            if size == 0 { continue; }
+            let asize;
+            if (ctype & 0x20) != 0 {
+                if (ctype & 0x40) != 0 {
+                    asize = self.asize - self.ablob;
+                } else {
+                    asize = self.asize;
+                }
+                validate!(asize <= size);
+                let mut buf: Vec<u8> = Vec::with_capacity(asize + 1);
+                buf.resize(asize + 1, 0);
+                buf[0] = (self.src.tell() & 1) as u8;
+                self.src.read_buf(&mut buf[1..])?;
+
+                let str = strmgr.get_stream(self.aud_id).unwrap();
+                let (tb_num, tb_den) = str.get_timebase();
+                let ts = NATimeInfo::new(Some(self.apos), None, None, tb_num, tb_den);
+                let apkt = NAPacket::new(str, ts, false, buf);
+
+                self.apos += (asize as u64) / 41 * 32;
+                self.pkt_buf.push(apkt);
+            } else {
+                asize = 0;
+            }
+            let mut buf: Vec<u8> = Vec::with_capacity(size - asize + 1);
+            buf.resize(size - asize + 1, 0);
+            buf[0] = ctype;
+            self.src.read_buf(&mut buf[1..])?;
+
+            let str = strmgr.get_stream(self.vid_id).unwrap();
+            let (tb_num, tb_den) = str.get_timebase();
+            let ts = NATimeInfo::new(Some(self.vpos), None, None, tb_num, tb_den);
+            let pkt = NAPacket::new(str, ts, (ctype & 3) == 3, buf);
+
+            self.vpos += 1;
+            return Ok(pkt);
+        }
+    }
+
+    #[allow(unused_variables)]
+    fn seek(&mut self, time: u64) -> DemuxerResult<()> {
+        Err(DemuxerError::NotImplemented)
+    }
+}
+
+impl<'a> BMV3Demuxer<'a> {
+    fn new(io: &'a mut ByteReader<'a>) -> Self {
+        Self {
+            src:        io,
+            vid_id:     0,
+            aud_id:     0,
+            vpos:       0,
+            apos:       0,
+            asize:      0,
+            ablob:      0,
+            pkt_buf:    Vec::with_capacity(1),
+        }
+    }
+}
+
+pub struct BMV3DemuxerCreator { }
+
+impl DemuxerCreator for BMV3DemuxerCreator {
+    fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<DemuxCore<'a> + 'a> {
+        Box::new(BMV3Demuxer::new(br))
+    }
+    fn get_name(&self) -> &'static str { "bmv3" }
+}
+
 #[cfg(test)]
 mod test {
     use super::*;
@@ -122,4 +261,22 @@ mod test {
             println!("Got {}", pkt);
         }
     }
+    #[test]
+    fn test_bmv3_demux() {
+        let mut file = File::open("assets/Game/DW3-Loffnote.bmv").unwrap();
+        let mut fr = FileReader::new_read(&mut file);
+        let mut br = ByteReader::new(&mut fr);
+        let mut dmx = BMV3Demuxer::new(&mut br);
+        let mut sm = StreamManager::new();
+        dmx.open(&mut sm).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);
+        }
+    }
 }
index 9d7d18728ceb0c85ac55c5d45eb2844dd040ea5e..884539c0f664fa78d31fb00ea16af60cdefc2188 100644 (file)
@@ -5,7 +5,7 @@ macro_rules! validate {
     ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DemuxerError::InvalidData); } };
 }
 
-#[cfg(feature="demuxer_bmv")]
+#[cfg(any(feature="demuxer_bmv",feature="demuxer_bmv3"))]
 mod bmv;
 #[cfg(feature="demuxer_gdv")]
 mod gdv;
@@ -15,6 +15,8 @@ mod vmd;
 const GAME_DEMUXERS: &[&'static DemuxerCreator] = &[
 #[cfg(feature="demuxer_bmv")]
     &bmv::BMVDemuxerCreator {},
+#[cfg(feature="demuxer_bmv3")]
+    &bmv::BMV3DemuxerCreator {},
 #[cfg(feature="demuxer_gdv")]
     &gdv::GDVDemuxerCreator {},
 #[cfg(feature="demuxer_vmd")]