2020-05-08 23:17:41 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate bitflags;
|
|
|
|
|
2020-05-09 13:16:19 +00:00
|
|
|
pub(crate) mod au;
|
2020-05-08 23:17:41 +00:00
|
|
|
pub(crate) mod controller;
|
2020-05-09 00:47:36 +00:00
|
|
|
pub(crate) mod twitch;
|
2020-05-08 23:17:41 +00:00
|
|
|
|
2020-05-09 13:16:19 +00:00
|
|
|
use crate::au::Lerper;
|
|
|
|
|
2020-05-08 22:32:29 +00:00
|
|
|
use anyhow::{anyhow, Result};
|
|
|
|
use log::{debug, error, info, warn};
|
2020-05-08 21:54:57 +00:00
|
|
|
use std::{
|
|
|
|
fs::{File, OpenOptions},
|
|
|
|
io::{Read, Write},
|
|
|
|
str::from_utf8,
|
2020-05-08 23:17:41 +00:00
|
|
|
sync::{Arc, RwLock},
|
|
|
|
thread::spawn,
|
2020-05-08 21:54:57 +00:00
|
|
|
};
|
|
|
|
|
2020-05-08 23:17:41 +00:00
|
|
|
pub(crate) struct State {
|
2020-05-09 03:44:03 +00:00
|
|
|
frame: u64,
|
2020-05-09 13:16:19 +00:00
|
|
|
|
|
|
|
stickx: Lerper,
|
|
|
|
sticky: Lerper,
|
|
|
|
a_button: Lerper,
|
|
|
|
b_button: Lerper,
|
|
|
|
z_button: Lerper,
|
|
|
|
r_button: Lerper,
|
|
|
|
start: Lerper,
|
|
|
|
c_left: Lerper,
|
|
|
|
c_right: Lerper,
|
|
|
|
c_up: Lerper,
|
|
|
|
c_down: Lerper,
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) type MTState = Arc<RwLock<State>>;
|
|
|
|
|
2020-05-08 22:32:29 +00:00
|
|
|
fn main() -> Result<()> {
|
2020-05-08 21:54:57 +00:00
|
|
|
pretty_env_logger::try_init()?;
|
2020-05-09 00:47:36 +00:00
|
|
|
kankyo::init()?;
|
2020-05-08 21:54:57 +00:00
|
|
|
|
|
|
|
let mut vblank = File::open("vblank")?;
|
|
|
|
let mut input = OpenOptions::new().write(true).open("input")?;
|
|
|
|
|
2020-05-09 14:12:13 +00:00
|
|
|
const STICK_LERP_TIME: f64 = 270.0; // 270 frames to lerp stick positions down to 0
|
2020-05-09 13:16:19 +00:00
|
|
|
const BUTTON_LERP_TIME: f64 = 20.0; // 20 frames to lerp button inputs down to 0
|
|
|
|
|
2020-05-08 23:17:41 +00:00
|
|
|
let st = {
|
|
|
|
let st = State {
|
2020-05-09 03:44:03 +00:00
|
|
|
frame: 0,
|
2020-05-09 13:16:19 +00:00
|
|
|
|
|
|
|
stickx: Lerper::init(STICK_LERP_TIME, 127, -128, 0),
|
|
|
|
sticky: Lerper::init(STICK_LERP_TIME, 127, -128, 0),
|
|
|
|
a_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
|
|
|
b_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
2020-05-09 14:12:13 +00:00
|
|
|
z_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
2020-05-09 13:16:19 +00:00
|
|
|
r_button: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
|
|
|
start: Lerper::init(BUTTON_LERP_TIME / 4.0, 64, -1, 0), // z button is special
|
|
|
|
c_left: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
|
|
|
c_right: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
|
|
|
c_up: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
|
|
|
c_down: Lerper::init(BUTTON_LERP_TIME, 64, -1, 0),
|
2020-05-08 23:17:41 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
Arc::new(RwLock::new(st))
|
|
|
|
};
|
|
|
|
|
2020-05-08 22:00:20 +00:00
|
|
|
info!("ready");
|
2020-05-08 21:54:57 +00:00
|
|
|
|
2020-05-08 23:17:41 +00:00
|
|
|
{
|
|
|
|
let st = st.clone();
|
2020-05-09 00:47:36 +00:00
|
|
|
spawn(move || twitch::run(st));
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
|
|
|
|
2020-05-08 21:54:57 +00:00
|
|
|
loop {
|
|
|
|
let mut data = [0; 3];
|
2020-05-08 22:18:45 +00:00
|
|
|
debug!("waiting for vblank");
|
2020-05-08 21:54:57 +00:00
|
|
|
vblank.read(&mut data)?;
|
|
|
|
let str = from_utf8(&data)?;
|
|
|
|
debug!("got data: {}", str);
|
2020-05-08 22:32:29 +00:00
|
|
|
|
2020-05-09 13:16:19 +00:00
|
|
|
let mut controller = [0; 4];
|
|
|
|
|
2020-05-08 22:32:29 +00:00
|
|
|
match str {
|
2020-05-08 23:17:41 +00:00
|
|
|
"OK\n" => {
|
2020-05-09 13:16:19 +00:00
|
|
|
{
|
|
|
|
let mut data = st.write().unwrap();
|
|
|
|
data.frame += 1;
|
|
|
|
}
|
|
|
|
|
2020-05-08 23:23:56 +00:00
|
|
|
let mut data = st.write().unwrap();
|
2020-05-09 13:16:19 +00:00
|
|
|
let frame = data.frame + 1;
|
2020-05-09 03:44:03 +00:00
|
|
|
|
2020-05-09 13:16:19 +00:00
|
|
|
//data.stickx.update(data.controller[2] as i64);
|
|
|
|
//data.sticky.update(data.controller[3] as i64);
|
2020-05-09 14:12:13 +00:00
|
|
|
debug!("x before: {}", data.stickx.scalar);
|
2020-05-09 13:16:19 +00:00
|
|
|
let mut stickx_scalar = data.stickx.apply(frame) as i8;
|
2020-05-09 14:12:13 +00:00
|
|
|
debug!("x after: {}", data.stickx.scalar);
|
|
|
|
debug!("y before: {}", data.sticky.scalar);
|
2020-05-09 13:16:19 +00:00
|
|
|
let mut sticky_scalar = data.sticky.apply(frame) as i8;
|
2020-05-09 14:12:13 +00:00
|
|
|
debug!("y after: {}", data.sticky.scalar);
|
2020-05-09 03:44:03 +00:00
|
|
|
|
2020-05-09 13:16:19 +00:00
|
|
|
let dist = stick_distance(stickx_scalar, sticky_scalar);
|
2020-05-09 03:44:03 +00:00
|
|
|
if dist <= 10 {
|
2020-05-09 13:16:19 +00:00
|
|
|
stickx_scalar = 0;
|
|
|
|
sticky_scalar = 0;
|
2020-05-09 03:44:03 +00:00
|
|
|
}
|
|
|
|
|
2020-05-09 13:16:19 +00:00
|
|
|
use controller::{HiButtons, LoButtons};
|
|
|
|
|
|
|
|
let mut hi = HiButtons::NONE;
|
|
|
|
let mut lo = LoButtons::NONE;
|
2020-05-09 14:12:13 +00:00
|
|
|
const BUTTON_PUSH_THRESHOLD: i64 = 2;
|
2020-05-09 13:16:19 +00:00
|
|
|
|
|
|
|
// high buttons
|
|
|
|
data.a_button.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.a_button.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
hi = hi | HiButtons::A_BUTTON;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.b_button.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.b_button.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
hi = hi | HiButtons::B_BUTTON;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.z_button.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.z_button.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
hi = hi | HiButtons::Z_BUTTON;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.start.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.start.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
hi = hi | HiButtons::START;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.r_button.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.r_button.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
lo = lo | LoButtons::R_BUTTON;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.c_up.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.c_up.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
lo = lo | LoButtons::C_UP;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.c_down.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.c_down.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
lo = lo | LoButtons::C_DOWN;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.c_left.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.c_left.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
lo = lo | LoButtons::C_LEFT;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
data.c_right.apply(frame);
|
2020-05-09 14:12:13 +00:00
|
|
|
if data.c_right.pressed(BUTTON_PUSH_THRESHOLD) {
|
|
|
|
lo = lo | LoButtons::C_RIGHT;
|
|
|
|
}
|
2020-05-09 13:16:19 +00:00
|
|
|
|
2020-05-09 15:00:46 +00:00
|
|
|
debug!(
|
2020-05-09 14:12:13 +00:00
|
|
|
"[ rust] {:02x}{:02x} {:02x}{:02x}",
|
|
|
|
hi.bits(),
|
|
|
|
lo.bits(),
|
|
|
|
stickx_scalar as u8,
|
|
|
|
sticky_scalar as u8
|
|
|
|
);
|
2020-05-09 13:16:19 +00:00
|
|
|
controller[0] = hi.bits() as u8;
|
|
|
|
controller[1] = lo.bits() as u8;
|
|
|
|
controller[2] = stickx_scalar as u8;
|
|
|
|
controller[3] = sticky_scalar as u8;
|
|
|
|
|
|
|
|
input.write(&controller)?;
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
2020-05-08 22:32:29 +00:00
|
|
|
"BYE" => {
|
|
|
|
warn!("asked to exit by the game");
|
|
|
|
return Ok(());
|
2020-05-08 23:17:41 +00:00
|
|
|
}
|
2020-05-08 22:32:29 +00:00
|
|
|
_ => {
|
|
|
|
error!("got unknown FIFO data {}", str);
|
|
|
|
return Err(anyhow!("unknown FIFO data received"));
|
|
|
|
}
|
|
|
|
};
|
2020-05-08 21:54:57 +00:00
|
|
|
}
|
2020-05-08 20:30:43 +00:00
|
|
|
}
|
2020-05-09 03:44:03 +00:00
|
|
|
|
|
|
|
fn stick_distance(x: i8, y: i8) -> i8 {
|
|
|
|
let x = (x as f64).powi(2);
|
|
|
|
let y = (y as f64).powi(2);
|
|
|
|
(x + y).sqrt() as i8
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
|
|
|
#[test]
|
|
|
|
fn stick_distance() {
|
|
|
|
for case in [
|
|
|
|
(0, 0, 0),
|
|
|
|
(127, 0, 127),
|
|
|
|
(64, 64, 90),
|
|
|
|
(-64, 64, 90),
|
|
|
|
(-64, -64, 90),
|
|
|
|
]
|
|
|
|
.iter()
|
|
|
|
{
|
|
|
|
let x = case.0;
|
|
|
|
let y = case.1;
|
|
|
|
assert_eq!(crate::stick_distance(x, y), case.2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|