1mod 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
48pub const DEFAULT_PCFS_VERSION: u32 = 0x0200_0600;
50
51#[derive(Clone, Debug, PartialEq, Eq)]
53pub struct SataPacketHeader {
54 packet_data_len: u32,
56 packet_id: u32,
61 flags: u32,
65 version: u32,
70 timestamp_on_host: u32,
75 pid_on_host: u32,
77 }
79
80impl SataPacketHeader {
81 #[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 #[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 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 user: (u32, u32),
275 capabilities: (u32, u32),
277 command: u32,
279}
280
281impl SataCommandInfo {
282 #[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 #[must_use]
294 pub const fn user(&self) -> (u32, u32) {
295 self.user
296 }
297
298 pub const fn set_user(&mut self, new: (u32, u32)) {
300 self.user = new;
301 }
302
303 #[must_use]
309 pub const fn capabilities(&self) -> (u32, u32) {
310 self.capabilities
311 }
312
313 pub const fn set_capabilities(&mut self, new: (u32, u32)) {
315 self.capabilities = new;
316 }
317
318 #[must_use]
320 pub const fn command(&self) -> u32 {
321 self.command
322 }
323
324 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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Valuable)]
440pub enum MoveToFileLocation {
441 Begin,
443 Current,
445 End,
447 Offset(u32),
449}
450
451impl MoveToFileLocation {
452 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 }
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#[derive(Clone, Debug)]
539pub struct SataRequest<InnerTy: Debug> {
540 header: SataPacketHeader,
542 command_info: SataCommandInfo,
544 body: InnerTy,
546}
547
548impl<InnerTy: Debug> SataRequest<InnerTy> {
549 #[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 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#[derive(Debug)]
691pub struct SataResponse<InnerTy: Debug> {
692 header: SataPacketHeader,
694 flags: u32,
696 pid: u32,
698 force_zero_version: bool,
700 body: InnerTy,
702}
703
704impl<InnerTy: Debug> SataResponse<InnerTy> {
705 #[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 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 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 new_buff.put_u32(value.pid);
869 new_buff.extend([0; 8]);
870 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#[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#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Valuable)]
988pub struct SataFileDescriptorResult {
989 file_descriptor: Result<i32, u32>,
994}
995
996impl SataFileDescriptorResult {
997 #[must_use]
999 pub const fn success(fd: i32) -> Self {
1000 Self {
1001 file_descriptor: Ok(fd),
1002 }
1003 }
1004
1005 #[must_use]
1007 pub const fn error(error_code: u32) -> Self {
1008 Self {
1009 file_descriptor: Err(error_code),
1010 }
1011 }
1012
1013 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 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}