quake3_qvm/
parser.rs

1//! Parsers for the different QVM and related formats.
2
3use super::{Instruction, QVM, VM_MAGIC};
4use opcodes::Opcode;
5use super::errors::*;
6use nom;
7use nom::{le_u32, le_u8};
8
9type Input = u8;
10type InputSlice<'a> = &'a [Input];
11
12/// Creates a named parser for an instruction that only consists of an opcode
13macro_rules! instruction {
14    ($name:ident, $opcode:path, $instruction:path) => {
15        named!($name<InputSlice,Instruction>,
16            value!($instruction, tag!([$opcode as Input]))
17        );
18    }
19}
20
21/// Creates a named parser for an instruction that has an operand
22macro_rules! instruction_with_operand {
23    ($name:ident, $opcode:path, $operand_fn:ident, $instruction:path) => {
24        named!($name<InputSlice,Instruction>,
25            do_parse!(
26                tag!([$opcode as Input]) >>
27                operand: $operand_fn      >>
28                ($instruction(operand))
29            )
30        );
31    }
32}
33
34/// Creates a named parser for an instruction with `u32` operand
35macro_rules! instruction_u32 {
36    ($name:ident, $opcode:path, $instruction:path) => {
37        instruction_with_operand!($name, $opcode, le_u32, $instruction);
38    }
39}
40
41/// Creates a named parser for an instruction with `u8` operand
42macro_rules! instruction_u8 {
43    ($name:ident, $opcode:path, $instruction:path) => {
44        instruction_with_operand!($name, $opcode, le_u8, $instruction);
45    }
46}
47
48
49instruction!(instruction_undef, Opcode::UNDEF, Instruction::UNDEF);
50
51instruction!(instruction_ignore, Opcode::IGNORE, Instruction::IGNORE);
52
53instruction!(instruction_break, Opcode::BREAK, Instruction::BREAK);
54
55instruction_u32!(instruction_enter, Opcode::ENTER, Instruction::ENTER);
56instruction_u32!(instruction_leave, Opcode::LEAVE, Instruction::LEAVE);
57instruction!(instruction_call, Opcode::CALL, Instruction::CALL);
58instruction!(instruction_push, Opcode::PUSH, Instruction::PUSH);
59instruction!(instruction_pop, Opcode::POP, Instruction::POP);
60
61instruction_u32!(instruction_const, Opcode::CONST, Instruction::CONST);
62instruction_u32!(instruction_local, Opcode::LOCAL, Instruction::LOCAL);
63
64instruction!(instruction_jump, Opcode::JUMP, Instruction::JUMP);
65
66instruction_u32!(instruction_eq, Opcode::EQ, Instruction::EQ);
67instruction_u32!(instruction_ne, Opcode::NE, Instruction::NE);
68
69instruction_u32!(instruction_lti, Opcode::LTI, Instruction::LTI);
70instruction_u32!(instruction_lei, Opcode::LEI, Instruction::LEI);
71instruction_u32!(instruction_gti, Opcode::GTI, Instruction::GTI);
72instruction_u32!(instruction_gei, Opcode::GEI, Instruction::GEI);
73
74instruction_u32!(instruction_ltu, Opcode::LTU, Instruction::LTU);
75instruction_u32!(instruction_leu, Opcode::LEU, Instruction::LEU);
76instruction_u32!(instruction_gtu, Opcode::GTU, Instruction::GTU);
77instruction_u32!(instruction_geu, Opcode::GEU, Instruction::GEU);
78
79instruction_u32!(instruction_eqf, Opcode::EQF, Instruction::EQF);
80instruction_u32!(instruction_nef, Opcode::NEF, Instruction::NEF);
81
82instruction_u32!(instruction_ltf, Opcode::LTF, Instruction::LTF);
83instruction_u32!(instruction_lef, Opcode::LEF, Instruction::LEF);
84instruction_u32!(instruction_gtf, Opcode::GTF, Instruction::GTF);
85instruction_u32!(instruction_gef, Opcode::GEF, Instruction::GEF);
86
87instruction!(instruction_load1, Opcode::LOAD1, Instruction::LOAD1);
88instruction!(instruction_load2, Opcode::LOAD2, Instruction::LOAD2);
89instruction!(instruction_load4, Opcode::LOAD4, Instruction::LOAD4);
90instruction!(instruction_store1, Opcode::STORE1, Instruction::STORE1);
91instruction!(instruction_store2, Opcode::STORE2, Instruction::STORE2);
92instruction!(instruction_store4, Opcode::STORE4, Instruction::STORE4);
93instruction_u8!(instruction_arg, Opcode::ARG, Instruction::ARG);
94
95instruction_u32!(instruction_block_copy,
96                 Opcode::BLOCK_COPY,
97                 Instruction::BLOCK_COPY);
98
99instruction!(instruction_sex8, Opcode::SEX8, Instruction::SEX8);
100instruction!(instruction_sex16, Opcode::SEX16, Instruction::SEX16);
101
102instruction!(instruction_negi, Opcode::NEGI, Instruction::NEGI);
103instruction!(instruction_add, Opcode::ADD, Instruction::ADD);
104instruction!(instruction_sub, Opcode::SUB, Instruction::SUB);
105instruction!(instruction_divi, Opcode::DIVI, Instruction::DIVI);
106instruction!(instruction_divu, Opcode::DIVU, Instruction::DIVU);
107instruction!(instruction_modi, Opcode::MODI, Instruction::MODI);
108instruction!(instruction_modu, Opcode::MODU, Instruction::MODU);
109instruction!(instruction_muli, Opcode::MULI, Instruction::MULI);
110instruction!(instruction_mulu, Opcode::MULU, Instruction::MULU);
111
112instruction!(instruction_band, Opcode::BAND, Instruction::BAND);
113instruction!(instruction_bor, Opcode::BOR, Instruction::BOR);
114instruction!(instruction_bxor, Opcode::BXOR, Instruction::BXOR);
115instruction!(instruction_bcom, Opcode::BCOM, Instruction::BCOM);
116
117instruction!(instruction_lsh, Opcode::LSH, Instruction::LSH);
118instruction!(instruction_rshi, Opcode::RSHI, Instruction::RSHI);
119instruction!(instruction_rshu, Opcode::RSHU, Instruction::RSHU);
120
121instruction!(instruction_negf, Opcode::NEGF, Instruction::NEGF);
122instruction!(instruction_addf, Opcode::ADDF, Instruction::ADDF);
123instruction!(instruction_subf, Opcode::SUBF, Instruction::SUBF);
124instruction!(instruction_divf, Opcode::DIVF, Instruction::DIVF);
125instruction!(instruction_mulf, Opcode::MULF, Instruction::MULF);
126
127instruction!(instruction_cvif, Opcode::CVIF, Instruction::CVIF);
128instruction!(instruction_cvfi, Opcode::CVFI, Instruction::CVFI);
129
130
131named!(ins<InputSlice,Instruction>,
132    alt!(instruction_undef
133        | instruction_ignore
134        | instruction_break
135        | instruction_enter | instruction_leave | instruction_call | instruction_push | instruction_pop
136        | instruction_const | instruction_local
137        | instruction_jump
138        | instruction_eq | instruction_ne
139        | instruction_lti | instruction_lei | instruction_gti | instruction_gei
140        | instruction_ltu | instruction_leu | instruction_gtu | instruction_geu
141        | instruction_eqf | instruction_nef
142        | instruction_ltf | instruction_lef | instruction_gtf | instruction_gef
143        | instruction_load1 | instruction_load2 | instruction_load4 | instruction_store1 | instruction_store2 | instruction_store4 | instruction_arg
144        | instruction_block_copy
145        | instruction_sex8 | instruction_sex16
146        | instruction_negi | instruction_add | instruction_sub | instruction_divi | instruction_divu | instruction_modi | instruction_modu | instruction_muli | instruction_mulu
147        | instruction_band | instruction_bor | instruction_bxor | instruction_bcom
148        | instruction_lsh  | instruction_rshi | instruction_rshu
149        | instruction_negf | instruction_addf | instruction_subf | instruction_divf | instruction_mulf
150        | instruction_cvif | instruction_cvfi
151    )
152);
153
154macro_rules! length_size(
155    ($i:expr, $s:expr, $submac:ident!( $($args:tt)* )) => (
156    {
157        match take!($i, $s as usize) {
158            nom::IResult::Error(e)                         => nom::IResult::Error(e),
159            nom::IResult::Incomplete(nom::Needed::Unknown) => nom::IResult::Incomplete(nom::Needed::Unknown),
160            nom::IResult::Incomplete(nom::Needed::Size(n)) => {
161                nom::IResult::Incomplete(nom::Needed::Size(
162                    n + nom::InputLength::input_len(&($i)) - ($s)
163                ))
164            },
165            nom::IResult::Done(i2, o2)  => {
166                match complete!(o2, $submac!($($args)*)) {
167                    nom::IResult::Error(e)      => nom::IResult::Error(e),
168                    nom::IResult::Incomplete(i) => nom::IResult::Incomplete(i),
169                    nom::IResult::Done(_, o3)   => nom::IResult::Done(i2, o3)
170                }
171            }
172        }
173    }
174    );
175
176  ($i:expr, $submac:ident!( $($args:tt)* ), $g:expr) => (
177    length_value!($i, $submac!($($args)*), call!($g));
178  );
179
180  ($i:expr, $f:expr, $submac:ident!( $($args:tt)* )) => (
181    length_value!($i, call!($f), $submac!($($args)*));
182  );
183
184  ($i:expr, $f:expr, $g:expr) => (
185    length_value!($i, call!($f), call!($g));
186  );
187);
188
189const HEADER_LENGTH_V1: u32 = 32;
190
191named!(qvm<InputSlice, QVM>,
192    do_parse!(
193        tag!(VM_MAGIC)                                  >>
194//        magic: le_u32                                   >>
195        instruction_count: le_u32                       >>
196        code_offset: le_u32                             >>
197        code_length: le_u32                             >>
198        data_offset: le_u32                             >>
199        data_length: le_u32                             >>
200        lit_length: le_u32                              >>
201        bss_length: le_u32                              >>
202        // Read padding between header and code segment
203        take!(code_offset - HEADER_LENGTH_V1)           >>
204        code: length_size!(
205            code_length as usize,
206            count!(ins, instruction_count as usize)
207        )                                               >>
208        // Read padding between code and data segment
209        take!(data_offset - code_offset - code_length)  >>
210        data: length_size!(
211            data_length as usize,
212            count!(le_u32, data_length as usize / 4)
213        )                                               >>
214        // lit segment is always aligned, no padding here
215        lit: length_size!(
216            lit_length as usize,
217            count!(le_u8, lit_length as usize)
218        )                                               >>
219        eof!()                                          >>
220        (
221            QVM {
222                code: code,
223                data: data,
224                lit: lit,
225                bss_length: bss_length,
226            }
227        )
228    )
229);
230
231
232/// Tries to parse a QVM from a byte slice.
233pub fn parse_qvm(data: InputSlice) -> Result<QVM> {
234    match qvm(data).to_full_result() {
235        Ok(v) => Ok(v),
236        Err(e) => Err(ErrorKind::Parser(e).into()),
237    }
238}
239
240
241#[cfg(test)]
242mod tests {
243    use super::{instruction_break, instruction_enter, instruction_arg, ins, qvm, parse_qvm,
244                InputSlice};
245    use bytecode::Instruction;
246    use nom::IResult;
247    use nom;
248    use QVM;
249
250    /// q3asm reserves the stack in the BSS segment
251    const Q3ASM_STACK_SIZE: usize = 0x10000;
252
253    #[test]
254    fn test_instruction_break_exact_match() {
255        let data = [0x2];
256        let result = instruction_break(&data);
257        assert_eq!(result, IResult::Done(&b""[..], Instruction::BREAK));
258    }
259
260    #[test]
261    fn test_instruction_break_tag_mismatch() {
262        let data = [0x0];
263        let result = instruction_break(&data);
264        assert_eq!(result, IResult::Error(nom::ErrorKind::Tag));
265    }
266
267    #[test]
268    fn test_instruction_enter_exact_match() {
269        let data = [0x3, 0x42, 0x0, 0x0, 0x0];
270        let result = instruction_enter(&data);
271        assert_eq!(result, IResult::Done(&b""[..], Instruction::ENTER(0x42)));
272    }
273
274    #[test]
275    fn test_instruction_arg_exact_match() {
276        let data = [0x21, 0x42];
277        let result = instruction_arg(&data);
278        assert_eq!(result, IResult::Done(&b""[..], Instruction::ARG(0x42)));
279    }
280
281    #[test]
282    fn test_ins_enter_exact_match() {
283        let data = [0x3, 0x42, 0x0, 0x0, 0x0];
284        let result = ins(&data);
285        assert_eq!(result, IResult::Done(&b""[..], Instruction::ENTER(0x42)));
286    }
287
288    #[test]
289    fn test_qvm_file_minimal() {
290        let data = include_bytes!("../assets/mod-minimal.qvm");
291        let result = qvm(data);
292        let expected = QVM {
293            code: vec![Instruction::ENTER(8),
294                       Instruction::CONST(4294967295), // TODO: This is actually -1, need to rethink types!
295                       Instruction::LEAVE(8),
296                       Instruction::PUSH,
297                       Instruction::LEAVE(8)],
298            data: vec![0],
299            lit: vec![],
300            bss_length: Q3ASM_STACK_SIZE as u32,
301        };
302        assert_eq!(result, IResult::Done(&b""[..], expected));
303    }
304
305    #[test]
306    fn test_qvm_file_bss() {
307        let data = include_bytes!("../assets/mod-bss.qvm");
308        let result = qvm(data);
309        let expected = QVM {
310            code: vec![Instruction::ENTER(8),
311                       Instruction::CONST(4294967295), // TODO: This is actually -1, need to rethink types!
312                       Instruction::LEAVE(8),
313                       Instruction::PUSH,
314                       Instruction::LEAVE(8)],
315            data: vec![0],
316            lit: vec![],
317            bss_length: Q3ASM_STACK_SIZE as u32 + 4,
318        };
319        assert_eq!(result, IResult::Done(&b""[..], expected));
320    }
321
322    #[test]
323    fn test_qvm_file_data() {
324        let data = include_bytes!("../assets/mod-data.qvm");
325        let result = qvm(data);
326        let expected = QVM {
327            code: vec![Instruction::ENTER(8),
328                       Instruction::CONST(4294967295), // TODO: This is actually -1, need to rethink types!
329                       Instruction::LEAVE(8),
330                       Instruction::PUSH,
331                       Instruction::LEAVE(8)],
332            data: vec![
333                0, // for alignment?
334                0xDEADBEEF,
335            ],
336            lit: vec![],
337            bss_length: Q3ASM_STACK_SIZE as u32,
338        };
339        assert_eq!(result, IResult::Done(&b""[..], expected));
340    }
341
342    #[test]
343    fn test_qvm_file_lit() {
344        let data = include_bytes!("../assets/mod-lit.qvm");
345        let result = qvm(data);
346        let expected = QVM {
347            code: vec![Instruction::ENTER(8),
348                       Instruction::CONST(4294967295), // TODO: This is actually -1, need to rethink types!
349                       Instruction::LEAVE(8),
350                       Instruction::PUSH,
351                       Instruction::LEAVE(8)],
352            data: vec![0],
353            lit: vec![
354                '!' as u8,
355                0, // padding for aligment?
356                0,
357                0,
358            ],
359            bss_length: Q3ASM_STACK_SIZE as u32,
360        };
361        assert_eq!(result, IResult::Done(&b""[..], expected));
362    }
363
364
365    #[test]
366    fn test_ins_file() {
367        let data = include_bytes!("../assets/mod-minimal.qvm");
368        named!(ins5<InputSlice,Vec<Instruction>>, count!(ins, 5));
369        let result = ins5(&data[32..53]);
370        let expected = vec![Instruction::ENTER(8),
371                            Instruction::CONST(4294967295), // TODO: This is actually -1, need to rethink types!
372                            Instruction::LEAVE(8),
373                            Instruction::PUSH,
374                            Instruction::LEAVE(8)];
375        assert_eq!(result, IResult::Done(&b""[..], expected));
376    }
377
378    // TODO: This is more of an integration test
379    #[test]
380    // TODO: This test won't work due to v2 magic, which is unimplemented
381    #[ignore]
382    fn test_parse_qvm_ioq3_qagame() {
383        let data = include_bytes!("../assets/ioq3/baseq3/vm/qagame.qvm");
384        let result = parse_qvm(data).unwrap();
385        // TODO: What to assert here?
386    }
387
388}