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

1//! Definitions for the `CreateDir` packet type, and it's response types.
2//!
3//! This creates folders on disk, and nothing more.
4
5use crate::{errors::NetworkParseError, fsemul::pcfs::errors::PcfsApiError};
6use bytes::{BufMut, Bytes, BytesMut};
7use std::ffi::CStr;
8use valuable::{Fields, NamedField, NamedValues, StructDef, Structable, Valuable, Value, Visit};
9
10/// A packet to create a new directory.
11///
12/// This will create a directory if it does not exist.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct SataCreateFolderPacketBody {
15	/// The path to create, note that this is not the 'resolved' path which is
16	/// the path to actual read from.
17	///
18	/// Interpolation has a few known ways of being replaced:
19	///
20	/// - `%MLC_EMU_DIR`: `<cafe_sdk>/data/mlc/`
21	/// - `%SLC_EMU_DIR`: `<cafe_sdk>/data/slc/`
22	/// - `%DISC_EMU_DIR`: `<cafe_sdk>/data/disc/`
23	/// - `%SAVE_EMU_DIR`: `<cafe_sdk>/data/save/`
24	/// - `%NETWORK`: <mounted network share path>
25	path: String,
26	/// If we're setting the write mode.
27	set_write_mode: bool,
28}
29
30impl SataCreateFolderPacketBody {
31	/// Attempt to construct a new create directory packet.
32	///
33	/// ## Errors
34	///
35	/// If the path is longer than 511 bytes. Normally the max path is 512 bytes,
36	/// but because we need to encode our data as a C-String with a NUL
37	/// terminator we cannot be longer than 511 bytes.
38	///
39	/// Consider using relative/mapped paths if possible when dealing with long
40	/// paths.
41	pub fn new(path: String, set_write_mode: bool) -> Result<Self, PcfsApiError> {
42		if path.len() > 511 {
43			return Err(PcfsApiError::PathTooLong(path));
44		}
45
46		Ok(Self {
47			path,
48			set_write_mode,
49		})
50	}
51
52	#[must_use]
53	pub fn path(&self) -> &str {
54		self.path.as_str()
55	}
56
57	/// Update the path to send in this particular create directory packet.
58	///
59	/// ## Errors
60	///
61	/// If the path is longer than 511 bytes. Normally the max path is 512 bytes,
62	/// but because we need to encode our data as a C-String with a NUL
63	/// terminator we cannot be longer than 511 bytes.
64	///
65	/// Consider using relative/mapped paths if possible when dealing with long
66	/// paths.
67	pub fn set_path(&mut self, new_path: String) -> Result<(), PcfsApiError> {
68		if new_path.len() > 511 {
69			return Err(PcfsApiError::PathTooLong(new_path));
70		}
71
72		self.path = new_path;
73		Ok(())
74	}
75
76	#[must_use]
77	pub const fn will_set_write_mode(&self) -> bool {
78		self.set_write_mode
79	}
80
81	/// Update the `set_write_mode` flag, which determines if we'll set the file
82	/// as writable or not.
83	pub const fn set_write_mode(&mut self, will_set: bool) {
84		self.set_write_mode = will_set;
85	}
86}
87
88impl TryFrom<Bytes> for SataCreateFolderPacketBody {
89	type Error = NetworkParseError;
90
91	fn try_from(value: Bytes) -> Result<Self, Self::Error> {
92		if value.len() < 0x204 {
93			return Err(NetworkParseError::FieldNotLongEnough(
94				"SataCreateDirectory",
95				"Body",
96				0x204,
97				value.len(),
98				value,
99			));
100		}
101		if value.len() > 0x204 {
102			return Err(NetworkParseError::UnexpectedTrailer(
103				"SataCreateDirectory",
104				value.slice(0x204..),
105			));
106		}
107
108		let path_c_str =
109			CStr::from_bytes_until_nul(&value[..0x200]).map_err(NetworkParseError::BadCString)?;
110		let mode = u32::from_be_bytes([value[0x200], value[0x201], value[0x202], value[0x203]]);
111
112		Ok(Self {
113			path: path_c_str.to_str()?.to_owned(),
114			set_write_mode: mode & 0x222 != 0,
115		})
116	}
117}
118
119impl From<&SataCreateFolderPacketBody> for Bytes {
120	fn from(value: &SataCreateFolderPacketBody) -> Self {
121		let mut result = BytesMut::with_capacity(0x204);
122		result.extend_from_slice(value.path.as_bytes());
123		// These are C Strings so we need a NUL terminator.
124		// Pad with `0`, til we get a full path with a nul terminator.
125		result.extend(BytesMut::zeroed(0x200 - result.len()));
126		result.put_u32(if value.set_write_mode { 0x666 } else { 0x444 });
127		result.freeze()
128	}
129}
130
131impl From<SataCreateFolderPacketBody> for Bytes {
132	fn from(value: SataCreateFolderPacketBody) -> Self {
133		Self::from(&value)
134	}
135}
136
137const SATA_CREATE_FOLDER_PACKET_BODY_FIELDS: &[NamedField<'static>] = &[NamedField::new("path")];
138
139impl Structable for SataCreateFolderPacketBody {
140	fn definition(&self) -> StructDef<'_> {
141		StructDef::new_static(
142			"SataCreateDirectoryPacketBody",
143			Fields::Named(SATA_CREATE_FOLDER_PACKET_BODY_FIELDS),
144		)
145	}
146}
147
148impl Valuable for SataCreateFolderPacketBody {
149	fn as_value(&self) -> Value<'_> {
150		Value::Structable(self)
151	}
152
153	fn visit(&self, visitor: &mut dyn Visit) {
154		visitor.visit_named_fields(&NamedValues::new(
155			SATA_CREATE_FOLDER_PACKET_BODY_FIELDS,
156			&[Valuable::as_value(&self.path)],
157		));
158	}
159}