Skip to main content

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