detection module start
[nihav.git] / src / detect.rs
CommitLineData
d8ce0de0
KS
1use std::io::SeekFrom;
2use io::byteio::ByteReader;
3
4#[derive(Debug,Clone,Copy,PartialEq)]
5pub enum DetectionScore {
6 No,
7 ExtensionMatches,
8 MagicMatches,
9}
10
11impl DetectionScore {
12 pub fn less(&self, other: DetectionScore) -> bool {
13 (*self as i32) < (other as i32)
14 }
15}
16
17#[allow(dead_code)]
18enum CondArg {
19 Byte(u8),
20 U16BE(u16),
21 U16LE(u16),
22 U24BE(u32),
23 U24LE(u32),
24 U32BE(u32),
25 U32LE(u32),
26 U64BE(u64),
27 U64LE(u64),
28}
29
30impl CondArg {
31 fn val(&self) -> u64 {
32 match *self {
33 CondArg::Byte(b) => { b as u64 }
34 CondArg::U16BE(v) => { v as u64 }
35 CondArg::U16LE(v) => { v as u64 }
36 CondArg::U24BE(v) => { v as u64 }
37 CondArg::U24LE(v) => { v as u64 }
38 CondArg::U32BE(v) => { v as u64 }
39 CondArg::U32LE(v) => { v as u64 }
40 CondArg::U64BE(v) => { v }
41 CondArg::U64LE(v) => { v }
42 }
43 }
44 fn read_val(&self, src: &mut ByteReader) -> Option<u64> {
45 match *self {
46 CondArg::Byte(_) => {
47 let res = src.peek_byte();
48 if let Err(_) = res { return None; }
49 Some(res.unwrap() as u64)
50 }
51 CondArg::U16BE(_) => {
52 let res = src.peek_u16be();
53 if let Err(_) = res { return None; }
54 Some(res.unwrap() as u64)
55 }
56 CondArg::U16LE(_) => {
57 let res = src.peek_u16le();
58 if let Err(_) = res { return None; }
59 Some(res.unwrap() as u64)
60 }
61 CondArg::U24BE(_) => {
62 let res = src.peek_u24be();
63 if let Err(_) = res { return None; }
64 Some(res.unwrap() as u64)
65 }
66 CondArg::U24LE(_) => {
67 let res = src.peek_u24le();
68 if let Err(_) = res { return None; }
69 Some(res.unwrap() as u64)
70 }
71 CondArg::U32BE(_) => {
72 let res = src.peek_u32be();
73 if let Err(_) = res { return None; }
74 Some(res.unwrap() as u64)
75 }
76 CondArg::U32LE(_) => {
77 let res = src.peek_u32le();
78 if let Err(_) = res { return None; }
79 Some(res.unwrap() as u64)
80 }
81 CondArg::U64BE(_) => {
82 let res = src.peek_u64be();
83 if let Err(_) = res { return None; }
84 Some(res.unwrap())
85 }
86 CondArg::U64LE(_) => {
87 let res = src.peek_u64le();
88 if let Err(_) = res { return None; }
89 Some(res.unwrap())
90 }
91 }
92 }
93 fn eq(&self, src: &mut ByteReader) -> bool {
94 let val = self.read_val(src);
95 if let None = val { false }
96 else { val.unwrap() == self.val() }
97 }
98 fn ge(&self, src: &mut ByteReader) -> bool {
99 let val = self.read_val(src);
100 if let None = val { false }
101 else { val.unwrap() >= self.val() }
102 }
103 fn gt(&self, src: &mut ByteReader) -> bool {
104 let val = self.read_val(src);
105 if let None = val { false }
106 else { val.unwrap() > self.val() }
107 }
108 fn le(&self, src: &mut ByteReader) -> bool {
109 let val = self.read_val(src);
110 if let None = val { false }
111 else { val.unwrap() <= self.val() }
112 }
113 fn lt(&self, src: &mut ByteReader) -> bool {
114 let val = self.read_val(src);
115 if let None = val { false }
116 else { val.unwrap() < self.val() }
117 }
118}
119
120#[allow(dead_code)]
121enum Cond<'a> {
122 And(&'a Cond<'a>, &'a Cond<'a>),
123 Or(&'a Cond<'a>, &'a Cond<'a>),
124 Equals(CondArg),
125 EqualsString(&'static [u8]),
126 InRange(CondArg, CondArg),
127 LessThan(CondArg),
128 GreaterThan(CondArg),
129}
130
131impl<'a> Cond<'a> {
132 fn eval(&self, src: &mut ByteReader) -> bool {
133 match *self {
134 Cond::And(ref a, ref b) => { a.eval(src) && b.eval(src) },
135 Cond::Or (ref a, ref b) => { a.eval(src) || b.eval(src) },
136 Cond::Equals(ref arg) => { arg.eq(src) },
137 Cond::InRange(ref a, ref b) => { a.le(src) && b.ge(src) },
138 Cond::LessThan(ref arg) => { arg.lt(src) },
139 Cond::GreaterThan(ref arg) => { arg.gt(src) },
140 Cond::EqualsString(str) => {
141 let mut val: Vec<u8> = Vec::with_capacity(str.len());
142 val.resize(str.len(), 0);
143 let res = src.peek_buf(val.as_mut_slice());
144 if let Err(_) = res { return false; }
145 val == str
146 }
147 }
148 }
149}
150
151struct CheckItem<'a> {
152 offs: u32,
153 cond: &'a Cond<'a>,
154}
155
156#[allow(dead_code)]
157struct DetectConditions<'a> {
158 demux_name: &'static str,
159 extensions: &'static str,
160 conditions: &'a [CheckItem<'a>],
161}
162
163const DETECTORS: &[DetectConditions] = &[
164 DetectConditions {
165 demux_name: "avi",
166 extensions: ".avi",
167 conditions: &[CheckItem{offs:0, cond: &Cond::Or(
168 &Cond::EqualsString(b"RIFX"),
169 &Cond::EqualsString(b"RIFF")) },
170 CheckItem{offs:8, cond: &Cond::EqualsString(b"AVI LIST") }
171 ],
172 },
173 DetectConditions {
174 demux_name: "gdv",
175 extensions: ".gdv",
176 conditions: &[CheckItem{offs:0, cond: &Cond::Equals(CondArg::U32LE(0x29111994))}],
177 },
178];
179
180pub fn detect_format(name: &str, src: &mut ByteReader) -> Option<(&'static str, DetectionScore)> {
181 let mut result = None;
182 let lname = name.to_lowercase();
183 for detector in DETECTORS {
184 let mut score = DetectionScore::No;
185 if name.len() > 0 {
186 for ext in detector.extensions.split(',') {
187 if lname.ends_with(ext) {
188 score = DetectionScore::ExtensionMatches;
189 break;
190 }
191 }
192 }
193 let mut passed = detector.conditions.len() > 0;
194 for ck in detector.conditions {
195 let ret = src.seek(SeekFrom::Start(ck.offs as u64));
196 if let Err(_) = ret {
197 passed = false;
198 break;
199 }
200 if !ck.cond.eval(src) {
201 passed = false;
202 break;
203 }
204 }
205 if passed {
206 score = DetectionScore::MagicMatches;
207 }
208 if score == DetectionScore::MagicMatches {
209 return Some((detector.demux_name, score));
210 }
211 if let None = result {
212 result = Some((detector.demux_name, score));
213 } else {
214 let (_, oldsc) = result.unwrap();
215 if oldsc.less(score) {
216 result = Some((detector.demux_name, score));
217 }
218 }
219 }
220 result
221}
222
223#[cfg(test)]
224mod test {
225 use super::*;
226 use std::fs::File;
227 use io::byteio::*;
228
229 #[test]
230 fn test_avi_detect() {
231 let name = "assets/laser05.avi";
232 let mut file = File::open(name).unwrap();
233 let mut fr = FileReader::new_read(&mut file);
234 let mut br = ByteReader::new(&mut fr);
235 let (name, score) = detect_format(name, &mut br).unwrap();
236 assert_eq!(name, "avi");
237 assert_eq!(score, DetectionScore::MagicMatches);
238 }
239
240 #[test]
241 fn test_gdv_detect() {
242 let name = "assets/intro1.gdv";
243 let mut file = File::open(name).unwrap();
244 let mut fr = FileReader::new_read(&mut file);
245 let mut br = ByteReader::new(&mut fr);
246 let (name, score) = detect_format(name, &mut br).unwrap();
247 assert_eq!(name, "gdv");
248 assert_eq!(score, DetectionScore::MagicMatches);
249 }
250}