1use crate::host::{Gamepad, KEY_STATE_BYTES, Pointer, RawInput};
19use crate::keymap::KEY_CODES;
20
21const PAD_NAMES: [&str; 12] = ["A", "B", "X", "Y", "L", "R", "SELECT", "START", "UP", "DOWN", "LEFT", "RIGHT"];
22const MOUSE_NAMES: [&str; 5] = ["L", "R", "M", "BACK", "FWD"];
23
24#[derive(Default, Clone)]
25pub struct Script {
26 pads: Vec<(u64, u64, u32)>,
27 text: Vec<(u64, String)>,
28 keys: Vec<(u64, u64, Vec<u16>)>,
29 ptr: Vec<(u64, u64, f64, f64, u32)>,
30 moves: Vec<(u64, u64, f64, f64)>,
31 wheel: Vec<(u64, u64, f64, f64)>,
32 gp: Vec<(u64, u64, usize, Vec<(usize, f64)>, Vec<(usize, f64)>)>,
34}
35
36#[derive(Default)]
38pub struct ScriptState {
39 keys: [u8; KEY_STATE_BYTES],
40 x: f64,
41 y: f64,
42 buttons: u32,
43}
44
45fn split_items(spec: &str) -> Vec<String> {
46 let (mut items, mut cur, mut quoted, mut escaped, mut depth) = (Vec::new(), String::new(), false, false, 0);
47 for ch in spec.chars() {
48 if escaped {
49 escaped = false;
50 } else if ch == '\\' && quoted {
51 escaped = true;
52 } else if ch == '"' {
53 quoted = !quoted;
54 } else if !quoted && ch == '(' {
55 depth += 1;
56 } else if !quoted && ch == ')' {
57 depth -= 1;
58 } else if ch == ',' && !quoted && depth == 0 {
59 items.push(std::mem::take(&mut cur));
60 continue;
61 }
62 cur.push(ch);
63 }
64 items.push(cur);
65 items
66}
67
68fn args<'a>(rest: &'a str, name: &str) -> Option<&'a str> {
69 rest.strip_prefix(name)?.strip_prefix('(')?.strip_suffix(')')
70}
71
72impl Script {
73 pub fn parse(spec: &str) -> Result<Script, String> {
74 let mut s = Script::default();
75 for item in split_items(spec) {
76 let bad = || format!("bad --input item {item:?}");
77 let (range, rest) = item.split_once(':').ok_or_else(bad)?;
78 let (from, to) = range.split_once('-').unwrap_or((range, range));
79 let (from, to): (u64, u64) = (from.parse().map_err(|_| bad())?, to.parse().map_err(|_| bad())?);
80 let num = |v: &str| v.trim().parse::<f64>().map_err(|_| bad());
81 let pair = |a: &str| -> Result<(f64, f64), String> {
82 let (x, y) = a.split_once(',').ok_or_else(bad)?;
83 Ok((num(x)?, num(y)?))
84 };
85 if let Some(q) = rest.strip_prefix('"') {
86 let body = q.strip_suffix('"').ok_or_else(bad)?;
87 let mut t = String::new();
88 let mut chars = body.chars();
89 while let Some(c) = chars.next() {
90 t.push(if c != '\\' {
91 c
92 } else {
93 match chars.next().ok_or_else(bad)? {
94 'n' => '\n',
95 'b' => '\u{8}',
96 other => other,
97 }
98 });
99 }
100 s.text.push((from, t));
101 } else if let Some(a) = args(rest, "KEY") {
102 let mut codes = Vec::new();
103 for k in a.split('+') {
104 let c = KEY_CODES.iter().position(|n| !n.is_empty() && *n == k).ok_or_else(|| format!("{}: unknown key {k:?}", bad()))?;
105 codes.push(c as u16);
106 }
107 s.keys.push((from, to, codes));
108 } else if let Some(a) = args(rest, "PTR") {
109 let parts: Vec<&str> = a.splitn(3, ',').collect();
110 if parts.len() < 2 {
111 return Err(bad());
112 }
113 let mut buttons = 0;
114 if let Some(b) = parts.get(2) {
115 for n in b.split('+') {
116 buttons |= 1 << MOUSE_NAMES.iter().position(|m| m.eq_ignore_ascii_case(n.trim())).ok_or_else(bad)?;
117 }
118 }
119 s.ptr.push((from, to, num(parts[0])?, num(parts[1])?, buttons));
120 } else if let Some(a) = args(rest, "MOVE") {
121 let (x, y) = pair(a)?;
122 s.moves.push((from, to, x, y));
123 } else if let Some(a) = args(rest, "WHEEL") {
124 let (x, y) = pair(a)?;
125 s.wheel.push((from, to, x, y));
126 } else if rest.starts_with("GP") && rest.len() > 3 {
127 let slot: usize = rest[2..3].parse().map_err(|_| bad())?;
128 let a = args(&rest[3..], "").ok_or_else(bad)?;
129 if slot > 3 {
130 return Err(bad());
131 }
132 let (mut buttons, mut axes) = (Vec::new(), Vec::new());
133 for part in a.split('+').filter(|p| !p.is_empty()) {
134 if let Some(i) = part.strip_prefix('B') {
135 buttons.push((i.parse().map_err(|_| bad())?, 1.0));
136 } else if let Some(ax) = part.strip_prefix('A') {
137 let (i, v) = ax.split_once('=').ok_or_else(bad)?;
138 axes.push((i.parse().map_err(|_| bad())?, num(v)?));
139 } else {
140 return Err(bad());
141 }
142 }
143 s.gp.push((from, to, slot, buttons, axes));
144 } else {
145 let mut mask = 0;
146 for b in rest.split('+') {
147 mask |= 1 << PAD_NAMES.iter().position(|n| n.eq_ignore_ascii_case(b)).ok_or_else(bad)?;
148 }
149 s.pads.push((from, to, mask));
150 }
151 }
152 Ok(s)
153 }
154
155 pub fn pad(&self, frame: u64) -> u32 {
156 self.pads.iter().filter(|(f, t, _)| (*f..=*t).contains(&frame)).fold(0, |m, (_, _, b)| m | b)
157 }
158
159 pub fn text(&self, frame: u64) -> String {
160 self.text.iter().filter(|(f, _)| *f == frame).map(|(_, t)| t.as_str()).collect()
161 }
162
163 pub fn raw(&self, frame: u64, st: &mut ScriptState, drawable: (f32, f32), mode: u32) -> RawInput {
165 let on = |f: u64, t: u64| (f..=t).contains(&frame);
166 let mut keys = [0u8; KEY_STATE_BYTES];
167 for (f, t, codes) in &self.keys {
168 if on(*f, *t) {
169 for &c in codes {
170 keys[c as usize / 8] |= 1 << (c % 8);
171 }
172 }
173 }
174 let mut key_events = Vec::new();
176 for down in [false, true] {
177 for c in 1..KEY_CODES.len() as u16 {
178 let (was, is) = (st.keys[c as usize / 8] >> (c % 8) & 1 == 1, keys[c as usize / 8] >> (c % 8) & 1 == 1);
179 if was != is && is == down {
180 key_events.push((c, down));
181 }
182 }
183 }
184 st.keys = keys;
185
186 let (px, py) = (st.x, st.y);
187 let mut buttons = 0;
188 for (f, t, x, y, b) in &self.ptr {
189 if on(*f, *t) {
190 st.x = *x;
191 st.y = *y;
192 buttons = *b;
193 }
194 }
195 let (mut dx, mut dy) = (st.x - px, st.y - py);
196 for (f, t, x, y) in &self.moves {
197 if on(*f, *t) {
198 dx += x;
199 dy += y;
200 }
201 }
202 let (mut wx, mut wy) = (0.0, 0.0);
203 for (f, t, x, y) in &self.wheel {
204 if on(*f, *t) {
205 wx += x;
206 wy += y;
207 }
208 }
209 let inside = st.x >= 0.0 && st.y >= 0.0 && st.x < drawable.0 as f64 && st.y < drawable.1 as f64;
210 let pointer = Pointer {
211 x: st.x as f32,
212 y: st.y as f32,
213 dx: dx as f32,
214 dy: dy as f32,
215 wheel_x: wx as f32,
216 wheel_y: wy as f32,
217 buttons,
218 pressed: buttons & !st.buttons,
219 released: st.buttons & !buttons,
220 flags: (inside as u32) | (mode & 6),
221 drawable,
222 };
223 st.buttons = buttons;
224
225 let mut gamepads: [Gamepad; 4] = Default::default();
226 for (slot, g) in gamepads.iter_mut().enumerate() {
227 if self.gp.iter().any(|e| e.2 == slot) {
228 *g = Gamepad { connected: true, standard: true, buttons: vec![0.0; 17], axes: vec![0.0; 4], name: "scripted".into() };
229 }
230 }
231 for (f, t, slot, bs, axs) in &self.gp {
232 if on(*f, *t) {
233 let g = &mut gamepads[*slot];
234 for &(i, v) in bs {
235 if i < 32 {
236 g.buttons.resize(g.buttons.len().max(i + 1), 0.0);
237 g.buttons[i] = v as f32;
238 }
239 }
240 for &(i, v) in axs {
241 if i < 16 {
242 g.axes.resize(g.axes.len().max(i + 1), 0.0);
243 g.axes[i] = v as f32;
244 }
245 }
246 }
247 }
248 RawInput { keys: Some(keys), key_events, pointer: Some(pointer), gamepads: Some(gamepads) }
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 #[test]
257 fn parses_all_actions_and_derives_events() {
258 let s = Script::parse(r#"0-1:A+UP,2:"x,y",3-4:KEY(ShiftLeft+ArrowLeft),5:PTR(10,20,L),6:PTR(15,20),6:WHEEL(0,1),7-8:GP1(B0+A1=0.5),9:MOVE(3,-4)"#).unwrap();
259 assert_eq!(s.pad(1), 1 | 1 << 8);
260 assert_eq!(s.text(2), "x,y");
261 let mut st = ScriptState::default();
262 let d = (1280.0, 720.0);
263 let mut frames: Vec<RawInput> = (0..10).map(|i| s.raw(i, &mut st, d, 0)).collect();
264 let shift = KEY_CODES.iter().position(|n| *n == "ShiftLeft").unwrap() as u16;
265 assert!(frames[3].key_events.contains(&(shift, true)));
266 assert!(frames[5].key_events.contains(&(shift, false)));
267 let p5 = frames[5].pointer.unwrap();
268 assert_eq!((p5.x, p5.y, p5.buttons, p5.pressed), (10.0, 20.0, 1, 1));
269 let p6 = frames[6].pointer.unwrap();
270 assert_eq!((p6.dx, p6.released, p6.wheel_y), (5.0, 1, 1.0));
271 let g = frames.remove(7).gamepads.unwrap();
272 assert!(g[1].connected && !g[0].connected);
273 assert_eq!((g[1].buttons[0], g[1].axes[1]), (1.0, 0.5));
274 assert_eq!(frames[8].pointer.unwrap().dx, 3.0); assert!(Script::parse("1:KEY(NoSuchKey)").is_err());
276 }
277}