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

1//! Definitions for the `ReadDirectory` packet type, and it's response types.
2//!
3//! This will return the file information for the next file present within a
4//! directory. This does not recurse.
5
6use crate::{
7	errors::NetworkParseError,
8	fsemul::pcfs::{errors::PcfsApiError, sata::proto::get_info_by_query::SataFDInfo},
9};
10use bytes::{Buf, BufMut, Bytes, BytesMut};
11use std::ffi::CStr;
12use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
13
14/// No more items in this directory! sorry!
15pub const NO_MORE_ITEMS: u32 = 0xFFF0_FFFC;
16
17/// A packet to get information about another file within a directory.
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct SataReadFolderPacketBody {
20	file_descriptor: i32,
21}
22
23impl SataReadFolderPacketBody {
24	/// Create a new read directory packet body.
25	#[must_use]
26	pub const fn new(file_descriptor: i32) -> Self {
27		Self { file_descriptor }
28	}
29
30	#[must_use]
31	pub const fn file_descriptor(&self) -> i32 {
32		self.file_descriptor
33	}
34
35	pub const fn set_file_descriptor(&mut self) -> i32 {
36		self.file_descriptor
37	}
38}
39
40impl From<&SataReadFolderPacketBody> for Bytes {
41	fn from(value: &SataReadFolderPacketBody) -> Self {
42		let mut buff = BytesMut::with_capacity(4);
43		buff.put_i32(value.file_descriptor);
44		buff.freeze()
45	}
46}
47
48impl From<SataReadFolderPacketBody> for Bytes {
49	fn from(value: SataReadFolderPacketBody) -> Self {
50		Self::from(&value)
51	}
52}
53
54impl TryFrom<Bytes> for SataReadFolderPacketBody {
55	type Error = NetworkParseError;
56
57	fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
58		if value.len() < 0x4 {
59			return Err(NetworkParseError::FieldNotLongEnough(
60				"SataReadDir",
61				"Body",
62				0x4,
63				value.len(),
64				value,
65			));
66		}
67		if value.len() > 0x4 {
68			return Err(NetworkParseError::UnexpectedTrailer(
69				"SataReadDir",
70				value.slice(0x4..),
71			));
72		}
73
74		let fd = value.get_i32();
75
76		Ok(Self {
77			file_descriptor: fd,
78		})
79	}
80}
81
82const SATA_READ_FOLDER_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("fd")];
83
84impl Structable for SataReadFolderPacketBody {
85	fn definition(&self) -> StructDef<'_> {
86		StructDef::new_static(
87			"SataReadDirPacketBody",
88			Fields::Named(SATA_READ_FOLDER_PACKET_BODY_FIELDS),
89		)
90	}
91}
92
93impl Valuable for SataReadFolderPacketBody {
94	fn as_value(&self) -> Value<'_> {
95		Value::Structable(self)
96	}
97
98	fn visit(&self, visitor: &mut dyn Visit) {
99		visitor.visit_named_fields(&NamedValues::new(
100			SATA_READ_FOLDER_PACKET_BODY_FIELDS,
101			&[Valuable::as_value(&self.file_descriptor)],
102		));
103	}
104}
105
106#[derive(Clone, Debug, PartialEq, Eq, Valuable)]
107pub struct DirectoryItemResponse {
108	/// The underlying return code for this directory item.
109	return_code: u32,
110	/// The next bit of file information, if there are any items left in the
111	/// directory.
112	next_file_info: Option<(SataFDInfo, String)>,
113}
114
115impl DirectoryItemResponse {
116	/// Create a Directory Item.
117	///
118	/// This assumes there was another item in the directory and we want to
119	/// return it.
120	///
121	/// ## Errors
122	///
123	/// If the path is longer than 255 bytes.
124	pub fn new_next(info: SataFDInfo, path: String) -> Result<Self, PcfsApiError> {
125		if path.len() > 255 {
126			return Err(PcfsApiError::PathTooLong(path));
127		}
128
129		Ok(Self {
130			return_code: 0,
131			next_file_info: Some((info, path)),
132		})
133	}
134
135	/// Return that there's nothing left in the directory.
136	#[must_use]
137	pub const fn new_nothing_left() -> Self {
138		Self {
139			return_code: NO_MORE_ITEMS,
140			next_file_info: None,
141		}
142	}
143
144	/// Create a new directory item response given an error code.
145	#[must_use]
146	pub const fn new_error_code(rc: u32) -> Self {
147		Self {
148			return_code: rc,
149			next_file_info: None,
150		}
151	}
152
153	/// Get the underlying return code for this directory item.
154	#[must_use]
155	pub const fn return_code(&self) -> u32 {
156		self.return_code
157	}
158
159	/// Return if this directory item was 'successfully' fetched.
160	///
161	/// This is really only useful when performing manual construction of the
162	/// read directory packet. As when deserializing we will always return a
163	/// proper error code if it's not successful.
164	#[must_use]
165	pub fn is_successful(&self) -> bool {
166		[NO_MORE_ITEMS, 0].contains(&self.return_code)
167	}
168
169	/// Get the file that was returned as being next in the directory.
170	#[must_use]
171	pub const fn file_info(&self) -> Option<&(SataFDInfo, String)> {
172		self.next_file_info.as_ref()
173	}
174
175	/// Consume the underlying packet, and just get the next file info if there
176	/// is any.
177	#[must_use]
178	pub fn take_file_info(self) -> Option<(SataFDInfo, String)> {
179		self.next_file_info
180	}
181}
182
183impl From<&DirectoryItemResponse> for Bytes {
184	fn from(value: &DirectoryItemResponse) -> Self {
185		if let Some(nfi) = value.file_info() {
186			let mut buff = BytesMut::with_capacity(0x158);
187			buff.put_u32(value.return_code());
188			buff.extend(Bytes::from(&nfi.0));
189			let path_bytes = nfi.1.as_bytes();
190			buff.extend(Bytes::from(Vec::from(path_bytes)));
191			buff.extend(BytesMut::zeroed(256 - path_bytes.len()));
192			buff.freeze()
193		} else {
194			let mut bytes = BytesMut::with_capacity(0x158);
195			bytes.put_u32(value.return_code());
196			bytes.extend([0; 0x154]);
197			bytes.freeze()
198		}
199	}
200}
201
202impl From<DirectoryItemResponse> for Bytes {
203	fn from(value: DirectoryItemResponse) -> Self {
204		Self::from(&value)
205	}
206}
207
208impl TryFrom<Bytes> for DirectoryItemResponse {
209	type Error = NetworkParseError;
210
211	fn try_from(mut value: Bytes) -> Result<Self, Self::Error> {
212		if value.len() < 4 {
213			return Err(NetworkParseError::NotEnoughData(
214				"DirectoryItemResponse",
215				4,
216				value.len(),
217				value,
218			));
219		}
220
221		// Okay we've at least got an rc....
222		let rc = value.get_u32();
223		if rc != 0 && rc != NO_MORE_ITEMS {
224			return Err(NetworkParseError::ErrorCode(rc));
225		}
226		if rc == NO_MORE_ITEMS {
227			return Ok(DirectoryItemResponse::new_nothing_left());
228		}
229
230		// rc is now guaranteed to be 0... We should expect a full packet.
231		if value.len() < 0x154 {
232			return Err(NetworkParseError::NotEnoughData(
233				"DirectoryItemResponse",
234				0x154,
235				value.len(),
236				value,
237			));
238		}
239		if value.len() > 0x154 {
240			return Err(NetworkParseError::UnexpectedTrailer(
241				"DirectoryItemResponse",
242				value.slice(0x154..),
243			));
244		}
245
246		let fd_info = SataFDInfo::try_from(value.slice(..84))?;
247		let path_bytes = value.slice(84..);
248		let path =
249			CStr::from_bytes_until_nul(&path_bytes).map_err(NetworkParseError::BadCString)?;
250
251		Ok(Self {
252			return_code: rc,
253			next_file_info: Some((fd_info, path.to_str()?.to_owned())),
254		})
255	}
256}