]>
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 | if self.fwd { | |
88 | self.data[self.pos] = self.data[saddr as usize]; | |
89 | } else { | |
90 | self.data[self.pos] = self.data[saddr as usize]; | |
91 | } | |
92 | self.advance(); | |
93 | } | |
94 | } | |
95 | fn repeat(&mut self, len: usize) { | |
96 | let last = if self.fwd { self.data[self.pos - 1] } else { self.data[self.pos + 1] }; | |
97 | for _ in 0..len { | |
98 | self.put_byte(last); | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | struct BMVVideoDecoder { | |
104 | info: NACodecInfoRef, | |
105 | pal: [u8; 768], | |
106 | frame: [u8; FRAME_W * FRAME_H], | |
107 | } | |
108 | ||
109 | impl BMVVideoDecoder { | |
110 | fn new() -> Self { | |
111 | Self { | |
112 | info: NACodecInfoRef::default(), pal: [0; 768], frame: [0; FRAME_W * FRAME_H], | |
113 | } | |
114 | } | |
115 | fn decode_frame(&mut self, src: &[u8], bufinfo: &mut NABufferType, line: i16) -> DecoderResult<()> { | |
116 | let bufo = bufinfo.get_vbuf(); | |
117 | let mut buf = bufo.unwrap(); | |
118 | let paloff = buf.get_offset(1); | |
119 | let stride = buf.get_stride(0); | |
120 | let data = buf.get_data_mut().unwrap(); | |
121 | let dst = data.as_mut_slice(); | |
122 | ||
123 | let fwd = (line <= -640) || (line >= 0); | |
124 | ||
125 | let mut br = BMVReader::new(src, fwd); | |
126 | let mut bw = BMVWriter::new(&mut self.frame, fwd, line as isize); | |
127 | let mut mode = 0; | |
128 | while !bw.is_at_end() { | |
129 | let mut val = 0; | |
130 | let mut shift = 0; | |
131 | loop { | |
132 | let nib = br.get_nibble() as usize; | |
133 | if (nib & 0xC) != 0 { | |
134 | val |= nib << shift; | |
135 | break; | |
136 | } | |
137 | val |= nib << shift; | |
138 | shift += 2; | |
139 | } | |
140 | if (val & 1) != 0 { | |
141 | mode += 1; | |
142 | } | |
143 | mode += 1; | |
144 | if mode >= 4 { | |
145 | mode -= 3; | |
146 | } | |
147 | validate!(val >= 2); | |
148 | let len = (val >> 1) - 1; | |
149 | ||
150 | match mode { | |
151 | 1 => bw.copy(len), | |
152 | 2 => for _ in 0..len { bw.put_byte(br.get_byte()); }, | |
153 | 3 => bw.repeat(len), | |
154 | _ => unreachable!(), | |
155 | }; | |
156 | } | |
157 | ||
158 | for y in 0..FRAME_H { | |
159 | for x in 0..FRAME_W { | |
160 | dst[y * stride + x] = self.frame[y * FRAME_W + x]; | |
161 | } | |
162 | } | |
163 | ||
164 | let dpal = &mut dst[paloff..][..768]; | |
165 | dpal.copy_from_slice(&self.pal[0..]); | |
166 | ||
167 | Ok(()) | |
168 | } | |
169 | } | |
170 | ||
171 | impl NADecoder for BMVVideoDecoder { | |
172 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
173 | if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() { | |
174 | let fmt = NAPixelFormaton::new(ColorModel::RGB(RGBSubmodel::RGB), | |
175 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 0, 3)), | |
176 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 1, 3)), | |
177 | Some(NAPixelChromaton::new(0, 0, true, 8, 0, 2, 3)), | |
178 | None, None, | |
179 | FORMATON_FLAG_PALETTE, 3); | |
180 | let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(FRAME_W, FRAME_H, false, fmt)); | |
181 | self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); | |
182 | ||
183 | Ok(()) | |
184 | } else { | |
185 | Err(DecoderError::InvalidData) | |
186 | } | |
187 | } | |
188 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
189 | let src = pkt.get_buffer(); | |
190 | validate!(src.len() > 1); | |
191 | ||
192 | let mut mr = MemoryReader::new_read(&src); | |
193 | let mut br = ByteReader::new(&mut mr); | |
194 | let flags = br.read_byte()?; | |
195 | ||
196 | if (flags & BMV_COMMAND) != 0 { | |
197 | let size = if (flags & BMV_PRINT) != 0 { 8 } else { 10 }; | |
198 | br.read_skip(size)?; | |
199 | } | |
200 | if (flags & BMV_PAL) != 0 { | |
201 | br.read_buf(&mut self.pal)?; | |
202 | } | |
203 | let line; | |
204 | if (flags & BMV_SCROLL) != 0 { | |
205 | line = br.read_u16le()? as i16; | |
206 | } else if (flags & BMV_INTRA) == BMV_INTRA { | |
207 | line = -640; | |
208 | } else { | |
209 | line = 0; | |
210 | } | |
211 | let pos = br.tell() as usize; | |
212 | ||
213 | let bufret = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0); | |
214 | if let Err(_) = bufret { return Err(DecoderError::InvalidData); } | |
215 | let mut bufinfo = bufret.unwrap(); | |
216 | ||
217 | self.decode_frame(&src[pos..], &mut bufinfo, line)?; | |
218 | ||
219 | let is_intra = (flags & BMV_INTRA) == BMV_INTRA; | |
220 | let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo); | |
221 | frm.set_keyframe(is_intra); | |
222 | frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); | |
223 | Ok(frm.into_ref()) | |
224 | } | |
225 | } | |
226 | ||
227 | ||
228 | pub fn get_decoder_video() -> Box<NADecoder> { | |
229 | Box::new(BMVVideoDecoder::new()) | |
230 | } | |
231 | ||
232 | struct BMVAudioDecoder { | |
233 | ainfo: NAAudioInfo, | |
234 | chmap: NAChannelMap, | |
235 | } | |
236 | ||
237 | const BMV_AUD_SCALES: [i32; 16] = [ 16512, 8256, 4128, 2064, 1032, 516, 258, 192, 129, 88, 64, 56, 48, 40, 36, 32 ]; | |
238 | ||
239 | impl BMVAudioDecoder { | |
240 | fn new() -> Self { | |
241 | Self { | |
242 | ainfo: NAAudioInfo::new(0, 1, formats::SND_S16P_FORMAT, 0), | |
243 | chmap: NAChannelMap::new(), | |
244 | } | |
245 | } | |
246 | } | |
247 | ||
248 | fn scale_sample(samp: u8, scale: i32) -> i16 { | |
249 | let val = (((samp as i8) as i32) * scale) >> 5; | |
250 | if val < -32768 { | |
251 | -32768 | |
252 | } else if val > 32767 { | |
253 | 32767 | |
254 | } else { | |
255 | val as i16 | |
256 | } | |
257 | } | |
258 | ||
259 | impl NADecoder for BMVAudioDecoder { | |
260 | fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { | |
261 | if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() { | |
262 | self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), ainfo.get_channels(), formats::SND_S16P_FORMAT, 32); | |
263 | self.chmap = NAChannelMap::from_str("L,R").unwrap(); | |
264 | Ok(()) | |
265 | } else { | |
266 | Err(DecoderError::InvalidData) | |
267 | } | |
268 | } | |
269 | fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> { | |
270 | let info = pkt.get_stream().get_info(); | |
271 | if let NACodecTypeInfo::Audio(_) = info.get_properties() { | |
272 | let pktbuf = pkt.get_buffer(); | |
273 | validate!(pktbuf.len() > 1); | |
274 | let nblocks = pktbuf[0] as usize; | |
275 | validate!(pktbuf.len() == 1 + 65 * nblocks); | |
276 | let samples = nblocks * 32; | |
277 | let abuf = alloc_audio_buffer(self.ainfo, samples, self.chmap.clone())?; | |
278 | let mut adata = abuf.get_abuf_i16().unwrap(); | |
279 | let off1 = adata.get_offset(1); | |
280 | let dst = adata.get_data_mut().unwrap(); | |
281 | let psrc = &pktbuf[1..]; | |
282 | for (n, src) in psrc.chunks_exact(65).enumerate() { | |
283 | let code = src[0].rotate_right(1); | |
284 | let scale0 = BMV_AUD_SCALES[(code & 0xF) as usize]; | |
285 | let scale1 = BMV_AUD_SCALES[(code >> 4) as usize]; | |
286 | let aoff0 = n * 32; | |
287 | let aoff1 = aoff0 + off1; | |
288 | let data = &src[1..]; | |
289 | for (i, samp) in data.chunks_exact(2).enumerate() { | |
290 | dst[aoff0 + i] = scale_sample(samp[0], scale0); | |
291 | dst[aoff1 + i] = scale_sample(samp[1], scale1); | |
292 | } | |
293 | } | |
294 | let mut frm = NAFrame::new_from_pkt(pkt, info, abuf); | |
295 | frm.set_duration(Some(samples as u64)); | |
296 | frm.set_keyframe(false); | |
297 | Ok(frm.into_ref()) | |
298 | } else { | |
299 | Err(DecoderError::InvalidData) | |
300 | } | |
301 | } | |
302 | } | |
303 | ||
304 | pub fn get_decoder_audio() -> Box<NADecoder> { | |
305 | Box::new(BMVAudioDecoder::new()) | |
306 | } | |
307 | ||
308 | #[cfg(test)] | |
309 | mod test { | |
310 | use nihav_core::codecs::RegisteredDecoders; | |
311 | use nihav_core::demuxers::RegisteredDemuxers; | |
312 | use nihav_core::test::dec_video::*; | |
313 | use crate::codecs::game_register_all_codecs; | |
314 | use crate::demuxers::game_register_all_demuxers; | |
315 | #[test] | |
316 | fn test_bmv_video() { | |
317 | let mut dmx_reg = RegisteredDemuxers::new(); | |
318 | game_register_all_demuxers(&mut dmx_reg); | |
319 | let mut dec_reg = RegisteredDecoders::new(); | |
320 | game_register_all_codecs(&mut dec_reg); | |
321 | ||
322 | // let file = "assets/Game/PERFECT.BMV"; | |
323 | // let file = "assets/Game/DW2-MOUSE.BMV"; | |
324 | let file = "assets/Game/WILDCAT.BMV"; | |
325 | test_file_decoding("bmv", file, Some(40), true, false, None, &dmx_reg, &dec_reg); | |
326 | } | |
327 | #[test] | |
328 | fn test_bmv_audio() { | |
329 | let mut dmx_reg = RegisteredDemuxers::new(); | |
330 | game_register_all_demuxers(&mut dmx_reg); | |
331 | let mut dec_reg = RegisteredDecoders::new(); | |
332 | game_register_all_codecs(&mut dec_reg); | |
333 | ||
334 | let file = "assets/Game/PERFECT.BMV"; | |
335 | // let file = "assets/Game/DW2-MOUSE.BMV"; | |
336 | // let file = "assets/Game/WILDCAT.BMV"; | |
337 | test_decode_audio("bmv", file, None, "bmv", &dmx_reg, &dec_reg); | |
338 | } | |
339 | } |