1use std::cmp::min;
2use std::time::Duration;
3use bytes::{BufMut, BytesMut};
4use libftd2xx::{BitMode, DeviceInfo, Ftdi, FtdiCommon};
5use log::debug;
6use crate::{Error, Flashcart, Result};
7use crate::carts::{Cic, SaveType};
8use crate::Error::CommunicationFailed;
9use crate::unfloader::{DataType, DebugResponse};
10
11#[derive(Clone, PartialEq, Debug)]
12pub enum Command {
13 LoadFromPc {
14 addr: u32,
15 bank_id_len: u32,
16 data: Vec<u8>,
17 },
18 DumpToPc {
19 addr: u32,
20 bank_id_len: u32,
21 },
22 TargetSideFifo(Vec<u8>),
23 SetSaveType(SaveType),
24 SetCicType(Cic),
25 SetCiExtended(u32),
26 VersionRequest,
27}
28impl Command {
29 pub fn id(&self) -> u8 {
30 use Command::*;
31 match self {
32 LoadFromPc { .. } => 0x20,
33 DumpToPc { .. } => 0x30,
34 TargetSideFifo(_) => 0x40,
35 SetSaveType(_) => 0x70,
36 SetCicType(_) => 0x72,
37 SetCiExtended(_) => 0x74,
38 VersionRequest => 0x80,
39 }
40 }
41
42 pub fn encode_packet(&self) -> Vec<u8> {
43 let mut packet = BytesMut::from([self.id(), 0x43, 0x4D, 0x44].as_ref());
44
45 use Command::*;
46 match self {
47 LoadFromPc { addr, bank_id_len, data } => {
48 packet.put_u32(*addr);
49 packet.put_u32(*bank_id_len);
50 packet.put_slice(data);
51 },
52 DumpToPc { addr, bank_id_len } => {
53 packet.put_u32(*addr);
54 packet.put_u32(*bank_id_len);
55 },
56 TargetSideFifo(data) => packet.put_slice(data),
57 SetSaveType(savetype) => packet.put_u32((savetype_index(*savetype).unwrap_or(0) as u32) & 0x0000000F),
58 SetCicType(cic) => packet.put_u32((cic_index(*cic).unwrap_or(1) & 0x7) as u32 | 0x80000000),
59 SetCiExtended(enable) => packet.put_u32(*enable),
60 VersionRequest => (),
61 }
62
63 packet.to_vec()
64 }
65
66 pub fn recv_length(&self) -> u32 {
67 use Command::*;
68 match self {
69 LoadFromPc { .. } => 0,
70 DumpToPc { bank_id_len, .. } => bank_id_len & 0x00FFFFFF,
71 TargetSideFifo(_) => 0,
72 SetSaveType(_) => 0,
73 SetCicType(_) => 0,
74 SetCiExtended(_) => 0,
75 VersionRequest => 8,
76 }
77 }
78
79 pub fn complete_check<D: AsRef<[u8]>>(&self, data: D) -> Result<()> {
88 let expected = [0x43, 0x4D, 0x50, self.id()];
89 if expected == data.as_ref() {
90 Ok(())
91 } else {
92 Err(CommunicationFailed(format!("64Drive: complete packet mismatch: {:02X?} vs expected {expected:02X?}", data.as_ref())))
93 }
94 }
95}
96
97#[derive(Copy, Clone, Debug, PartialEq)]
98pub enum Segment {
99 Rom,
100 Sram256,
101 Sram768,
102 FlashRam,
103 Eeprom4,
104 Eeprom16,
105}
106impl Segment {
107 pub fn max_length(self, cart: &mut SixtyFourDrive) -> u32 {
108 use Segment::*;
109 match self {
110 Rom if !cart.is_hw1().unwrap_or(false) => 240 * 1024 * 1024,
111 Rom => 64 * 1024 * 1024,
112 Sram256 => 32 * 1024,
113 Sram768 => 96 * 1024,
114 FlashRam => 128 * 1024,
115 Eeprom4 => 512,
116 Eeprom16 => 2 * 1024,
117 }
118 }
119}
120
121#[derive(Copy, Clone, Debug, PartialEq)]
122pub enum Model {
123 HW1, HW2,
124}
125
126#[derive(Debug)]
127pub struct SixtyFourDrive {
128 device: Ftdi,
129}
130impl Flashcart for SixtyFourDrive {
131 fn upload_rom(&mut self, data: &[u8]) -> Result<()> {
132 self.upload(Segment::Rom, 0, data)
133 }
134
135 fn download_rom(&mut self, length: u32) -> Result<Vec<u8>> {
136 self.download(Segment::Rom, 0, length)
137 }
138
139 fn set_cic(&mut self, cic: Cic) -> Result<()> {
140 let cic_index = (cic_index(cic).unwrap_or(1) & 0x7) as u32 | 0x80000000;
141
142 self.send_packet(Command::SetCicType(cic))?;
143
144 debug!("CIC is set {:#010X}", cic_index);
145 Ok(())
146 }
147
148 fn set_savetype(&mut self, savetype: SaveType) -> Result<()> {
149 let savetype_index = (savetype_index(savetype).unwrap_or(0) as u32) & 0x0000000F;
150
151 self.send_packet(Command::SetSaveType(savetype))?;
152
153 debug!("SaveType is set {:#010X}", savetype_index);
154 Ok(())
155 }
156
157 fn recv_debug(&mut self) -> Result<DebugResponse> {
158 let buf = self.ftdi_read(4)?;
159 if buf != b"DMA@"{
160 debug!("buf mismatch: {buf:02X?}");
161 std::thread::sleep(Duration::from_millis(5));
162 self.device.purge_rx()?;
163 return Err(Error::CommunicationFailed(format!("64drive: debug packet mismatch: {buf:02X?} vs expected {:02X?}", b"DMA@")))
164 }
165
166 let [kind, length @ ..]: [u8; 4] = self.ftdi_read(4)?.try_into().unwrap();
167
168 let kind = DataType::from(kind);
169 let length = u32::from_be_bytes([0, length[0], length[1], length[2]]) as usize;
170
171 let data = self.ftdi_read(length)?;
172
173 let complete = self.ftdi_read(4)?;
174 if complete != b"CMPH" {
175 return Err(Error::CommunicationFailed(format!("64drive: complete packet mismatch: {complete:02X?} vs expected {:02X?}", b"CMPH")));
176 }
177
178 debug!("Received {kind:?} data: {data:02X?}");
179
180 Ok((kind, data))
181 }
182
183 fn send_debug(&mut self) -> Result<()> {
184 todo!()
185 }
186
187 fn info(&mut self) -> Result<DeviceInfo> {
188 self.device.device_info().map_err(|err| err.into())
189 }
190}
191impl SixtyFourDrive {
192 pub fn new(mut device: Ftdi) -> Result<Self> {
193 device.reset().unwrap_or_default();
194 device.set_timeouts(Duration::from_secs(10), Duration::from_secs(10))?;
195
196 device.set_bit_mode(0xFF, BitMode::Reset)?;
197 device.set_bit_mode(0xFF, BitMode::SyncFifo)?;
198
199 device.purge_all()?;
200
201 Ok(Self {
202 device,
203 })
204 }
205
206 fn is_hw1(&mut self) -> Result<bool> {
207 let info = self.info()?;
208
209 Ok(match (info.vendor_id, info.product_id, info.description.as_str()) {
210 (0x0403, 0x6010, "64drive USB device A") => true,
211 _ => false
212 })
213 }
214
215 pub fn upload(&mut self, segment: Segment, offset: u32, data: &[u8]) -> Result<()> {
216 const SIZE: u32 = 0x800000;
217
218 let chunks = (data.len() as f32 / SIZE as f32).ceil() as u32;
219 let bank = bank_index(&segment, self.is_hw1()?, false); let mut data_index = 0;
222 for i in 0..chunks {
223 let length = min(data.len() - data_index, SIZE as usize);
224
225 let addr = offset + (i as u32 * SIZE);
226 let bank_id_len = bank | (length as u32 & 0x00FFFFFF);
227
228 let cmd = Command::LoadFromPc {
229 addr,
230 bank_id_len,
231 data: data[data_index..(data_index + length)].to_vec(),
232 };
233
234 data_index += length;
235
236 debug!("Uploading data. offset: {addr:#010X}, banklen: {bank_id_len:#010X}");
237 self.send_packet(cmd)?;
238 debug!("Write complete.");
239 }
240
241 debug!("Upload complete!");
242 Ok(())
243 }
244
245 pub fn download(&mut self, segment: Segment, offset: u32, mut length: u32) -> Result<Vec<u8>> {
246 const SIZE: u32 = 0x20000;
247
248 if length == 0 {
249 return Ok(vec![]);
250 }
251
252 if length & 3 > 0 {
253 length = length + (4 - (length & 3));
254 }
255 length = min(length, segment.max_length(self));
256
257 let chunks = (length as f32 / SIZE as f32).ceil() as u32;
258 let bank = bank_index(&segment, self.is_hw1()?, false); let mut data = vec![];
261 let mut data_index = 0;
262 for i in 0..chunks {
263 let length = min(length - data_index, SIZE);
264 if (length & 0x00FFFFFF) < 4 {
265 break;
266 }
267 data_index += length;
268
269 let addr = offset + (i * SIZE);
270 let bank_id_len = bank | (length & 0x00FFFFFF);
271
272 let cmd = Command::DumpToPc {
273 addr,
274 bank_id_len,
275 };
276
277 debug!("Downloading data. offset: {addr:#010X}, banklen: {bank_id_len:#010X}");
278 let buf = self.send_packet(cmd)?;
279 debug!("Read complete.");
280
281 data.extend_from_slice(&buf);
282 }
283
284 debug!("Download complete! {:.4} MiB", data.len() as f32 / (1024.0 * 1024.0));
285 Ok(data)
286 }
287
288 fn send_packet(&mut self, cmd: Command) -> Result<Vec<u8>> {
289 self.ftdi_write(cmd.encode_packet())?;
290
291 let response = self.ftdi_read(cmd.recv_length() as usize)?;
292 cmd.complete_check(self.ftdi_read(4)?)?;
293
294 Ok(response)
295 }
296
297 fn ftdi_read(&mut self, length: usize) -> Result<Vec<u8>> {
298 let mut buf = vec![0xFFu8; length];
299 if length == 0 {
300 return Ok(buf);
301 }
302
303 self.device.read_all(&mut buf)?;
304
305 Ok(buf)
306 }
307
308 fn ftdi_write<D: AsRef<[u8]>>(&mut self, data: D) -> Result<()> {
309 self.device.write_all(data.as_ref()).map_err(|err| err.into())
310 }
311}
312
313
314
315fn bank_index(segment: &Segment, is_hw1: bool, is_stadium: bool) -> u32 {
316 use Segment::*;
317
318 (match segment {
319 Rom => 1,
320 Sram256 => 2,
321 Sram768 => 3,
322 FlashRam if is_hw1 && is_stadium => 5,
323 FlashRam => 4,
324 Eeprom4 => 6,
325 Eeprom16 => 6,
326 } << 24)
327}
328
329fn cic_index(cic: Cic) -> Option<u8> {
333 use Cic::*;
334
335 match cic {
336 Var6101 => Some(0),
337 Var6102 => Some(1),
338 Var7101 => Some(2),
339 Var7102 => Some(3),
340 VarX103 => Some(4),
341 VarX105 => Some(5),
342 VarX106 => Some(6),
343 Var5101 => Some(7),
344 Auto | Unknown => None
345 }
346}
347
348fn savetype_index(savetype: SaveType) -> Option<u8> {
349 use SaveType::*;
350
351 match savetype {
352 Nothing => Some(0),
353 Eeprom4Kbit => Some(1),
354 Eeprom16Kbit => Some(2),
355 Sram256Kbit => Some(3),
356 FlashRam1Mbit => Some(4),
357 Sram768Kbit => Some(5),
358 FlashRam1MbitStadium => Some(6),
359 Auto | Unknown => None,
360 }
361}