diff options
Diffstat (limited to 'src/devices/file_device/directory_listing.rs')
-rw-r--r-- | src/devices/file_device/directory_listing.rs | 115 |
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()))) + } +} + |