cat_dev/fsemul/pcfs/sata/proto/
mod.rs

1//! Protocols for the SATA 'PCFS' Protocol.
2//!
3//! This top level function is just all of the things that are common across
4//! the entire protocol, each packet serializer/deserializer/handler is kept
5//! in it's own sub-file.
6
7mod change_mode;
8mod change_owner;
9mod close_file;
10mod close_folder;
11mod create_folder;
12mod get_info_by_query;
13mod open_file;
14mod open_folder;
15mod ping;
16mod read_file;
17mod read_folder;
18mod remove;
19mod rename;
20mod rewind_folder;
21mod set_file_position;
22mod stat_file;
23mod write_file;
24
25use crate::{
26	errors::{CatBridgeError, FSError, NetworkError, NetworkParseError},
27	fsemul::{
28		HostFilesystem,
29		pcfs::errors::{PcfsApiError, SataProtocolError},
30	},
31	net::models::{IntoResponse, Response},
32};
33use bytes::{BufMut, Bytes, BytesMut};
34use std::{
35	fmt::{Debug, Display, Formatter, Result as FmtResult},
36	time::{Duration, SystemTime, UNIX_EPOCH},
37};
38use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
39
40pub use crate::fsemul::pcfs::sata::proto::{
41	change_mode::*, change_owner::*, close_file::*, close_folder::*, create_folder::*,
42	get_info_by_query::*, open_file::*, open_folder::*, ping::*, read_file::*, read_folder::*,
43	remove::*, rename::*, rewind_folder::*, set_file_position::*, stat_file::*, write_file::*,
44};
45
46/// The Default PCFS Version we claim to be.
47pub const DEFAULT_PCFS_VERSION: u32 = 0x0200_0600;
48
49/// The header for all of our SATA PCFS packets.
50#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct SataPacketHeader {
52	/// The length of the packet after the header.
53	packet_data_len: u32,
54	/// An "ID" for packets, THIS IS NOT THE PACKET TYPE.
55	///
56	/// This seemingly more used to correlate requests/responses on the client
57	/// side incase it sends multiple packets at once.
58	packet_id: u32,
59	/// A bitfield of various flags in the header.
60	///
61	/// These flags depend on the request command.
62	flags: u32,
63	/// The version of the PCFS protocol to use.
64	///
65	/// *note: this will be 0 at start, and then be filled in by the first
66	/// server response.*
67	version: u32,
68	/// The timestamp of the host. Yes, they are not ready for the 32 bit
69	/// overflow.
70	///
71	/// For us we actually end up *wrapping* around on our timestamp.
72	timestamp_on_host: u32,
73	/// The pid opf the running host process.
74	pid_on_host: u32,
75	// There are techincally 8 bytes of 0x0 that are used for 'padding'
76}
77
78impl SataPacketHeader {
79	/// Create a new packet header.
80	///
81	/// Most of the fields here will be set to `0`, or default values. As the
82	/// fields are usually not filled out by the client or changable.
83	#[must_use]
84	pub const fn new(packet_id: u32) -> Self {
85		Self {
86			packet_data_len: 0,
87			packet_id,
88			flags: 0,
89			version: DEFAULT_PCFS_VERSION,
90			timestamp_on_host: 0,
91			pid_on_host: 0,
92		}
93	}
94
95	#[must_use]
96	pub const fn data_len(&self) -> u32 {
97		self.packet_data_len
98	}
99
100	pub const fn set_data_len(&mut self, len: u32) {
101		self.packet_data_len = len;
102	}
103
104	#[must_use]
105	pub const fn id(&self) -> u32 {
106		self.packet_id
107	}
108
109	pub const fn set_id(&mut self, new_id: u32) {
110		self.packet_id = new_id;
111	}
112
113	#[must_use]
114	pub const fn flags(&self) -> u32 {
115		self.flags
116	}
117
118	pub const fn set_flags(&mut self, new_flags: u32) {
119		self.flags = new_flags;
120	}
121
122	#[must_use]
123	pub const fn version(&self) -> u32 {
124		self.version
125	}
126
127	#[must_use]
128	pub const fn raw_timestamp_on_host(&self) -> u32 {
129		self.timestamp_on_host
130	}
131
132	/// A "timestamp on the host".
133	///
134	/// Because this timestamp is *NOT* 32 bit epoch safe. These timestamps
135	/// may be *VERY WRONG* on purpose if it's past the year 2038. Don't come
136	/// blame me. Blame nintendo.
137	#[must_use]
138	pub fn host_timestamp(&self) -> SystemTime {
139		UNIX_EPOCH
140			.checked_add(Duration::from_secs(u64::from(self.timestamp_on_host)))
141			.unwrap_or_else(SystemTime::now)
142	}
143
144	#[must_use]
145	pub const fn host_pid(&self) -> u32 {
146		self.pid_on_host
147	}
148
149	/// Non-host packets should have empty fields for timestamp on host, and
150	/// pid.
151	///
152	/// ## Errors
153	///
154	/// If the non host side of a connection error'd.
155	pub fn ensure_not_from_host(&self) -> Result<(), SataProtocolError> {
156		if self.pid_on_host != 0 || self.timestamp_on_host != 0 {
157			return Err(SataProtocolError::NonHostSetHostOnlyHeaderFields(
158				self.timestamp_on_host,
159				self.pid_on_host,
160			));
161		}
162
163		Ok(())
164	}
165}
166
167impl From<&SataPacketHeader> for Bytes {
168	fn from(value: &SataPacketHeader) -> Self {
169		let mut buff = BytesMut::with_capacity(0x20);
170
171		buff.put_u32(value.packet_data_len);
172		buff.put_u32(value.packet_id);
173		buff.put_u32(value.flags);
174		buff.put_u32(value.version);
175		buff.put_u32(value.timestamp_on_host);
176		buff.put_u32(value.pid_on_host);
177		buff.extend([0; 8]);
178
179		buff.freeze()
180	}
181}
182
183impl From<SataPacketHeader> for Bytes {
184	fn from(value: SataPacketHeader) -> Self {
185		Self::from(&value)
186	}
187}
188
189impl TryFrom<Bytes> for SataPacketHeader {
190	type Error = NetworkParseError;
191
192	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
193		if value.len() < 0x20 {
194			return Err(NetworkParseError::FieldNotLongEnough(
195				"SataPacket",
196				"Header",
197				0x20,
198				value.len(),
199				value,
200			));
201		}
202		if value.len() > 0x20 {
203			return Err(NetworkParseError::UnexpectedTrailer(
204				"SataPacketHeader",
205				value.slice(0x20..),
206			));
207		}
208
209		let packet_data_len = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
210		let packet_id = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
211		let flags = u32::from_be_bytes([value[8], value[9], value[10], value[11]]);
212		let version = u32::from_be_bytes([value[12], value[13], value[14], value[15]]);
213		let timestamp_on_host = u32::from_be_bytes([value[16], value[17], value[18], value[19]]);
214		let pid_on_host = u32::from_be_bytes([value[20], value[21], value[22], value[23]]);
215
216		let should_be_padding = [
217			value[24], value[25], value[26], value[27], value[28], value[29], value[30], value[31],
218		];
219		if should_be_padding != [0x0; 8] {
220			return Err(SataProtocolError::HeaderBadPadding(should_be_padding).into());
221		}
222
223		Ok(Self {
224			packet_data_len,
225			packet_id,
226			flags,
227			version,
228			timestamp_on_host,
229			pid_on_host,
230		})
231	}
232}
233
234const SATA_PACKET_HEADER_FIELDS: &[NamedField<'static>] = &[
235	NamedField::new("data_len"),
236	NamedField::new("id"),
237	NamedField::new("flags"),
238	NamedField::new("version"),
239	NamedField::new("host_timestamp"),
240	NamedField::new("host_pid"),
241];
242
243impl Structable for SataPacketHeader {
244	fn definition(&self) -> StructDef<'_> {
245		StructDef::new_static("SataPacketHeader", Fields::Named(SATA_PACKET_HEADER_FIELDS))
246	}
247}
248
249impl Valuable for SataPacketHeader {
250	fn as_value(&self) -> Value<'_> {
251		Value::Structable(self)
252	}
253
254	fn visit(&self, visitor: &mut dyn Visit) {
255		visitor.visit_named_fields(&NamedValues::new(
256			SATA_PACKET_HEADER_FIELDS,
257			&[
258				Valuable::as_value(&self.packet_data_len),
259				Valuable::as_value(&self.packet_id),
260				Valuable::as_value(&self.flags),
261				Valuable::as_value(&self.version),
262				Valuable::as_value(&self.timestamp_on_host),
263				Valuable::as_value(&self.pid_on_host),
264			],
265		));
266	}
267}
268
269#[derive(Clone, Debug, PartialEq, Eq)]
270pub struct SataCommandInfo {
271	/// Logged as "user:" in various log messages.
272	user: (u32, u32),
273	/// Logged as "cap:" in various log messages.
274	capabilities: (u32, u32),
275	/// The 'command' to run, aka packet id.
276	command: u32,
277}
278
279impl SataCommandInfo {
280	/// Create a new set of command info.
281	#[must_use]
282	pub const fn new(user: (u32, u32), capabilities: (u32, u32), command: u32) -> Self {
283		Self {
284			user,
285			capabilities,
286			command,
287		}
288	}
289
290	/// Get the user bytes for this command.
291	#[must_use]
292	pub const fn user(&self) -> (u32, u32) {
293		self.user
294	}
295
296	/// Set the new user bytes for this command.
297	pub const fn set_user(&mut self, new: (u32, u32)) {
298		self.user = new;
299	}
300
301	/// Get the capabilities for this command.
302	///
303	/// In log messages the first byte covers capabilities:
304	/// 1,2,4,5. While the second byte contains "capability 3" which seems to be
305	/// forcing disabling of FFIO/CSR.
306	#[must_use]
307	pub const fn capabilities(&self) -> (u32, u32) {
308		self.capabilities
309	}
310
311	/// Set the capability bytes for this command.
312	pub const fn set_capabilities(&mut self, new: (u32, u32)) {
313		self.capabilities = new;
314	}
315
316	/// Get the actual 'packet id', or command.
317	#[must_use]
318	pub const fn command(&self) -> u32 {
319		self.command
320	}
321
322	/// Set the command id for this packet.
323	pub const fn set_command(&mut self, new: u32) {
324		self.command = new;
325	}
326}
327
328impl From<&SataCommandInfo> for Bytes {
329	fn from(value: &SataCommandInfo) -> Self {
330		let mut buff = BytesMut::with_capacity(0x14);
331		buff.put_u32(value.user.0);
332		buff.put_u32(value.user.1);
333		buff.put_u32(value.capabilities.0);
334		buff.put_u32(value.capabilities.1);
335		buff.put_u32(value.command);
336		buff.freeze()
337	}
338}
339
340impl From<SataCommandInfo> for Bytes {
341	fn from(value: SataCommandInfo) -> Self {
342		Self::from(&value)
343	}
344}
345
346impl TryFrom<Bytes> for SataCommandInfo {
347	type Error = NetworkParseError;
348
349	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
350		if value.len() < 0x14 {
351			return Err(NetworkParseError::FieldNotLongEnough(
352				"SataPacket",
353				"CommandInfo",
354				0x14,
355				value.len(),
356				value,
357			));
358		}
359		if value.len() > 0x14 {
360			return Err(NetworkParseError::UnexpectedTrailer(
361				"SataPacketCommandInfo",
362				value.slice(0x14..),
363			));
364		}
365
366		let user0 = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
367		let user1 = u32::from_be_bytes([value[4], value[5], value[6], value[7]]);
368		let cap0 = u32::from_le_bytes([value[8], value[9], value[10], value[11]]);
369		let cap1 = u32::from_le_bytes([value[12], value[13], value[14], value[15]]);
370		let cmd = u32::from_be_bytes([value[16], value[17], value[18], value[19]]);
371
372		Ok(Self {
373			user: (user0, user1),
374			capabilities: (cap0, cap1),
375			command: cmd,
376		})
377	}
378}
379
380const SATA_COMMAND_INFO_FIELDS: &[NamedField<'static>] = &[
381	NamedField::new("user.0"),
382	NamedField::new("user.1"),
383	NamedField::new("cap.0"),
384	NamedField::new("cap.1"),
385	NamedField::new("cap.2"),
386	NamedField::new("cap.3"),
387	NamedField::new("cap.4"),
388	NamedField::new("command"),
389];
390
391impl Structable for SataCommandInfo {
392	fn definition(&self) -> StructDef<'_> {
393		StructDef::new_static("SataCommandInfo", Fields::Named(SATA_COMMAND_INFO_FIELDS))
394	}
395}
396
397impl Valuable for SataCommandInfo {
398	fn as_value(&self) -> Value<'_> {
399		Value::Structable(self)
400	}
401
402	fn visit(&self, visitor: &mut dyn Visit) {
403		visitor.visit_named_fields(&NamedValues::new(
404			SATA_COMMAND_INFO_FIELDS,
405			&[
406				Valuable::as_value(&self.user.0),
407				Valuable::as_value(&self.user.1),
408				Valuable::as_value(&(self.capabilities.0 & 0xFF)),
409				Valuable::as_value(&((self.capabilities.0 >> 8) & 0xFF)),
410				Valuable::as_value(&((self.capabilities.1) & 0xFF)),
411				Valuable::as_value(&((self.capabilities.0 >> 16) & 0xFF)),
412				Valuable::as_value(&((self.capabilities.0 >> 24) & 0xFF)),
413				Valuable::as_value(&self.command),
414			],
415		));
416	}
417}
418
419impl Display for SataCommandInfo {
420	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
421		write!(
422			fmt,
423			"Cmd: {},   user: {} {},  cap: {} {} {} {} {}",
424			self.command,
425			self.user.0,
426			self.user.1,
427			self.capabilities.0 & 0xFF,
428			(self.capabilities.0 >> 8) & 0xFF,
429			self.capabilities.1 & 0xFF,
430			(self.capabilities.0 >> 16) & 0xFF,
431			(self.capabilities.0 >> 24) & 0xFF,
432		)
433	}
434}
435
436/// Move to a particular location inside of a file.
437#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Valuable)]
438pub enum MoveToFileLocation {
439	/// Move to the beginning of the file.
440	Begin,
441	/// Move nowhere.
442	Current,
443	/// Move to the end of a file.
444	End,
445}
446
447impl MoveToFileLocation {
448	/// Actually seek around a particular file descriptor on a host filesystem.
449	///
450	/// ## Errors
451	///
452	/// If we do end up calling seek file, and the host filesystem seek files
453	/// throws an error.
454	pub async fn do_move(
455		&self,
456		host_fs: &HostFilesystem,
457		handle: i32,
458		stream_id: Option<u64>,
459	) -> Result<(), FSError> {
460		match *self {
461			MoveToFileLocation::Begin => {
462				host_fs.seek_file(handle, true, stream_id).await?;
463			}
464			MoveToFileLocation::Current => {
465				// Luckily to move to current, we don't need to move at all.
466			}
467			MoveToFileLocation::End => {
468				host_fs.seek_file(handle, false, stream_id).await?;
469			}
470		}
471
472		Ok(())
473	}
474}
475
476impl From<&MoveToFileLocation> for u32 {
477	fn from(value: &MoveToFileLocation) -> u32 {
478		match *value {
479			MoveToFileLocation::Begin => 0,
480			MoveToFileLocation::Current => 1,
481			MoveToFileLocation::End => 2,
482		}
483	}
484}
485
486impl From<MoveToFileLocation> for u32 {
487	fn from(value: MoveToFileLocation) -> u32 {
488		Self::from(&value)
489	}
490}
491
492impl TryFrom<u32> for MoveToFileLocation {
493	type Error = SataProtocolError;
494
495	fn try_from(value: u32) -> Result<Self, Self::Error> {
496		match value {
497			0 => Ok(Self::Begin),
498			1 => Ok(Self::Current),
499			2 => Ok(Self::End),
500			val => Err(SataProtocolError::UnknownFileLocation(val)),
501		}
502	}
503}
504
505impl Display for MoveToFileLocation {
506	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
507		match self {
508			Self::Begin => write!(fmt, "Begin"),
509			Self::Current => write!(fmt, "Nowhere"),
510			Self::End => write!(fmt, "End"),
511		}
512	}
513}
514
515#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Valuable)]
516#[repr(transparent)]
517pub struct SataCapabilitiesFlags(pub u32);
518
519bitflags::bitflags! {
520	impl SataCapabilitiesFlags: u32 {
521		const FAST_FILE_IO_SUPPORTED = 0b0000_0010;
522		const COMBINED_SEND_RECV_SUPPORTED = 0b0000_0100;
523	}
524}
525
526/// A wrapper type that attachs headers, and more to a request.
527#[derive(Clone, Debug)]
528pub struct SataRequest<InnerTy: Debug> {
529	/// The header of the packet to send.
530	header: SataPacketHeader,
531	/// The command information to send to the server.
532	command_info: SataCommandInfo,
533	/// The body of the request.
534	body: InnerTy,
535}
536
537impl<InnerTy: Debug> SataRequest<InnerTy> {
538	/// Construct a new sata request.
539	#[must_use]
540	pub const fn new(
541		header: SataPacketHeader,
542		command_info: SataCommandInfo,
543		body: InnerTy,
544	) -> Self {
545		Self {
546			header,
547			command_info,
548			body,
549		}
550	}
551
552	#[must_use]
553	pub const fn header(&self) -> &SataPacketHeader {
554		&self.header
555	}
556
557	#[must_use]
558	pub const fn header_mut(&mut self) -> &mut SataPacketHeader {
559		&mut self.header
560	}
561
562	#[must_use]
563	pub const fn command_info(&self) -> &SataCommandInfo {
564		&self.command_info
565	}
566
567	#[must_use]
568	pub const fn command_info_mut(&mut self) -> &mut SataCommandInfo {
569		&mut self.command_info
570	}
571
572	#[must_use]
573	pub const fn body(&self) -> &InnerTy {
574		&self.body
575	}
576
577	#[must_use]
578	pub const fn body_mut(&mut self) -> &mut InnerTy {
579		&mut self.body
580	}
581
582	#[must_use]
583	pub fn into_parts(self) -> (SataPacketHeader, SataCommandInfo, InnerTy) {
584		(self.header, self.command_info, self.body)
585	}
586
587	/// Parse a sata request with an 'opaque' body, which means unparsed.
588	///
589	/// This is useful in several situations where we don't know the type of body
590	/// yet, so we keep it opaque.
591	///
592	/// ## Errors
593	///
594	/// If the packet header, or command info cannot be parsed.
595	pub fn parse_opaque(mut body: Bytes) -> Result<SataRequest<Bytes>, NetworkParseError> {
596		if body.len() < 0x34 {
597			return Err(NetworkParseError::NotEnoughData(
598				"SataRequest",
599				0x34,
600				body.len(),
601				body,
602			));
603		}
604
605		let header = SataPacketHeader::try_from(body.split_to(0x20))?;
606		let ci = SataCommandInfo::try_from(body.split_to(0x14))?;
607
608		Ok(SataRequest {
609			header,
610			command_info: ci,
611			body,
612		})
613	}
614}
615
616impl<ErrorTy, InnerTy: Debug> TryFrom<Bytes> for SataRequest<InnerTy>
617where
618	InnerTy: TryFrom<Bytes, Error = ErrorTy>,
619	ErrorTy: Into<NetworkParseError>,
620{
621	type Error = NetworkParseError;
622
623	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
624		let (header, ci, bytes) = Self::parse_opaque(value)?.into_parts();
625		let body = InnerTy::try_from(bytes).map_err(Into::into)?;
626
627		Ok(Self {
628			header,
629			command_info: ci,
630			body,
631		})
632	}
633}
634
635impl<InnerTy: Debug> From<SataRequest<InnerTy>> for Bytes
636where
637	InnerTy: Into<Bytes>,
638{
639	fn from(value: SataRequest<InnerTy>) -> Self {
640		let body = value.body.into();
641		let mut packet = BytesMut::with_capacity(0x34 + body.len());
642		packet.extend(Bytes::from(&value.header));
643		packet.extend(Bytes::from(&value.command_info));
644		packet.extend(body);
645		packet.freeze()
646	}
647}
648
649const SATA_REQUEST_FIELDS: &[NamedField<'static>] = &[
650	NamedField::new("header"),
651	NamedField::new("command_info"),
652	NamedField::new("body"),
653];
654
655impl<InnerTy: Debug> Structable for SataRequest<InnerTy> {
656	fn definition(&self) -> StructDef<'_> {
657		StructDef::new_static("SataRequest", Fields::Named(SATA_REQUEST_FIELDS))
658	}
659}
660
661impl<InnerTy: Debug> Valuable for SataRequest<InnerTy> {
662	fn as_value(&self) -> Value<'_> {
663		Value::Structable(self)
664	}
665
666	fn visit(&self, visitor: &mut dyn Visit) {
667		visitor.visit_named_fields(&NamedValues::new(
668			SATA_REQUEST_FIELDS,
669			&[
670				Valuable::as_value(&self.header),
671				Valuable::as_value(&self.command_info),
672				Valuable::as_value(&format!("{:?}", self.body)),
673			],
674		));
675	}
676}
677
678/// A wrapper type that just attaches headers, and more to a response.
679#[derive(Debug)]
680pub struct SataResponse<InnerTy: Debug> {
681	/// The header of the packet.
682	header: SataPacketHeader,
683	/// Flags to return in the header.
684	flags: u32,
685	/// The process id to report.
686	pid: u32,
687	/// Whether or not to force a '0' as our version.
688	force_zero_version: bool,
689	/// The body of the rsponse.
690	body: InnerTy,
691}
692
693impl<InnerTy: Debug> SataResponse<InnerTy> {
694	/// Construct a new sata response.
695	#[must_use]
696	pub const fn new(pid: u32, header: SataPacketHeader, body: InnerTy) -> Self {
697		Self {
698			header,
699			flags: 0,
700			pid,
701			body,
702			force_zero_version: false,
703		}
704	}
705
706	#[must_use]
707	pub const fn new_force_zero_version(pid: u32, header: SataPacketHeader, body: InnerTy) -> Self {
708		Self {
709			header,
710			flags: 0,
711			pid,
712			body,
713			force_zero_version: true,
714		}
715	}
716
717	#[must_use]
718	pub const fn new_with_flags(
719		pid: u32,
720		header: SataPacketHeader,
721		flags: u32,
722		body: InnerTy,
723	) -> Self {
724		Self {
725			header,
726			flags,
727			pid,
728			body,
729			force_zero_version: false,
730		}
731	}
732
733	#[must_use]
734	pub const fn from_existing(header: SataPacketHeader, body: InnerTy) -> Self {
735		let flags = header.flags();
736		let host_pid = header.host_pid();
737
738		Self {
739			header,
740			flags,
741			pid: host_pid,
742			body,
743			force_zero_version: false,
744		}
745	}
746
747	#[must_use]
748	pub const fn header(&self) -> &SataPacketHeader {
749		&self.header
750	}
751
752	#[must_use]
753	pub const fn body(&self) -> &InnerTy {
754		&self.body
755	}
756
757	#[must_use]
758	pub fn take_body(self) -> InnerTy {
759		self.body
760	}
761
762	#[must_use]
763	pub fn to_parts(self) -> (SataPacketHeader, InnerTy) {
764		(self.header, self.body)
765	}
766
767	/// Parse a sata response with an 'opaque' body, which means unparsed.
768	///
769	/// This is useful in several situations where we don't know the type of body
770	/// yet, so we keep it opaque.
771	///
772	/// ## Errors
773	///
774	/// If the packet header cannot be parsed.
775	pub fn parse_opaque(mut body: Bytes) -> Result<SataResponse<Bytes>, NetworkParseError> {
776		if body.len() < 0x20 {
777			return Err(NetworkParseError::NotEnoughData(
778				"SataResponse",
779				0x20,
780				body.len(),
781				body,
782			));
783		}
784
785		let header = SataPacketHeader::try_from(body.split_to(0x20))?;
786
787		Ok(SataResponse::from_existing(header, body))
788	}
789}
790
791impl<ErrorTy: Into<NetworkError>, InnerTy: Debug> TryFrom<Bytes> for SataResponse<InnerTy>
792where
793	InnerTy: TryFrom<Bytes, Error = ErrorTy>,
794{
795	type Error = NetworkError;
796
797	fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
798		if value.len() < 0x20 {
799			return Err(
800				NetworkParseError::NotEnoughData("SataResponse", 0x20, value.len(), value).into(),
801			);
802		}
803
804		let real_body = value.split_off(0x20_usize);
805		let header = SataPacketHeader::try_from(value)?;
806		let flags = header.flags();
807		let pid = header.host_pid();
808		let body = InnerTy::try_from(real_body).map_err(Into::into)?;
809
810		Ok(Self {
811			header,
812			flags,
813			pid,
814			body,
815			force_zero_version: false,
816		})
817	}
818}
819
820impl<InnerTy: Debug> TryFrom<SataResponse<InnerTy>> for Bytes
821where
822	InnerTy: Into<Bytes>,
823{
824	type Error = PcfsApiError;
825
826	fn try_from(value: SataResponse<InnerTy>) -> Result<Self, Self::Error> {
827		let body_as_bytes = value.body.into();
828		let mut new_buff = BytesMut::with_capacity(0x20 + body_as_bytes.len());
829
830		new_buff.put_u32(
831			u32::try_from(body_as_bytes.len())
832				.map_err(|_| PcfsApiError::PacketTooLargeForSata(body_as_bytes.len()))?,
833		);
834		new_buff.put_u32(value.header.id());
835		new_buff.put_u32(value.flags);
836		if value.force_zero_version {
837			new_buff.put_u32(0);
838		} else {
839			new_buff.put_u32(if value.header.version() != 0 {
840				value.header.version()
841			} else {
842				DEFAULT_PCFS_VERSION
843			});
844		}
845		// Calculate epoch, and wrap around...
846		new_buff.put_u32(
847			u32::try_from(
848				SystemTime::now()
849					.duration_since(SystemTime::UNIX_EPOCH)
850					.unwrap_or(Duration::from_secs(0))
851					.as_secs()
852					.rem_euclid(u64::from(u32::MAX)),
853			)
854			.unwrap_or(u32::MAX),
855		);
856		// Put our PID, and appropriate padding.
857		new_buff.put_u32(value.pid);
858		new_buff.extend([0; 8]);
859		// Now we can finally add our body!
860		new_buff.extend(body_as_bytes);
861
862		Ok(new_buff.freeze())
863	}
864}
865
866impl<InnerTy: Debug> IntoResponse for SataResponse<InnerTy>
867where
868	InnerTy: Into<Bytes>,
869{
870	fn to_response(self) -> Result<Response, CatBridgeError> {
871		Ok(Response::new_with_body(self.try_into()?))
872	}
873}
874
875const SATA_RESPONSE_FIELDS: &[NamedField<'static>] = &[
876	NamedField::new("header"),
877	NamedField::new("pid"),
878	NamedField::new("flags"),
879	NamedField::new("body"),
880];
881
882impl<InnerTy: Debug> Structable for SataResponse<InnerTy> {
883	fn definition(&self) -> StructDef<'_> {
884		StructDef::new_static("SataResponse", Fields::Named(SATA_RESPONSE_FIELDS))
885	}
886}
887
888impl<InnerTy: Debug> Valuable for SataResponse<InnerTy> {
889	fn as_value(&self) -> Value<'_> {
890		Value::Structable(self)
891	}
892
893	fn visit(&self, visitor: &mut dyn Visit) {
894		visitor.visit_named_fields(&NamedValues::new(
895			SATA_RESPONSE_FIELDS,
896			&[
897				Valuable::as_value(&self.header),
898				Valuable::as_value(&self.pid),
899				Valuable::as_value(&self.flags),
900				Valuable::as_value(&format!("{:?}", self.body)),
901			],
902		));
903	}
904}
905
906/// A result code that is serializable, and abstracts away just returning a
907/// single result code.
908#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Valuable)]
909#[repr(transparent)]
910pub struct SataResultCode(pub u32);
911
912impl SataResultCode {
913	#[must_use]
914	pub const fn success() -> Self {
915		Self(0)
916	}
917
918	#[must_use]
919	pub const fn error(code: u32) -> Self {
920		Self(code)
921	}
922}
923
924impl From<&SataResultCode> for Bytes {
925	fn from(value: &SataResultCode) -> Self {
926		let mut buff = BytesMut::with_capacity(4);
927		buff.put_u32(value.0);
928		buff.freeze()
929	}
930}
931
932impl From<SataResultCode> for Bytes {
933	fn from(value: SataResultCode) -> Self {
934		Self::from(&value)
935	}
936}
937
938impl TryFrom<Bytes> for SataResultCode {
939	type Error = NetworkParseError;
940
941	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
942		if value.len() < 0x4 {
943			return Err(NetworkParseError::FieldNotLongEnough(
944				"SataPacket",
945				"ResultCode",
946				0x4,
947				value.len(),
948				value,
949			));
950		}
951		if value.len() > 0x4 {
952			return Err(NetworkParseError::UnexpectedTrailer(
953				"SataResultCode",
954				value.slice(0x4..),
955			));
956		}
957
958		let rc = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
959
960		Ok(Self(rc))
961	}
962}
963
964impl Display for SataResultCode {
965	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
966		if self.0 == 0 {
967			write!(fmt, "Success")
968		} else {
969			write!(fmt, "Failure ({:02x})", self.0)
970		}
971	}
972}
973
974/// A file descriptor from a sata endpoint that optionally includes a single
975/// result code in back.
976#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
977pub struct SataFileDescriptorResult {
978	/// The file descriptor or error code...
979	///
980	/// `Ok()` is the file descriptor, which implies success.
981	/// `Err()` is an error code, which sets a file descriptor as an error.
982	file_descriptor: Result<i32, u32>,
983}
984
985impl SataFileDescriptorResult {
986	/// A successful file descriptor, with a 0 error code.
987	#[must_use]
988	pub const fn success(fd: i32) -> Self {
989		Self {
990			file_descriptor: Ok(fd),
991		}
992	}
993
994	/// A file descriptor result that returned an error code.
995	#[must_use]
996	pub const fn error(error_code: u32) -> Self {
997		Self {
998			file_descriptor: Err(error_code),
999		}
1000	}
1001
1002	/// The resulting file descriptor.
1003	///
1004	/// ## Errors
1005	///
1006	/// If there was an error code.
1007	pub const fn result(&self) -> Result<i32, u32> {
1008		self.file_descriptor
1009	}
1010}
1011
1012impl From<&SataFileDescriptorResult> for Bytes {
1013	fn from(value: &SataFileDescriptorResult) -> Self {
1014		let mut response = BytesMut::with_capacity(8);
1015
1016		match value.file_descriptor {
1017			Ok(fd) => {
1018				response.put_u32(0);
1019				response.put_i32(fd);
1020			}
1021			Err(code) => {
1022				response.put_u32(code);
1023				response.put_i32(i32::from_be_bytes([0xFF, 0xFF, 0xFF, 0xFF]));
1024			}
1025		}
1026
1027		response.freeze()
1028	}
1029}
1030
1031impl From<SataFileDescriptorResult> for Bytes {
1032	fn from(value: SataFileDescriptorResult) -> Self {
1033		Self::from(&value)
1034	}
1035}
1036
1037impl TryFrom<Bytes> for SataFileDescriptorResult {
1038	type Error = NetworkParseError;
1039
1040	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
1041		if value.len() < 0x8 {
1042			return Err(NetworkParseError::FieldNotLongEnough(
1043				"SataPacket",
1044				"FileDescriptorResult",
1045				0x8,
1046				value.len(),
1047				value,
1048			));
1049		}
1050		if value.len() > 0x8 {
1051			return Err(NetworkParseError::UnexpectedTrailer(
1052				"SataFileDescriptorResult",
1053				value.slice(0x8..),
1054			));
1055		}
1056
1057		let rc = u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
1058		let fd = i32::from_be_bytes([value[4], value[5], value[6], value[7]]);
1059
1060		if rc == 0 {
1061			Ok(Self::success(fd))
1062		} else {
1063			Ok(Self::error(rc))
1064		}
1065	}
1066}
1067
1068impl Display for SataFileDescriptorResult {
1069	fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
1070		match self.file_descriptor {
1071			Ok(fd) => write!(fmt, "Success ({fd})"),
1072			Err(rc) => write!(fmt, "Failure ({rc:02x})"),
1073		}
1074	}
1075}
1076
1077#[cfg(test)]
1078mod unit_tests {
1079	use super::*;
1080
1081	#[test]
1082	pub fn move_to_file_location_conversions() {
1083		for mtfl in vec![
1084			MoveToFileLocation::Begin,
1085			MoveToFileLocation::Current,
1086			MoveToFileLocation::End,
1087		] {
1088			assert_eq!(
1089				mtfl,
1090				MoveToFileLocation::try_from(u32::from(mtfl))
1091					.expect("MTFL turned into u32 could not be parsed"),
1092				"MoveToFileLocation wasn't the same after being converted back n forth!",
1093			);
1094		}
1095	}
1096
1097	fn parse_sata_request<
1098		ErrorTy: Into<NetworkParseError>,
1099		BodyTy: Debug + TryFrom<Bytes, Error = ErrorTy>,
1100	>(
1101		body: Bytes,
1102	) -> (SataPacketHeader, SataCommandInfo, BodyTy) {
1103		SataRequest::try_from(body)
1104			.expect("Failed to parse sata request!")
1105			.into_parts()
1106	}
1107
1108	#[test]
1109	pub fn decode_real_ping_packet() {
1110		let (header, command_info, _body) =
1111			parse_sata_request::<_, SataPingPacketBody>(Bytes::from(vec![
1112				0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00,
1113				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1114				0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x40, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
1115				0x51, 0xAD, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14,
1116			]));
1117
1118		assert_eq!(header.data_len(), 0x14);
1119		assert_eq!(header.id(), 0);
1120		assert_eq!(
1121			header.flags(),
1122			(SataCapabilitiesFlags::FAST_FILE_IO_SUPPORTED
1123				| SataCapabilitiesFlags::COMBINED_SEND_RECV_SUPPORTED)
1124				.0
1125		);
1126		assert_eq!(header.host_pid(), 0);
1127		assert_eq!(header.raw_timestamp_on_host(), 0);
1128		// This ping packet was the first packet, so no version was set yet.
1129		assert_eq!(header.version(), 0);
1130		assert_eq!(command_info.command(), 0x14);
1131	}
1132
1133	#[test]
1134	pub fn decode_real_query_info_packet() {
1135		let (_header, command_info, body) =
1136			parse_sata_request::<_, SataGetInfoByQueryPacketBody>(Bytes::from(vec![
1137				0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x02, 0x00,
1138				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1139				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1140				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x2f, 0x25, 0x53, 0x4c,
1141				0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79, 0x73, 0x00,
1142				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1143				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1144				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1145				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1146				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1147				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1148				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1149				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1150				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1151				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1152				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1153				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1154				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1155				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1156				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1157				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1158				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1159				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1160				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1161				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1162				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1163				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1164				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1165				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1166				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1167				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1168				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1169				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1170				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1171				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1172				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1173				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1174				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1175				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1176				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1177				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05,
1178			]));
1179
1180		assert_eq!(command_info.command(), 0x10);
1181		assert_eq!(body.query_type(), SataQueryType::FileDetails);
1182		assert_eq!(body.path(), "/%SLC_EMU_DIR/sys");
1183	}
1184
1185	#[test]
1186	pub fn decode_real_change_mode_packet() {
1187		let (_header, command_info, body) =
1188			parse_sata_request::<_, SataChangeModePacketBody>(Bytes::from(vec![
1189				0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1190				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1191				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1192				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x2f, 0x25, 0x53, 0x4c,
1193				0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79, 0x73, 0x2f,
1194				0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1195				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1196				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1197				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1198				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1199				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1200				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1201				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1202				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1203				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1204				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1205				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1206				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1207				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1208				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1209				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1210				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1211				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1212				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1213				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1214				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1215				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1216				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1217				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1218				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1219				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1220				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1221				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1222				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1223				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1224				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1225				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1226				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1227				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1228				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1229				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1230			]));
1231
1232		assert_eq!(command_info.command(), 0x13);
1233		assert_eq!(body.path(), "/%SLC_EMU_DIR/sys/config");
1234		assert!(!body.will_set_write_mode());
1235	}
1236
1237	#[test]
1238	pub fn decode_open_file_packet() {
1239		let (_header, command_info, body) =
1240			parse_sata_request::<_, SataOpenFilePacketBody>(Bytes::from(vec![
1241				0x00, 0x00, 0x02, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1242				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1243				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1244				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x72, 0x00, 0x53, 0x4c,
1245				0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79, 0x2f, 0x25,
1246				0x53, 0x4c, 0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x73, 0x79,
1247				0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65,
1248				0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1249				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1250				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1251				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1252				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1253				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1254				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1255				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1256				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1257				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1258				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1259				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1260				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1261				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1262				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1263				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1264				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1265				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1266				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1267				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1268				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1269				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1270				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1271				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1272				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1273				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1274				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1275				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1276				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1277				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1278				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1279				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1280				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1281				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1282				0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1283			]));
1284
1285		assert_eq!(command_info.command(), 0x5);
1286		assert_eq!(body.mode(), "r");
1287		assert_eq!(body.path(), "/%SLC_EMU_DIR/sys/config/system.xml");
1288	}
1289
1290	#[test]
1291	pub fn decode_read_file_packet() {
1292		let (_header, command_info, body) =
1293			parse_sata_request::<_, SataReadFilePacketBody>(Bytes::from(vec![
1294				0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1295				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1296				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1297				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01,
1298				0x00, 0x00, 0x05, 0x1b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1299				0x00, 0x00,
1300			]));
1301
1302		assert_eq!(command_info.command(), 0x6);
1303		assert_eq!(body.block_count(), 1);
1304		assert_eq!(body.block_size(), 0x51B);
1305		assert_eq!(body.file_descriptor(), 2);
1306		assert_eq!(body.move_to_pointer(), MoveToFileLocation::Begin);
1307		assert_eq!(body.should_move(), false);
1308	}
1309
1310	#[test]
1311	pub fn decode_close_file_packet() {
1312		let (_header, command_info, body) =
1313			parse_sata_request::<_, SataCloseFilePacketBody>(Bytes::from(vec![
1314				0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1315				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1316				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1317				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x02,
1318			]));
1319
1320		assert_eq!(command_info.command(), 0xD);
1321		assert_eq!(body.file_descriptor(), 2);
1322	}
1323
1324	#[test]
1325	pub fn decode_open_folder_packet() {
1326		let (_header, command_info, body) =
1327			parse_sata_request::<_, SataOpenFolderPacketBody>(Bytes::from(vec![
1328				0x00, 0x00, 0x02, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1329				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1330				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1331				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f, 0x25, 0x4d, 0x4c,
1332				0x43, 0x5f, 0x45, 0x4d, 0x55, 0x5f, 0x44, 0x49, 0x52, 0x2f, 0x75, 0x73, 0x72, 0x2f,
1333				0x74, 0x6d, 0x70, 0x00, 0x70, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x52, 0x2f, 0x73, 0x79,
1334				0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x79, 0x73, 0x74, 0x65,
1335				0x6d, 0x2e, 0x78, 0x6d, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1336				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1337				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1338				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1339				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1340				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1341				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1342				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1343				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1344				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1345				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1346				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1347				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1348				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1349				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1350				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1351				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1352				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1353				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1354				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1355				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1356				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1357				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1358				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1359				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1360				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1361				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1362				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1363				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1364				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1365				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1366				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1367				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1368				0x00, 0x00, 0x00, 0x00,
1369			]));
1370
1371		assert_eq!(command_info.command(), 0x1);
1372		assert_eq!(body.path(), "/%MLC_EMU_DIR/usr/tmp");
1373	}
1374
1375	#[test]
1376	pub fn decode_read_folder_packet() {
1377		let (_header, command_info, body) =
1378			parse_sata_request::<_, SataReadFolderPacketBody>(Bytes::from(vec![
1379				0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1380				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1381				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1382				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
1383			]));
1384
1385		assert_eq!(command_info.command(), 0x2);
1386		assert_eq!(body.file_descriptor(), 1);
1387	}
1388
1389	#[test]
1390	pub fn decode_close_folder_packet() {
1391		let (_header, command_info, body) =
1392			parse_sata_request::<_, SataCloseFolderPacketBody>(Bytes::from(vec![
1393				0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
1394				0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1395				0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
1396				0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01,
1397			]));
1398
1399		assert_eq!(command_info.command(), 0x04);
1400		assert_eq!(body.file_descriptor(), 1);
1401	}
1402}