1use alloc::string::{String, ToString};
17use alloc::vec;
18use alloc::vec::Vec;
19#[allow(unused_imports)]
20use log::{debug, error, info, trace, warn};
21
22use onerom_database::{RomEntry, RomType};
23
24use crate::Error;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28#[repr(u32)]
29pub enum Command {
30 Ping = 0x0000_0000,
32
33 ReadRom = 0x0000_0001,
35
36 GetRawData = 0x0000_0002,
39
40 Unknown = 0xFFFF_FFFF,
42}
43
44impl From<u32> for Command {
45 fn from(value: u32) -> Self {
46 match value {
47 0x0000_0000 => Command::Ping,
48 0x0000_0001 => Command::ReadRom,
49
50 _ => Command::Unknown,
51 }
52 }
53}
54
55impl From<Command> for u32 {
56 fn from(cmd: Command) -> Self {
57 cmd as u32
58 }
59}
60
61impl Command {
62 pub fn size() -> usize {
63 core::mem::size_of::<Self>()
64 }
65
66 pub fn as_bytes(&self) -> [u8; 4] {
67 (*self as u32).to_le_bytes()
68 }
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73#[repr(u32)]
74pub enum Response {
75 Pong = 0x0000_0000,
77
78 RomEntry = 0x0000_0001,
85
86 RomNotRecognised = 0x0000_0002,
88
89 Error = 0x8000_0000,
91
92 NoRom = 0x8000_0001,
95
96 Unknown = 0xFFFF_FFFF,
97}
98
99impl Response {
100 pub const fn size() -> usize {
101 core::mem::size_of::<Self>()
102 }
103
104 pub fn to_bytes(&self, buf: &mut [u8]) {
105 let value = *self as u32;
106 buf[..4].copy_from_slice(&value.to_le_bytes());
107 }
108}
109
110impl From<u32> for Response {
111 fn from(value: u32) -> Self {
112 match value {
113 0x0000_0000 => Response::Pong,
114 0x0000_0001 => Response::RomEntry,
115 0x0000_0002 => Response::RomNotRecognised,
116 0x8000_0000 => Response::Error,
117 0x8000_0001 => Response::NoRom,
118 _ => Response::Unknown,
119 }
120 }
121}
122
123#[derive(Debug, serde::Serialize, serde::Deserialize)]
124pub struct GetRawData {
125 rom_type: RomType,
127}
128
129impl GetRawData {
130 const fn binary_size() -> usize {
131 RomType::binary_size()
132 }
133
134 pub fn from_buffer(buf: &[u8]) -> Result<Self, Error> {
135 let rom_type_size = Self::binary_size();
137 if buf.len() < rom_type_size {
138 return Err(Error::BufferTooSmall);
139 }
140 let rom_type = RomType::from_bytes(&buf[0..rom_type_size])?;
141
142 Ok(Self { rom_type })
143 }
144
145 pub fn to_buffer(&self) -> Result<Vec<u8>, Error> {
146 let mut pos = 0;
147
148 let size = Self::binary_size() + Command::size();
149 let mut buf = vec![0; size];
150
151 if size < Command::size() {
153 return Err(Error::BufferTooSmall);
154 }
155 let rsp_u32 = Command::GetRawData as u32;
156 buf[pos..pos + 4].copy_from_slice(&rsp_u32.to_le_bytes());
157 pos += 4;
158
159 let rom_type_size = RomType::binary_size();
161 if size < pos + rom_type_size {
162 return Err(Error::BufferTooSmall);
163 }
164 self.rom_type.to_bytes(&mut buf[pos..pos + rom_type_size])?;
165
166 Ok(buf)
167 }
168}
169
170#[derive(Debug, serde::Serialize, serde::Deserialize)]
171pub struct LabRomType {
172 #[serde(rename = "ROM Type")]
173 rom_type: RomType,
174}
175
176#[derive(Debug, serde::Serialize, serde::Deserialize)]
177pub struct RomRawData {
178 #[serde(rename = "ROM Type")]
179 rom_type: RomType,
180 #[serde(rename = "Checksum")]
181 checksum: u32,
182 #[serde(rename = "SHA1 Digest")]
183 sha1: [u8; 20],
184}
185
186impl RomRawData {
187 const fn binary_size() -> usize {
188 RomType::binary_size() + 4 + 20
189 }
190
191 pub fn from_buffer(buf: &[u8]) -> Result<Self, Error> {
192 let mut pos = 0;
193
194 let rom_type_size = RomType::binary_size();
196 if buf.len() < pos + rom_type_size {
197 return Err(Error::BufferTooSmall);
198 }
199 let rom_type = RomType::from_bytes(&buf[pos..pos + rom_type_size])?;
200 pos += rom_type_size;
201
202 if buf.len() < pos + 4 {
204 return Err(Error::BufferTooSmall);
205 }
206 let checksum = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
207 pos += 4;
208
209 if buf.len() < pos + 20 {
211 return Err(Error::BufferTooSmall);
212 }
213 let mut sha1 = [0u8; 20];
214 sha1.copy_from_slice(&buf[pos..pos + 20]);
215
216 Ok(Self {
217 rom_type,
218 checksum,
219 sha1,
220 })
221 }
222
223 pub fn to_buffer(&self) -> Result<Vec<u8>, Error> {
224 let mut pos = 0;
225
226 let size = Self::binary_size();
227 let mut buf = vec![0; size];
228
229 let rom_type_size = RomType::binary_size();
231 if size < pos + rom_type_size {
232 return Err(Error::BufferTooSmall);
233 }
234 self.rom_type.to_bytes(&mut buf[pos..pos + rom_type_size])?;
235 pos += rom_type_size;
236
237 if size < pos + 4 {
239 return Err(Error::BufferTooSmall);
240 }
241 buf[pos..pos + 4].copy_from_slice(&self.checksum.to_le_bytes());
242 pos += 4;
243
244 if size < pos + 20 {
246 return Err(Error::BufferTooSmall);
247 }
248 buf[pos..pos + 20].copy_from_slice(&self.sha1);
249
250 Ok(buf)
251 }
252}
253
254#[derive(Debug, serde::Serialize, serde::Deserialize)]
256pub struct LabRomEntry {
257 #[serde(rename = "Name")]
258 name: String,
259 #[serde(rename = "Part Number")]
260 part_number: String,
261 #[serde(rename = "Checksum")]
262 checksum: u32,
263 #[serde(rename = "SHA1 Digest")]
264 sha1: [u8; 20],
265}
266
267impl LabRomEntry {
268 pub fn from_buffer(buf: &[u8]) -> Result<Self, Error> {
270 let mut pos = 0;
271
272 if buf.len() < 4 {
274 return Err(Error::BufferTooSmall);
275 }
276 let rsp_u32 = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
277 let response: Response = rsp_u32.into();
278 pos += 4;
279 match response {
280 Response::RomEntry => (), Response::NoRom => Err(Error::NoRom)?,
282 Response::RomNotRecognised => Err(Error::RomNotRecognised)?,
283 _ => {
284 warn!("Unexpected response code for RomMetadata: {rsp_u32:#010X} {response:?}");
285 Err(Error::InvalidResponse)?
286 }
287 }
288
289 let name_end = buf[pos..].iter().position(|&b| b == 0).ok_or_else(|| {
291 warn!("Name string not null-terminated");
292 Error::InvalidData
293 })?;
294 let name = String::from_utf8(buf[pos..pos + name_end].to_vec()).map_err(|_| {
295 warn!("Invalid UTF-8 in name");
296 Error::InvalidData
297 })?;
298 pos += name_end + 1; let part_end = buf[pos..].iter().position(|&b| b == 0).ok_or_else(|| {
302 warn!("Part number string not null-terminated");
303 Error::InvalidData
304 })?;
305 let part_number = String::from_utf8(buf[pos..pos + part_end].to_vec()).map_err(|_| {
306 warn!("Invalid UTF-8 in part number");
307 Error::InvalidData
308 })?;
309 pos += part_end + 1; if buf.len() < pos + 4 {
313 warn!("Buffer too short for checksum");
314 return Err(Error::BufferTooSmall);
315 }
316 let checksum = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
317 pos += 4;
318
319 if buf.len() < pos + 20 {
321 warn!("Buffer too short for SHA1 Digest");
322 return Err(Error::BufferTooSmall);
323 }
324 let mut sha1 = [0u8; 20];
325 sha1.copy_from_slice(&buf[pos..pos + 20]);
326
327 Ok(LabRomEntry {
328 name,
329 part_number,
330 checksum,
331 sha1,
332 })
333 }
334
335 fn buf_size(&self) -> usize {
336 Response::size() + self.name.len() + 1 + self.part_number.len() + 1 + 4 + 20
337 }
338
339 pub fn to_buffer(&self) -> Result<Vec<u8>, Error> {
340 let mut pos = 0;
341
342 let size = self.buf_size();
343 let mut buf = vec![0; size];
344
345 if size < 4 {
347 return Err(Error::BufferTooSmall);
348 }
349 let rsp_u32 = Response::RomEntry as u32;
350 buf[pos..pos + 4].copy_from_slice(&rsp_u32.to_le_bytes());
351 pos += 4;
352
353 let name_bytes = self.name.as_bytes();
355 if size < pos + name_bytes.len() + 1 {
356 return Err(Error::BufferTooSmall);
357 }
358 buf[pos..pos + name_bytes.len()].copy_from_slice(name_bytes);
359 pos += name_bytes.len();
360 buf[pos] = 0; pos += 1;
362
363 let part_bytes = self.part_number.as_bytes();
365 if size < pos + part_bytes.len() + 1 {
366 return Err(Error::BufferTooSmall);
367 }
368 buf[pos..pos + part_bytes.len()].copy_from_slice(part_bytes);
369 pos += part_bytes.len();
370 buf[pos] = 0; pos += 1;
372
373 if size < pos + 4 {
375 return Err(Error::BufferTooSmall);
376 }
377 buf[pos..pos + 4].copy_from_slice(&self.checksum.to_le_bytes());
378 pos += 4;
379
380 if size < pos + 20 {
382 return Err(Error::BufferTooSmall);
383 }
384 buf[pos..pos + 20].copy_from_slice(&self.sha1);
385
386 Ok(buf)
387 }
388}
389
390impl From<RomEntry> for LabRomEntry {
391 fn from(entry: RomEntry) -> Self {
392 LabRomEntry {
393 name: entry.name().to_string(),
394 part_number: entry.part().to_string(),
395 checksum: entry.sum(),
396 sha1: *entry.sha1(),
397 }
398 }
399}