]>
Commit | Line | Data |
---|---|---|
9067c1f8 KS |
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; } | |
da736dbd | 87 | self.data[self.pos] = self.data[saddr as usize]; |
9067c1f8 KS |
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 { | |
2422d969 | 100 | info: NACodecInfoRef, |
9067c1f8 KS |
101 | pal: [u8; 768], |
102 | frame: [u8; FRAME_W * FRAME_H], | |
103 | } | |
104 | ||
105 | impl BMVVideoDecoder { | |
106 | fn new() -> Self { | |
9067c1f8 | 107 | Self { |
2422d969 | 108 | info: NACodecInfoRef::default(), pal: [0; 768], frame: [0; FRAME_W * FRAME_H], |
9067c1f8 KS |
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); | |
1a967e6b | 116 | let data = buf.get_data_mut().unwrap(); |
9067c1f8 KS |
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; | |
d24468d9 | 145 | |
9067c1f8 KS |
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 { | |
01613464 | 168 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
9067c1f8 KS |
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)); | |
2422d969 | 177 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); |
9067c1f8 KS |
178 | |
179 | Ok(()) | |
180 | } else { | |
181 | Err(DecoderError::InvalidData) | |
182 | } | |
183 | } | |
01613464 | 184 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
9067c1f8 KS |
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 | ||
e69b1148 | 209 | let mut bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?; |
9067c1f8 KS |
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 }); | |
171860fc | 217 | Ok(frm.into_ref()) |
9067c1f8 | 218 | } |
f9be4e75 KS |
219 | fn flush(&mut self) { |
220 | } | |
9067c1f8 KS |
221 | } |
222 | ||
7d57ae2f KS |
223 | impl NAOptionHandler for BMVVideoDecoder { |
224 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
225 | fn set_options(&mut self, _options: &[NAOption]) { } | |
226 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
227 | } | |
228 | ||
9067c1f8 | 229 | |
08a1fab7 | 230 | pub fn get_decoder_video() -> Box<dyn NADecoder + Send> { |
9067c1f8 KS |
231 | Box::new(BMVVideoDecoder::new()) |
232 | } | |
233 | ||
234 | struct BMVAudioDecoder { | |
235 | ainfo: NAAudioInfo, | |
236 | chmap: NAChannelMap, | |
237 | } | |
238 | ||
239 | const BMV_AUD_SCALES: [i32; 16] = [ 16512, 8256, 4128, 2064, 1032, 516, 258, 192, 129, 88, 64, 56, 48, 40, 36, 32 ]; | |
240 | ||
241 | impl BMVAudioDecoder { | |
242 | fn new() -> Self { | |
243 | Self { | |
244 | ainfo: NAAudioInfo::new(0, 1, formats::SND_S16P_FORMAT, 0), | |
245 | chmap: NAChannelMap::new(), | |
246 | } | |
247 | } | |
248 | } | |
249 | ||
250 | fn scale_sample(samp: u8, scale: i32) -> i16 { | |
e69b1148 | 251 | let val = (i32::from(samp as i8) * scale) >> 5; |
9067c1f8 KS |
252 | if val < -32768 { |
253 | -32768 | |
254 | } else if val > 32767 { | |
255 | 32767 | |
256 | } else { | |
257 | val as i16 | |
258 | } | |
259 | } | |
260 | ||
261 | impl NADecoder for BMVAudioDecoder { | |
01613464 | 262 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { |
9067c1f8 KS |
263 | if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { |
264 | self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), formats::SND_S16P_FORMAT, 32); | |
265 | self.chmap = NAChannelMap::from_str("L,R").unwrap(); | |
266 | Ok(()) | |
267 | } else { | |
268 | Err(DecoderError::InvalidData) | |
269 | } | |
270 | } | |
01613464 | 271 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { |
9067c1f8 KS |
272 | let info = pkt.get_stream().get_info(); |
273 | if let NACodecTypeInfo::Audio(_) = info.get_properties() { | |
274 | let pktbuf = pkt.get_buffer(); | |
275 | validate!(pktbuf.len() > 1); | |
276 | let nblocks = pktbuf[0] as usize; | |
277 | validate!(pktbuf.len() == 1 + 65 * nblocks); | |
278 | let samples = nblocks * 32; | |
b70cc006 | 279 | let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; |
9067c1f8 KS |
280 | let mut adata = abuf.get_abuf_i16().unwrap(); |
281 | let off1 = adata.get_offset(1); | |
1a967e6b | 282 | let dst = adata.get_data_mut().unwrap(); |
9067c1f8 KS |
283 | let psrc = &pktbuf[1..]; |
284 | for (n, src) in psrc.chunks_exact(65).enumerate() { | |
285 | let code = src[0].rotate_right(1); | |
286 | let scale0 = BMV_AUD_SCALES[(code & 0xF) as usize]; | |
287 | let scale1 = BMV_AUD_SCALES[(code >> 4) as usize]; | |
288 | let aoff0 = n * 32; | |
289 | let aoff1 = aoff0 + off1; | |
290 | let data = &src[1..]; | |
291 | for (i, samp) in data.chunks_exact(2).enumerate() { | |
292 | dst[aoff0 + i] = scale_sample(samp[0], scale0); | |
293 | dst[aoff1 + i] = scale_sample(samp[1], scale1); | |
294 | } | |
295 | } | |
296 | let mut frm = NAFrame::new_from_pkt(pkt, info, abuf); | |
297 | frm.set_duration(Some(samples as u64)); | |
298 | frm.set_keyframe(false); | |
171860fc | 299 | Ok(frm.into_ref()) |
9067c1f8 KS |
300 | } else { |
301 | Err(DecoderError::InvalidData) | |
302 | } | |
303 | } | |
f9be4e75 KS |
304 | fn flush(&mut self) { |
305 | } | |
9067c1f8 KS |
306 | } |
307 | ||
7d57ae2f KS |
308 | impl NAOptionHandler for BMVAudioDecoder { |
309 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
310 | fn set_options(&mut self, _options: &[NAOption]) { } | |
311 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
312 | } | |
313 | ||
08a1fab7 | 314 | pub fn get_decoder_audio() -> Box<dyn NADecoder + Send> { |
9067c1f8 KS |
315 | Box::new(BMVAudioDecoder::new()) |
316 | } | |
317 | ||
318 | #[cfg(test)] | |
319 | mod test { | |
320 | use nihav_core::codecs::RegisteredDecoders; | |
321 | use nihav_core::demuxers::RegisteredDemuxers; | |
ce742854 | 322 | use nihav_codec_support::test::dec_video::*; |
78fb6560 | 323 | use crate::game_register_all_decoders; |
e64739f8 | 324 | use crate::game_register_all_demuxers; |
9067c1f8 KS |
325 | #[test] |
326 | fn test_bmv_video() { | |
327 | let mut dmx_reg = RegisteredDemuxers::new(); | |
328 | game_register_all_demuxers(&mut dmx_reg); | |
329 | let mut dec_reg = RegisteredDecoders::new(); | |
78fb6560 | 330 | game_register_all_decoders(&mut dec_reg); |
9067c1f8 | 331 | |
951d4be7 KS |
332 | test_decoding("bmv", "bmv-video", "assets/Game/WILDCAT.BMV", Some(40), &dmx_reg, &dec_reg, |
333 | ExpectedTestResult::MD5([0x9e91bb16, 0xc1edafc9, 0x4ef3171f, 0x0f3f6181])); | |
9067c1f8 KS |
334 | } |
335 | #[test] | |
336 | fn test_bmv_audio() { | |
337 | let mut dmx_reg = RegisteredDemuxers::new(); | |
338 | game_register_all_demuxers(&mut dmx_reg); | |
339 | let mut dec_reg = RegisteredDecoders::new(); | |
78fb6560 | 340 | game_register_all_decoders(&mut dec_reg); |
9067c1f8 | 341 | |
951d4be7 KS |
342 | test_decoding("bmv", "bmv-audio", "assets/Game/PERFECT.BMV", None, &dmx_reg, &dec_reg, |
343 | ExpectedTestResult::MD5([0x90b9ace4, 0x5fc19938, 0x7f534560, 0x32589cdf])); | |
9067c1f8 KS |
344 | } |
345 | } |