rusb2snes/
lib.rs

1use std::error::Error;
2use std::net::TcpStream;
3
4use serde::{Deserialize, Serialize};
5use strum_macros::Display;
6// use tungstenite::Error;
7use tungstenite::{connect, stream::MaybeTlsStream, Message, WebSocket};
8
9#[derive(Display, Debug)]
10pub enum Command {
11    AppVersion,
12    Name,
13    DeviceList,
14    Attach,
15    Info,
16    Boot,
17    Reset,
18    Menu,
19
20    List,
21    PutFile,
22    GetFile,
23    Rename,
24    Remove,
25
26    GetAddress,
27}
28
29#[derive(Display, Debug)]
30#[allow(dead_code)]
31pub enum Space {
32    SNES,
33    CMD,
34}
35
36#[derive(Debug, PartialEq)]
37pub struct Infos {
38    pub version: String,
39    pub dev_type: String,
40    pub game: String,
41    pub flags: Vec<String>,
42}
43
44#[derive(Serialize)]
45#[allow(non_snake_case)]
46struct USB2SnesQuery {
47    Opcode: String,
48    Space: String,
49    Flags: Vec<String>,
50    Operands: Vec<String>,
51}
52
53#[derive(Deserialize)]
54#[allow(non_snake_case)]
55struct USB2SnesResult {
56    Results: Vec<String>,
57}
58
59#[derive(Debug, PartialEq)]
60pub enum USB2SnesFileType {
61    File = 0,
62    Dir = 1,
63}
64
65#[derive(Deserialize, Serialize, Debug)]
66pub struct USB2SnesEndpoint {
67    pub address: String,
68    pub port: u16,
69}
70
71impl Default for USB2SnesEndpoint {
72    fn default() -> Self {
73        USB2SnesEndpoint {
74            address: "127.0.0.1".to_string(),
75            port: 23074,
76        }
77    }
78}
79
80#[derive(Debug, PartialEq)]
81pub struct USB2SnesFileInfo {
82    pub name: String,
83    pub file_type: USB2SnesFileType,
84}
85
86pub struct SyncClient {
87    client: WebSocket<MaybeTlsStream<TcpStream>>,
88    devel: bool,
89}
90
91impl SyncClient {
92    pub fn connect(endpoint: &USB2SnesEndpoint) -> Result<SyncClient, Box<dyn Error>> {
93        let ws = format!("ws://{}:{}", endpoint.address, endpoint.port);
94        let (client, _) = connect(ws).map_err(|e| Box::new(e) as Box<dyn Error>)?;
95        Ok(SyncClient {
96            client,
97            devel: false,
98        })
99    }
100
101    pub fn connect_with_devel(endpoint: &USB2SnesEndpoint) -> Result<SyncClient, Box<dyn Error>> {
102        let mut client = SyncClient::connect(endpoint)?;
103        client.devel = true;
104        Ok(client)
105    }
106
107    fn send_command(&mut self, command: Command, args: Vec<String>) -> Result<(), Box<dyn Error>> {
108        self.send_command_with_space(command, Space::SNES, args)?;
109        Ok(())
110    }
111
112    fn send_command_with_space(
113        &mut self,
114        command: Command,
115        space: Space,
116        args: Vec<String>,
117    ) -> Result<(), Box<dyn Error>> {
118        if self.devel {
119            println!("Send command : {:?}", command);
120        }
121        // let nspace: String = space.map(|sp| sp.to_string());
122        let query = USB2SnesQuery {
123            Opcode: command.to_string(),
124            Space: space.to_string(),
125            Flags: vec![],
126            Operands: args,
127        };
128        let json = serde_json::to_string_pretty(&query)?;
129        if self.devel {
130            println!("{}", json);
131        }
132        let message = Message::text(json);
133        self.client.send(message)?;
134        Ok(())
135    }
136
137    fn get_reply(&mut self) -> Result<USB2SnesResult, Box<dyn Error>> {
138        let reply = self.client.read()?;
139        let textreply = match reply {
140            Message::Text(value) => value.to_string(),
141            _ => {
142                return Err("Error getting a reply test".into());
143            }
144        };
145        Ok(serde_json::from_str(&textreply)?)
146    }
147
148    pub fn set_name(&mut self, name: String) -> Result<(), Box<dyn Error>> {
149        self.send_command(Command::Name, vec![name])?;
150        Ok(())
151    }
152
153    pub fn app_version(&mut self) -> Result<String, Box<dyn Error>> {
154        self.send_command(Command::AppVersion, vec![])?;
155        let usbreply = self.get_reply()?;
156        Ok(usbreply.Results[0].to_string())
157    }
158
159    pub fn list_device(&mut self) -> Result<Vec<String>, Box<dyn Error>> {
160        self.send_command(Command::DeviceList, vec![])?;
161        let usbreply = self.get_reply()?;
162        Ok(usbreply.Results)
163    }
164
165    pub fn attach(&mut self, device: &String) -> Result<(), Box<dyn Error>> {
166        self.send_command(Command::Attach, vec![device.to_string()])?;
167        Ok(())
168    }
169
170    pub fn info(&mut self) -> Result<Infos, Box<dyn Error>> {
171        self.send_command(Command::Info, vec![])?;
172        let usbreply = self.get_reply()?;
173        let info: Vec<String> = usbreply.Results;
174        Ok(Infos {
175            version: info[0].clone(),
176            dev_type: info[1].clone(),
177            game: info[2].clone(),
178            flags: (info[3..].to_vec()),
179        })
180    }
181
182    pub fn reset(&mut self) -> Result<(), Box<dyn Error>> {
183        self.send_command(Command::Reset, vec![])?;
184        Ok(())
185    }
186
187    pub fn menu(&mut self) -> Result<(), Box<dyn Error>> {
188        self.send_command(Command::Menu, vec![])?;
189        Ok(())
190    }
191
192    pub fn boot(&mut self, toboot: &str) -> Result<(), Box<dyn Error>> {
193        self.send_command(Command::Boot, vec![toboot.to_owned()])?;
194        Ok(())
195    }
196
197    pub fn ls(&mut self, path: &String) -> Result<Vec<USB2SnesFileInfo>, Box<dyn Error>> {
198        self.send_command(Command::List, vec![path.to_string()])?;
199        let usbreply = self.get_reply()?;
200        let vec_info = usbreply.Results;
201        let mut toret: Vec<USB2SnesFileInfo> = vec![];
202        let mut i = 0;
203        while i < vec_info.len() {
204            let info: USB2SnesFileInfo = USB2SnesFileInfo {
205                file_type: if vec_info[i] == "1" {
206                    USB2SnesFileType::File
207                } else {
208                    USB2SnesFileType::Dir
209                },
210                name: vec_info[i + 1].to_string(),
211            };
212            toret.push(info);
213            i += 2;
214        }
215        Ok(toret)
216    }
217
218    pub fn send_file(&mut self, path: &String, data: Vec<u8>) -> Result<(), Box<dyn Error>> {
219        self.send_command(
220            Command::PutFile,
221            vec![path.to_string(), format!("{:x}", data.len())],
222        )?;
223        let mut start = 0;
224        let mut stop = 1024;
225        // let test = Bytes::from(data);
226        let data_len = data.len();
227
228        while start < data_len {
229            let odata = data[start..stop].to_owned();
230            let message = Message::binary(odata);
231            self.client.send(message)?;
232            start = stop;
233            stop += 1024;
234            if stop > data.len() {
235                stop = data.len();
236            }
237        }
238        Ok(())
239    }
240
241    pub fn get_file(&mut self, path: &str) -> Result<Vec<u8>, Box<dyn Error>> {
242        self.send_command(Command::GetFile, vec![path.to_owned()])?;
243        let usb2snes_reply = self.get_reply()?;
244        let string_hex = &usb2snes_reply.Results[0];
245        let size = usize::from_str_radix(string_hex, 16)?;
246        let mut data: Vec<u8> = Vec::with_capacity(size);
247        loop {
248            let reply = self.client.read()?;
249            match reply {
250                Message::Binary(msgdata) => {
251                    data.extend(&msgdata);
252                }
253                _ => {
254                    return Err(format!("Error getting file {}", path).into());
255                }
256            }
257            if data.len() == size {
258                break;
259            }
260        }
261        Ok(data)
262    }
263
264    pub fn remove_path(&mut self, path: &str) -> Result<(), Box<dyn Error>> {
265        self.send_command(Command::Remove, vec![path.to_owned()])?;
266        Ok(())
267    }
268
269    pub fn get_address(&mut self, address: u32, size: usize) -> Result<Vec<u8>, Box<dyn Error>> {
270        self.send_command_with_space(
271            Command::GetAddress,
272            Space::SNES,
273            vec![format!("{:x}", address), format!("{:x}", size)],
274        )?;
275        let mut data: Vec<u8> = Vec::with_capacity(size);
276        loop {
277            let reply = self.client.read()?;
278            match reply {
279                Message::Binary(msgdata) => {
280                    data.extend(&msgdata);
281                }
282                _ => {
283                    return Err(format!("Error getting a reply from address {:x}", &address).into())
284                }
285            }
286            if data.len() == size {
287                break;
288            }
289        }
290        Ok(data)
291    }
292
293    pub fn get_multi_address_as_vec(
294        &mut self,
295        addresses: Vec<u32>,
296        sizes: Vec<usize>,
297    ) -> Result<Vec<u8>, Box<dyn Error>> {
298        let mut v_arg: Vec<String> = Vec::with_capacity(addresses.len() * 2);
299        let mut cpt = 0;
300        let mut total_size: usize = 0;
301        while cpt < addresses.len() {
302            v_arg.push(format!("{:x}", addresses[cpt]));
303            v_arg.push(format!("{:x}", sizes[cpt]));
304            total_size += sizes[cpt];
305            cpt += 1
306        }
307        self.send_command_with_space(Command::GetAddress, Space::SNES, v_arg)?;
308        let data = self.parse_multi_addresses(total_size)?;
309        Ok(data)
310    }
311
312    pub fn get_multi_address_from_pairs(
313        &mut self,
314        pairs: &[(u32, usize)],
315    ) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
316        let mut args = vec![];
317        let mut total_size = 0;
318        for &(address, size) in pairs.iter() {
319            args.push(format!("{:x}", address));
320            args.push(format!("{:x}", size));
321            total_size += size;
322        }
323        self.send_command_with_space(Command::GetAddress, Space::SNES, args)?;
324        let data = self.parse_multi_addresses(total_size)?;
325        let mut ret: Vec<Vec<u8>> = vec![];
326        let mut consumed = 0;
327        for &(_address, size) in pairs.iter() {
328            ret.push(data[consumed..consumed + size].into());
329            consumed += size;
330        }
331        Ok(ret)
332    }
333
334    fn parse_multi_addresses(&mut self, size: usize) -> Result<Vec<u8>, Box<dyn Error>> {
335        let mut data: Vec<u8> = Vec::with_capacity(size);
336        loop {
337            let reply = self.client.read()?;
338            match reply {
339                Message::Binary(msgdata) => {
340                    data.extend(&msgdata);
341                }
342                _ => return Err("Error parsing a reply from multiple addresses".into()),
343            }
344            if data.len() == size {
345                break;
346            }
347        }
348        Ok(data)
349    }
350}