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 #[allow(clippy::wildcard_enum_match_arm)]
270 pub fn from_buffer(buf: &[u8]) -> Result<Self, Error> {
271 let mut pos = 0;
272
273 if buf.len() < 4 {
275 return Err(Error::BufferTooSmall);
276 }
277 let rsp_u32 = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
278 let response: Response = rsp_u32.into();
279 pos += 4;
280 match response {
281 Response::RomEntry => (), Response::NoRom => Err(Error::NoRom)?,
283 Response::RomNotRecognised => Err(Error::RomNotRecognised)?,
284 _ => {
285 warn!("Unexpected response code for RomMetadata: {rsp_u32:#010X} {response:?}");
286 Err(Error::InvalidResponse)?
287 }
288 }
289
290 let name_end = buf[pos..].iter().position(|&b| b == 0).ok_or_else(|| {
292 warn!("Name string not null-terminated");
293 Error::InvalidData
294 })?;
295 let name = String::from_utf8(buf[pos..pos + name_end].to_vec()).map_err(|_| {
296 warn!("Invalid UTF-8 in name");
297 Error::InvalidData
298 })?;
299 pos += name_end + 1; let part_end = buf[pos..].iter().position(|&b| b == 0).ok_or_else(|| {
303 warn!("Part number string not null-terminated");
304 Error::InvalidData
305 })?;
306 let part_number = String::from_utf8(buf[pos..pos + part_end].to_vec()).map_err(|_| {
307 warn!("Invalid UTF-8 in part number");
308 Error::InvalidData
309 })?;
310 pos += part_end + 1; if buf.len() < pos + 4 {
314 warn!("Buffer too short for checksum");
315 return Err(Error::BufferTooSmall);
316 }
317 let checksum = u32::from_le_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
318 pos += 4;
319
320 if buf.len() < pos + 20 {
322 warn!("Buffer too short for SHA1 Digest");
323 return Err(Error::BufferTooSmall);
324 }
325 let mut sha1 = [0u8; 20];
326 sha1.copy_from_slice(&buf[pos..pos + 20]);
327
328 Ok(LabRomEntry {
329 name,
330 part_number,
331 checksum,
332 sha1,
333 })
334 }
335
336 fn buf_size(&self) -> usize {
337 Response::size() + self.name.len() + 1 + self.part_number.len() + 1 + 4 + 20
338 }
339
340 pub fn to_buffer(&self) -> Result<Vec<u8>, Error> {
341 let mut pos = 0;
342
343 let size = self.buf_size();
344 let mut buf = vec![0; size];
345
346 if size < 4 {
348 return Err(Error::BufferTooSmall);
349 }
350 let rsp_u32 = Response::RomEntry as u32;
351 buf[pos..pos + 4].copy_from_slice(&rsp_u32.to_le_bytes());
352 pos += 4;
353
354 let name_bytes = self.name.as_bytes();
356 if size < pos + name_bytes.len() + 1 {
357 return Err(Error::BufferTooSmall);
358 }
359 buf[pos..pos + name_bytes.len()].copy_from_slice(name_bytes);
360 pos += name_bytes.len();
361 buf[pos] = 0; pos += 1;
363
364 let part_bytes = self.part_number.as_bytes();
366 if size < pos + part_bytes.len() + 1 {
367 return Err(Error::BufferTooSmall);
368 }
369 buf[pos..pos + part_bytes.len()].copy_from_slice(part_bytes);
370 pos += part_bytes.len();
371 buf[pos] = 0; pos += 1;
373
374 if size < pos + 4 {
376 return Err(Error::BufferTooSmall);
377 }
378 buf[pos..pos + 4].copy_from_slice(&self.checksum.to_le_bytes());
379 pos += 4;
380
381 if size < pos + 20 {
383 return Err(Error::BufferTooSmall);
384 }
385 buf[pos..pos + 20].copy_from_slice(&self.sha1);
386
387 Ok(buf)
388 }
389}
390
391impl From<RomEntry> for LabRomEntry {
392 fn from(entry: RomEntry) -> Self {
393 LabRomEntry {
394 name: entry.name().to_string(),
395 part_number: entry.part().to_string(),
396 checksum: entry.sum(),
397 sha1: *entry.sha1(),
398 }
399 }
400}