1mod 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
46pub const DEFAULT_PCFS_VERSION: u32 = 0x0200_0600;
48
49#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct SataPacketHeader {
52 packet_data_len: u32,
54 packet_id: u32,
59 flags: u32,
63 version: u32,
68 timestamp_on_host: u32,
73 pid_on_host: u32,
75 }
77
78impl SataPacketHeader {
79 #[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 #[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 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 user: (u32, u32),
273 capabilities: (u32, u32),
275 command: u32,
277}
278
279impl SataCommandInfo {
280 #[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 #[must_use]
292 pub const fn user(&self) -> (u32, u32) {
293 self.user
294 }
295
296 pub const fn set_user(&mut self, new: (u32, u32)) {
298 self.user = new;
299 }
300
301 #[must_use]
307 pub const fn capabilities(&self) -> (u32, u32) {
308 self.capabilities
309 }
310
311 pub const fn set_capabilities(&mut self, new: (u32, u32)) {
313 self.capabilities = new;
314 }
315
316 #[must_use]
318 pub const fn command(&self) -> u32 {
319 self.command
320 }
321
322 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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Valuable)]
438pub enum MoveToFileLocation {
439 Begin,
441 Current,
443 End,
445}
446
447impl MoveToFileLocation {
448 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 }
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#[derive(Clone, Debug)]
528pub struct SataRequest<InnerTy: Debug> {
529 header: SataPacketHeader,
531 command_info: SataCommandInfo,
533 body: InnerTy,
535}
536
537impl<InnerTy: Debug> SataRequest<InnerTy> {
538 #[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 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#[derive(Debug)]
680pub struct SataResponse<InnerTy: Debug> {
681 header: SataPacketHeader,
683 flags: u32,
685 pid: u32,
687 force_zero_version: bool,
689 body: InnerTy,
691}
692
693impl<InnerTy: Debug> SataResponse<InnerTy> {
694 #[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 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 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 new_buff.put_u32(value.pid);
858 new_buff.extend([0; 8]);
859 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#[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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
977pub struct SataFileDescriptorResult {
978 file_descriptor: Result<i32, u32>,
983}
984
985impl SataFileDescriptorResult {
986 #[must_use]
988 pub const fn success(fd: i32) -> Self {
989 Self {
990 file_descriptor: Ok(fd),
991 }
992 }
993
994 #[must_use]
996 pub const fn error(error_code: u32) -> Self {
997 Self {
998 file_descriptor: Err(error_code),
999 }
1000 }
1001
1002 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 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}