1use std::error::Error;
2use std::net::TcpStream;
3
4use serde::{Deserialize, Serialize};
5use strum_macros::Display;
6use 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 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 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}