]>
Commit | Line | Data |
---|---|---|
1 | use nihav_core::formats; | |
2 | use nihav_core::codecs::*; | |
3 | use nihav_core::io::byteio::*; | |
4 | use std::str::FromStr; | |
5 | ||
6 | const FRAME_W: usize = 640; | |
7 | const FRAME_H: usize = 429; | |
8 | ||
9 | const BMV_INTRA: u8 = 0x03; | |
10 | const BMV_SCROLL: u8 = 0x04; | |
11 | const BMV_PAL: u8 = 0x08; | |
12 | const BMV_COMMAND: u8 = 0x10; | |
13 | const BMV_PRINT: u8 = 0x80; | |
14 | ||
15 | struct BMVReader<'a> { | |
16 | src: &'a [u8], | |
17 | pos: usize, | |
18 | fwd: bool, | |
19 | nibble: u8, | |
20 | saved: bool, | |
21 | } | |
22 | ||
23 | impl<'a> BMVReader<'a> { | |
24 | fn new(src: &'a [u8], fwd: bool) -> Self { | |
25 | let pos = if fwd { 0 } else { src.len() - 1 }; | |
26 | Self { src, pos, fwd, nibble: 0, saved: false } | |
27 | } | |
28 | fn advance(&mut self) { | |
29 | if self.fwd { | |
30 | if self.pos < self.src.len() - 1 { self.pos += 1; } | |
31 | } else { | |
32 | if self.pos > 0 { self.pos -= 1; } | |
33 | } | |
34 | } | |
35 | fn get_byte(&mut self) -> u8 { | |
36 | let ret = self.src[self.pos]; | |
37 | self.advance(); | |
38 | ret | |
39 | } | |
40 | fn get_nibble(&mut self) -> u8 { | |
41 | if self.saved { | |
42 | self.saved = false; | |
43 | self.nibble | |
44 | } else { | |
45 | let val = self.get_byte(); | |
46 | self.saved = true; | |
47 | self.nibble = val >> 4; | |
48 | val & 0xF | |
49 | } | |
50 | } | |
51 | } | |
52 | ||
53 | struct BMVWriter<'a> { | |
54 | data: &'a mut [u8], | |
55 | pos: usize, | |
56 | fwd: bool, | |
57 | off: isize, | |
58 | } | |
59 | ||
60 | impl<'a> BMVWriter<'a> { | |
61 | fn new(data: &'a mut [u8], fwd: bool, off: isize) -> Self { | |
62 | let pos = if fwd { 0 } else { data.len() - 1 }; | |
63 | Self { data, pos, fwd, off } | |
64 | } | |
65 | fn is_at_end(&self) -> bool { | |
66 | if self.fwd { | |
67 | self.pos == self.data.len() - 1 | |
68 | } else { | |
69 | self.pos == 0 | |
70 | } | |
71 | } | |
72 | fn advance(&mut self) { | |
73 | if self.fwd { | |
74 | if self.pos < self.data.len() - 1 { self.pos += 1; } | |
75 | } else { | |
76 | if self.pos > 0 { self.pos -= 1; } | |
77 | } | |
78 | } | |
79 | fn put_byte(&mut self, val: u8) { | |
80 | self.data[self.pos] = val; | |
81 | self.advance(); | |
82 | } | |
83 | fn copy(&mut self, len: usize) { | |
84 | for _ in 0..len { | |
85 | let saddr = (self.pos as isize) + self.off; | |
86 | if saddr < 0 { continue; } | |
87 | self.data[self.pos] = self.data[saddr as usize]; | |
88 | self.advance(); | |
89 | } | |
90 | } | |
91 | fn repeat(&mut self, len: usize) { | |
92 | let last = if self.fwd { self.data[self.pos - 1] } else { self.data[self.pos + 1] }; | |
93 | for _ in 0..len { | |
94 | self.put_byte(last); | |
95 | } | |
96 | } | |
97 | } | |
98 | ||
99 | struct BMVVideoDecoder { | |
100 | info: NACodecInfoRef, | |
101 | pal: [u8; 768], | |
102 | frame: [u8; FRAME_W * FRAME_H], | |
103 | } | |
104 | ||
105 | impl BMVVideoDecoder { | |
106 | fn new() -> Self { | |
107 | Self { | |
108 | info: NACodecInfoRef::default(), pal: [0; 768], frame: [0; FRAME_W * FRAME_H], | |
109 | } | |
110 | } | |
111 | fn decode_frame(&mut self, src: &[u8], bufinfo: &mut NABufferType, line: i16) -> DecoderResult<()> { | |
112 | let bufo = bufinfo.get_vbuf(); | |
113 | let mut buf = bufo.unwrap(); | |
114 | let paloff = buf.get_offset(1); | |
115 | let stride = buf.get_stride(0); | |
116 | let data = buf.get_data_mut().unwrap(); | |
117 | let dst = data.as_mut_slice(); | |
118 | ||
119 | let fwd = (line <= -640) || (line >= 0); | |
120 | ||
121 | let mut br = BMVReader::new(src, fwd); | |
122 | let mut bw = BMVWriter::new(&mut self.frame, fwd, line as isize); | |
123 | let mut mode = 0; | |
124 | while !bw.is_at_end() { | |
125 | let mut val = 0; | |
126 | let mut shift = 0; | |
127 | loop { | |
128 | let nib = br.get_nibble() as usize; | |
129 | if (nib & 0xC) != 0 { | |
130 | val |= nib << shift; | |
131 | break; | |
132 | } | |
133 | val |= nib << shift; | |
134 | shift += 2; | |
135 | } | |
136 | if (val & 1) != 0 { | |
137 | mode += 1; | |
138 | } | |
139 | mode += 1; | |
140 | if mode >= 4 { | |
141 | mode -= 3; | |
142 | } | |
143 | validate!(val >= 2); | |
144 | let len = (val >> 1) - 1; | |
145 | ||
146 | match mode { | |
147 | 1 => bw.copy(len), | |
148 | 2 => for _ in 0..len { bw.put_byte(br.get_byte()); }, | |
149 | 3 => bw.repeat(len), | |
150 | _ => unreachable!(), | |
151 | }; | |
152 | } | |
153 | ||
154 | for y in 0..FRAME_H { | |
155 | for x in 0..FRAME_W { | |
156 | dst[y * stride + x] = self.frame[y * FRAME_W + x]; | |
157 | } | |
158 | } | |
159 | ||
160 | let dpal = &mut dst[paloff..][..768]; | |
161 | dpal.copy_from_slice(&self.pal[0..]); | |
162 | ||
163 | Ok(()) | |
164 | } | |
165 | } | |
166 | ||
167 | impl NADecoder for BMVVideoDecoder { | |
168 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
169 | if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() { | |
170 | let fmt = NAPixelFormaton::new(ColorModel::RGB(RGBSubmodel::RGB), | |
171 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 0, 3)), | |
172 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 1, 3)), | |
173 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 2, 3)), | |
174 | None, None, | |
175 | FORMATON_FLAG_PALETTE, 3); | |
176 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(FRAME_W, FRAME_H, false, fmt)); | |
177 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); | |
178 | ||
179 | Ok(()) | |
180 | } else { | |
181 | Err(DecoderError::InvalidData) | |
182 | } | |
183 | } | |
184 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
185 | let src = pkt.get_buffer(); | |
186 | validate!(src.len() > 1); | |
187 | ||
188 | let mut mr = MemoryReader::new_read(&src); | |
189 | let mut br = ByteReader::new(&mut mr); | |
190 | let flags = br.read_byte()?; | |
191 | ||
192 | if (flags & BMV_COMMAND) != 0 { | |
193 | let size = if (flags & BMV_PRINT) != 0 { 8 } else { 10 }; | |
194 | br.read_skip(size)?; | |
195 | } | |
196 | if (flags & BMV_PAL) != 0 { | |
197 | br.read_buf(&mut self.pal)?; | |
198 | } | |
199 | let line; | |
200 | if (flags & BMV_SCROLL) != 0 { | |
201 | line = br.read_u16le()? as i16; | |
202 | } else if (flags & BMV_INTRA) == BMV_INTRA { | |
203 | line = -640; | |
204 | } else { | |
205 | line = 0; | |
206 | } | |
207 | let pos = br.tell() as usize; | |
208 | ||
209 | let mut bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; | |
210 | ||
211 | self.decode_frame(&src[pos..], &mut bufinfo, line)?; | |
212 | ||
213 | let is_intra = (flags & BMV_INTRA) == BMV_INTRA; | |
214 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); | |
215 | frm.set_keyframe(is_intra); | |
216 | frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); | |
217 | Ok(frm.into_ref()) | |
218 | } | |
219 | fn flush(&mut self) { | |
220 | } | |
221 | } | |
222 | ||
223 | ||
224 | pub fn get_decoder_video() -> Box<dyn NADecoder + Send> { | |
225 | Box::new(BMVVideoDecoder::new()) | |
226 | } | |
227 | ||
228 | struct BMVAudioDecoder { | |
229 | ainfo: NAAudioInfo, | |
230 | chmap: NAChannelMap, | |
231 | } | |
232 | ||
233 | const BMV_AUD_SCALES: [i32; 16] = [ 16512, 8256, 4128, 2064, 1032, 516, 258, 192, 129, 88, 64, 56, 48, 40, 36, 32 ]; | |
234 | ||
235 | impl BMVAudioDecoder { | |
236 | fn new() -> Self { | |
237 | Self { | |
238 | ainfo: NAAudioInfo::new(0, 1, formats::SND_S16P_FORMAT, 0), | |
239 | chmap: NAChannelMap::new(), | |
240 | } | |
241 | } | |
242 | } | |
243 | ||
244 | fn scale_sample(samp: u8, scale: i32) -> i16 { | |
245 | let val = (i32::from(samp as i8) * scale) >> 5; | |
246 | if val < -32768 { | |
247 | -32768 | |
248 | } else if val > 32767 { | |
249 | 32767 | |
250 | } else { | |
251 | val as i16 | |
252 | } | |
253 | } | |
254 | ||
255 | impl NADecoder for BMVAudioDecoder { | |
256 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
257 | if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { | |
258 | self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), formats::SND_S16P_FORMAT, 32); | |
259 | self.chmap = NAChannelMap::from_str("L,R").unwrap(); | |
260 | Ok(()) | |
261 | } else { | |
262 | Err(DecoderError::InvalidData) | |
263 | } | |
264 | } | |
265 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
266 | let info = pkt.get_stream().get_info(); | |
267 | if let NACodecTypeInfo::Audio(_) = info.get_properties() { | |
268 | let pktbuf = pkt.get_buffer(); | |
269 | validate!(pktbuf.len() > 1); | |
270 | let nblocks = pktbuf[0] as usize; | |
271 | validate!(pktbuf.len() == 1 + 65 * nblocks); | |
272 | let samples = nblocks * 32; | |
273 | let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; | |
274 | let mut adata = abuf.get_abuf_i16().unwrap(); | |
275 | let off1 = adata.get_offset(1); | |
276 | let dst = adata.get_data_mut().unwrap(); | |
277 | let psrc = &pktbuf[1..]; | |
278 | for (n, src) in psrc.chunks_exact(65).enumerate() { | |
279 | let code = src[0].rotate_right(1); | |
280 | let scale0 = BMV_AUD_SCALES[(code & 0xF) as usize]; | |
281 | let scale1 = BMV_AUD_SCALES[(code >> 4) as usize]; | |
282 | let aoff0 = n * 32; | |
283 | let aoff1 = aoff0 + off1; | |
284 | let data = &src[1..]; | |
285 | for (i, samp) in data.chunks_exact(2).enumerate() { | |
286 | dst[aoff0 + i] = scale_sample(samp[0], scale0); | |
287 | dst[aoff1 + i] = scale_sample(samp[1], scale1); | |
288 | } | |
289 | } | |
290 | let mut frm = NAFrame::new_from_pkt(pkt, info, abuf); | |
291 | frm.set_duration(Some(samples as u64)); | |
292 | frm.set_keyframe(false); | |
293 | Ok(frm.into_ref()) | |
294 | } else { | |
295 | Err(DecoderError::InvalidData) | |
296 | } | |
297 | } | |
298 | fn flush(&mut self) { | |
299 | } | |
300 | } | |
301 | ||
302 | pub fn get_decoder_audio() -> Box<dyn NADecoder + Send> { | |
303 | Box::new(BMVAudioDecoder::new()) | |
304 | } | |
305 | ||
306 | #[cfg(test)] | |
307 | mod test { | |
308 | use nihav_core::codecs::RegisteredDecoders; | |
309 | use nihav_core::demuxers::RegisteredDemuxers; | |
310 | use nihav_codec_support::test::dec_video::*; | |
311 | use crate::game_register_all_codecs; | |
312 | use crate::game_register_all_demuxers; | |
313 | #[test] | |
314 | fn test_bmv_video() { | |
315 | let mut dmx_reg = RegisteredDemuxers::new(); | |
316 | game_register_all_demuxers(&mut dmx_reg); | |
317 | let mut dec_reg = RegisteredDecoders::new(); | |
318 | game_register_all_codecs(&mut dec_reg); | |
319 | ||
320 | test_decoding("bmv", "bmv-video", "assets/Game/WILDCAT.BMV", Some(40), &dmx_reg, &dec_reg, | |
321 | ExpectedTestResult::MD5([0x9e91bb16, 0xc1edafc9, 0x4ef3171f, 0x0f3f6181])); | |
322 | } | |
323 | #[test] | |
324 | fn test_bmv_audio() { | |
325 | let mut dmx_reg = RegisteredDemuxers::new(); | |
326 | game_register_all_demuxers(&mut dmx_reg); | |
327 | let mut dec_reg = RegisteredDecoders::new(); | |
328 | game_register_all_codecs(&mut dec_reg); | |
329 | ||
330 | test_decoding("bmv", "bmv-audio", "assets/Game/PERFECT.BMV", None, &dmx_reg, &dec_reg, | |
331 | ExpectedTestResult::MD5([0x90b9ace4, 0x5fc19938, 0x7f534560, 0x32589cdf])); | |
332 | } | |
333 | } |