summaryrefslogtreecommitdiff
path: root/src/devices/file_device/directory_listing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/file_device/directory_listing.rs')
-rw-r--r--src/devices/file_device/directory_listing.rs115
1 files changed, 115 insertions, 0 deletions
diff --git a/src/devices/file_device/directory_listing.rs b/src/devices/file_device/directory_listing.rs
new file mode 100644
index 0000000..1d7ddd2
--- /dev/null
+++ b/src/devices/file_device/directory_listing.rs
@@ -0,0 +1,115 @@
+use super::*;
+
+
+pub struct DirectoryListing {
+ children: Vec<BedrockFilePath>,
+ length: u32,
+ selected: Option<u32>,
+ child_path_buffer: BedrockPathBuffer,
+}
+
+
+impl DirectoryListing {
+ pub fn from_path(path: &BedrockFilePath) -> Option<Self> {
+ macro_rules! unres {
+ ($result:expr) => { match $result { Ok(v) => v, Err(_) => continue} };
+ }
+ macro_rules! unopt {
+ ($option:expr) => { match $option { Some(v) => v, None => continue} };
+ }
+
+ #[cfg(target_family = "windows")] {
+ if path.as_path().components().count() == 0 {
+ return Some(Self::construct_virtual_root())
+ }
+ }
+
+ let mut children = Vec::new();
+ if let Ok(dir_listing) = std::fs::read_dir(path.as_path()) {
+ for (i, entry_result) in dir_listing.enumerate() {
+ // Firebreak to prevent emulator from consuming an absurd amount
+ // of memory when opening too large of a directory.
+ if i == (u16::MAX as usize) {
+ break;
+ }
+
+ let entry = unres!(entry_result);
+ let entry_path = unopt!(BedrockFilePath::from_path(&entry.path(), path.base()));
+ if entry_path.is_hidden() {
+ continue;
+ }
+
+ children.push(entry_path);
+ }
+ }
+
+ children.sort();
+ let length = u32::try_from(children.len()).ok()?;
+ let selected = None;
+ let child_path_buffer = BedrockPathBuffer::new();
+ Some( Self { children, length, selected, child_path_buffer } )
+ }
+
+ /// Generate entries for a virtual root directory.
+ #[cfg(target_family = "windows")]
+ fn construct_virtual_root() -> Self {
+ let mut children = Vec::new();
+ let base = PathBuf::from("");
+ let drive_bits = unsafe {
+ windows::Win32::Storage::FileSystem::GetLogicalDrives()
+ };
+ for i in 0..26 {
+ if drive_bits & (0x1 << i) != 0 {
+ let letter: char = (b'A' + i).into();
+ let path = PathBuf::from(format!("{letter}:/"));
+ if let Some(drive) = BedrockFilePath::from_path(&path, &base) {
+ children.push(drive);
+ }
+ }
+ }
+
+ let length = children.len() as u32;
+ let selected = None;
+ let child_path_buffer = BedrockPathBuffer::new();
+ Self { children, length, selected, child_path_buffer }
+ }
+
+ /// Attempts to return a directory child by index.
+ pub fn get(&self, index: u32) -> Option<&BedrockFilePath> {
+ self.children.get(index as usize)
+ }
+
+ pub fn length(&self) -> u32 {
+ self.length
+ }
+
+ /// Returns the index of the selected child, or zero if no child is selected.
+ pub fn selected(&self) -> u32 {
+ self.selected.unwrap_or(0)
+ }
+
+ /// Attempts to select a child by index.
+ pub fn set_selected(&mut self, index: u32) {
+ if let Some(child) = self.get(index) {
+ let buffer = child.as_buffer();
+ self.child_path_buffer.populate(buffer);
+ self.selected = Some(index);
+ } else {
+ self.child_path_buffer.clear();
+ self.selected = None;
+ }
+ }
+
+ pub fn child_path_buffer(&mut self) -> &mut BedrockPathBuffer {
+ &mut self.child_path_buffer
+ }
+
+ pub fn child_type(&self) -> Option<EntryType> {
+ self.selected.and_then(|s| self.get(s).and_then(|i| i.entry_type()))
+ }
+
+ pub fn child_path(&self) -> Option<BedrockFilePath> {
+ self.selected.and_then(|s| self.get(s).and_then(|i| Some(i.clone())))
+ }
+}
+