]> git.nihav.org Git - nihav.git/blame - nihav-game/src/codecs/seq.rs
avi: fix handling of multiple palette changes in single 'pc' chunk
[nihav.git] / nihav-game / src / codecs / seq.rs
CommitLineData
f9fc73be
KS
1use nihav_core::codecs::*;
2use nihav_core::io::byteio::*;
3
4const WIDTH: usize = 320;
5const HEIGHT: usize = 200;
6const FRAME_HEADER: usize = 24;
7
8struct SequenceDecoder {
9 info: NACodecInfoRef,
10 pal: [u8; 768],
11 frame: [u8; WIDTH * HEIGHT],
12}
13
14impl SequenceDecoder {
15 fn new() -> Self {
16 Self {
17 info: NACodecInfoRef::default(),
18 pal: [0; 768],
19 frame: [0; WIDTH * HEIGHT],
20 }
21 }
22}
23
24impl NADecoder for SequenceDecoder {
25 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
26 if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() {
27 if let Some(ref edata) = info.get_extradata() {
28 validate!(edata.len() > 32);
29 let pal_start = read_u16le(&edata[25..])? as usize;
30 let pal_len = read_u16le(&edata[29..])? as usize;
31 validate!(pal_len > 0 && pal_start + pal_len <= 256);
32 match edata[32] {
33 0 => {
34 let dpal = self.pal[pal_start * 3..].chunks_exact_mut(3);
35 for (dst, quad) in dpal.zip(edata[37..].chunks_exact(4)) {
36 dst.copy_from_slice(&quad[1..]);
37 }
38 },
39 1 => self.pal[pal_start * 3..][..pal_len * 3].copy_from_slice(&edata[37..][..pal_len * 3]),
40 _ => return Err(DecoderError::NotImplemented),
41 }
42 } else {
43 return Err(DecoderError::InvalidData);
44 }
45 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(WIDTH, HEIGHT, false, PAL8_FORMAT));
46 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
47
48 Ok(())
49 } else {
50 Err(DecoderError::InvalidData)
51 }
52 }
53 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
54 let src = pkt.get_buffer();
55 validate!(src.len() > FRAME_HEADER);
56
57 let mut mr = MemoryReader::new_read(&src);
58 let mut br = ByteReader::new(&mut mr);
59
60 let width = br.read_u16le()? as usize;
61 let height = br.read_u16le()? as usize;
62 let xoff = br.read_u16le()? as usize;
63 let yoff = br.read_u16le()? as usize;
64 validate!(width + xoff <= WIDTH && height + yoff <= HEIGHT);
65
66 let _ckey = br.read_byte()?;
67 let frm_type = br.read_byte()?;
68 br.read_skip(6)?;
69 let opcode_size = br.read_u16le()? as usize;
70 validate!(opcode_size <= src.len() - FRAME_HEADER);
71
72 let opcodes = &src[FRAME_HEADER..][..opcode_size];
73 let clr_data = &src[FRAME_HEADER + opcode_size..];
74 match frm_type {
75 0 => {
76 validate!(opcodes.is_empty());
77 validate!(clr_data.len() == width * height);
78
79 let dst = &mut self.frame[xoff + yoff * WIDTH..];
80 for (dline, sline) in dst.chunks_mut(WIDTH).zip(clr_data.chunks(width)) {
81 dline[..width].copy_from_slice(sline);
82 }
83 },
84 1 | 11 => {
85 validate!(!opcodes.is_empty());
86 let mut mr = MemoryReader::new_read(opcodes);
87 let mut ops = ByteReader::new(&mut mr);
88 let mut mr = MemoryReader::new_read(clr_data);
89 let mut clr = ByteReader::new(&mut mr);
90
91 let mut x = xoff;
92 let mut y = yoff;
93 while y < yoff + height {
94 let op = ops.read_byte()?;
95 let mut len = (op & 0x3F) as usize;
96 if len == 0 {
97 len = width + xoff - x;
98 }
99 match op >> 6 {
100 3 => x += len,
101 2 => {
102 clr.read_buf(&mut self.frame[x + y * WIDTH..][..len])?;
103 x += len;
104 },
105 _ => {
106 let len = ((u16::from(op & 0x7) << 8) | u16::from(ops.read_byte()?)) as usize;
107 match op >> 3 {
108 2 => x += len,
109 3 => {
110 for _ in 0..len {
111 validate!(y < height + yoff);
112 self.frame[x + y * WIDTH] = clr.read_byte()?;
113 x += 1;
114 if x == width + xoff {
115 y += 1;
116 x = xoff;
117 }
118 }
119 },
120 6 => {
121 let len = if len != 0 { len } else { height + yoff - y };
122 for _ in 0..(len * width) {
123 validate!(y < height + yoff);
124 self.frame[x + y * WIDTH] = clr.read_byte()?;
125 x += 1;
126 if x == width + xoff {
127 y += 1;
128 x = xoff;
129 }
130 }
131 },
132 7 => {
133 if len > 0 {
134 y += len;
135 } else {
136 y = height + yoff;
137 }
138 },
139 _ => return Err(DecoderError::NotImplemented),
140 }
141 },
142 }
143 if x == width + xoff {
144 x = xoff;
145 y += 1;
146 }
147 }
148 },
149 _ => return Err(DecoderError::InvalidData),
150 }
151
152
153 let buf = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 0)?;
154 let mut vbuf = buf.get_vbuf().unwrap();
155 let paloff = vbuf.get_offset(1);
156 let stride = vbuf.get_stride(0);
157 let data = vbuf.get_data_mut().unwrap();
158
159 for (drow, srow) in data.chunks_mut(stride).zip(self.frame.chunks_exact(WIDTH)) {
160 drow[..WIDTH].copy_from_slice(srow);
161 }
162 data[paloff..][..768].copy_from_slice(&self.pal);
163
164 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buf);
165 let ftype = if pkt.keyframe { FrameType::I } else { FrameType::P };
166 frm.set_frame_type(ftype);
167 Ok(frm.into_ref())
168 }
169 fn flush(&mut self) {
170 }
171}
172
173impl NAOptionHandler for SequenceDecoder {
174 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
175 fn set_options(&mut self, _options: &[NAOption]) { }
176 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
177}
178
179pub fn get_decoder() -> Box<dyn NADecoder + Send> {
180 Box::new(SequenceDecoder::new())
181}
182
183#[cfg(test)]
184mod test {
185 use nihav_core::codecs::RegisteredDecoders;
186 use nihav_core::demuxers::RegisteredDemuxers;
187 use nihav_codec_support::test::dec_video::*;
188 use crate::*;
189 #[test]
190 fn test_seq1() {
191 let mut dmx_reg = RegisteredDemuxers::new();
192 game_register_all_demuxers(&mut dmx_reg);
193 let mut dec_reg = RegisteredDecoders::new();
194 game_register_all_decoders(&mut dec_reg);
195
196 // sample from King's Quest VI
197 test_decoding("sierra-seq", "seq-video", "assets/Game/sierra/FS1.SEQ",
198 Some(2), &dmx_reg, &dec_reg,
199 ExpectedTestResult::MD5Frames(vec![
200 [0x5a7472da, 0xa9e242fd, 0x867efa52, 0x9625f05c],
201 [0x720ab982, 0x704970a0, 0xf854af8b, 0x3b86bed9],
202 [0xaa1426e1, 0x79750652, 0x87b7a727, 0xc582a561]]));
203 }
204 #[test]
205 fn test_seq2() {
206 let mut dmx_reg = RegisteredDemuxers::new();
207 game_register_all_demuxers(&mut dmx_reg);
208 let mut dec_reg = RegisteredDecoders::new();
209 game_register_all_decoders(&mut dec_reg);
210
211 // sample from Gabriel Knight
212 test_decoding("sierra-seq", "seq-video", "assets/Game/sierra/blood.seq",
213 Some(2), &dmx_reg, &dec_reg,
214 ExpectedTestResult::MD5Frames(vec![
215 [0x989422ee, 0x5892beae, 0x0ca9db17, 0xe25ab710],
216 [0x0d5f395e, 0x2eeac229, 0x1504ece0, 0xa7d3401e],
217 [0x988d3fa6, 0x68be4639, 0x7ab7137c, 0x72e69e26]]));
218 }
219}