summaryrefslogtreecommitdiff
path: root/src/devices/file_device.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/file_device.rs')
-rw-r--r--src/devices/file_device.rs300
1 files changed, 187 insertions, 113 deletions
diff --git a/src/devices/file_device.rs b/src/devices/file_device.rs
index 61966b1..83f0a56 100644
--- a/src/devices/file_device.rs
+++ b/src/devices/file_device.rs
@@ -1,20 +1,4 @@
-mod bedrock_file_path;
-mod bedrock_path_buffer;
-mod buffered_file;
-mod directory_listing;
-mod entry;
-mod operations;
-
-use buffered_file::BufferedFile;
-use bedrock_file_path::BedrockFilePath;
-use bedrock_path_buffer::BedrockPathBuffer;
-use directory_listing::DirectoryListing;
-use entry::{Entry, EntryType};
-use operations::{create_file, move_entry, delete_entry};
-
-use bedrock_core::*;
-
-use std::path::{Component, Path, PathBuf};
+use crate::*;
pub struct FileDevice {
@@ -25,13 +9,14 @@ pub struct FileDevice {
pub action_buffer: BedrockPathBuffer,
pub path_buffer: BedrockPathBuffer,
- pub entry: Option<(Entry, BedrockFilePath)>,
- pub cached_dir: Option<(Entry, BedrockFilePath)>,
+ pub entry: Option<(Entry, BedrockFilePath, Instant)>,
+ pub cached_dir: Option<(Entry, BedrockFilePath, Instant)>,
- pub success: bool,
+ pub error: bool,
pub pointer_write: u32,
pub length_write: u32,
+ pub enable: bool,
pub enable_read: bool,
pub enable_write: bool,
pub enable_create: bool,
@@ -39,21 +24,95 @@ pub struct FileDevice {
pub enable_delete: bool,
}
+
+impl Device for FileDevice {
+ fn read(&mut self, port: u8) -> u8 {
+ if !self.enable { return 0x00; }
+ match port {
+ 0x0 => read_b!(self.entry.is_some()),
+ 0x1 => read_b!(std::mem::take(&mut self.error)),
+ 0x2 => self.read_byte(),
+ 0x3 => self.read_byte(),
+ 0x4 => self.path_buffer.read(),
+ 0x5 => read_b!(self.entry_type()),
+ 0x6 => self.read_child_path(),
+ 0x7 => read_b!(self.child_type()),
+ 0x8 => read_hh!(self.pointer()),
+ 0x9 => read_hl!(self.pointer()),
+ 0xA => read_lh!(self.pointer()),
+ 0xB => read_ll!(self.pointer()),
+ 0xC => read_hh!(self.length()),
+ 0xD => read_hl!(self.length()),
+ 0xE => read_lh!(self.length()),
+ 0xF => read_ll!(self.length()),
+ _ => unreachable!(),
+ }
+ }
+
+ fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
+ if !self.enable { return None; }
+ match port {
+ 0x0 => self.write_to_entry_port(value),
+ 0x1 => self.write_to_action_port(value),
+ 0x2 => self.write_byte(value),
+ 0x3 => self.write_byte(value),
+ 0x4 => self.path_buffer.set_pointer(value),
+ 0x5 => self.ascend_to_parent(),
+ 0x6 => self.set_child_path(value),
+ 0x7 => self.descend_to_child(),
+ 0x8 => write_hh!(self.pointer_write, value),
+ 0x9 => write_hl!(self.pointer_write, value),
+ 0xA => write_lh!(self.pointer_write, value),
+ 0xB => {write_ll!(self.pointer_write, value); self.commit_pointer()},
+ 0xC => write_hh!(self.length_write, value),
+ 0xD => write_hl!(self.length_write, value),
+ 0xE => write_lh!(self.length_write, value),
+ 0xF => {write_ll!(self.length_write, value); self.commit_length()},
+ _ => unreachable!(),
+ };
+ return None;
+ }
+
+ fn wake(&mut self) -> bool {
+ false
+ }
+
+ fn reset(&mut self) {
+ todo!()
+ }
+}
+
+
impl FileDevice {
- pub fn new() -> Self {
+ pub fn new(config: &EmulatorConfig) -> Self {
#[cfg(target_family = "unix")]
let default_base: PathBuf = PathBuf::from("/");
#[cfg(target_family = "windows")]
let default_base: PathBuf = PathBuf::from("");
+ let current_dir = match std::env::current_dir() {
+ Ok(dir) => PathBuf::from(dir),
+ Err(_) => PathBuf::from(""),
+ };
+
+ let (enable, base_path, default_path) = if config.trust_files {
+ (true, default_base, current_dir)
+ } else if let Some(config_dir) = dirs_next::config_dir() {
+ let bedrock_dir = config_dir.join("bedrock");
+ let identifier = config.identifier.clone().unwrap_or("default".to_string());
+ let sandbox_dir = bedrock_dir.join(identifier);
+ vagabond::make_directory(&sandbox_dir).unwrap();
+ (true, sandbox_dir.clone(), sandbox_dir)
+ } else {
+ error!("Could not determine sandbox path for file device");
+ (false, default_base, current_dir)
+ };
+
// TODO: I'm not at all confident that the default path is correct
// when not being set as the current directory.
Self {
- base_path: default_base,
- default_path: match std::env::current_dir() {
- Ok(dir) => PathBuf::from(dir),
- Err(_) => PathBuf::from(""),
- },
+ base_path,
+ default_path,
entry_buffer: BedrockPathBuffer::new(),
action_buffer: BedrockPathBuffer::new(),
@@ -62,10 +121,11 @@ impl FileDevice {
entry: None,
cached_dir: None,
- success: false,
+ error: false,
pointer_write: 0,
length_write: 0,
+ enable,
enable_read: true,
enable_write: true,
enable_create: true,
@@ -74,6 +134,12 @@ impl FileDevice {
}
}
+ pub fn check_success(&mut self, success: bool) {
+ if !success {
+ self.error = true;
+ }
+ }
+
/// Safely close the current entry, cleaning up entry variables.
pub fn close(&mut self) {
self.entry_buffer.clear();
@@ -81,10 +147,10 @@ impl FileDevice {
self.path_buffer.clear();
self.flush();
- if let Some((Entry::Directory(mut dir), path)) = std::mem::take(&mut self.entry) {
+ if let Some((Entry::Directory(mut dir), path, time)) = std::mem::take(&mut self.entry) {
// Prevent the selected child from persisting when loading from cache.
dir.deselect_child();
- self.cached_dir = Some((Entry::Directory(dir), path));
+ self.cached_dir = Some((Entry::Directory(dir), path, time));
}
}
@@ -100,17 +166,17 @@ impl FileDevice {
if let Ok(file) = open_result {
self.close();
self.path_buffer.populate(path.as_buffer());
- self.entry = Some((Entry::File(BufferedFile::new(file)), path));
+ self.entry = Some((Entry::File(BufferedFile::new(file)), path, Instant::now()));
return Ok(());
};
}
Some(EntryType::Directory) => {
- // Attempt to use the cached directory.
- if let Some((dir, cached_path)) = std::mem::take(&mut self.cached_dir) {
- if cached_path == path {
+ // Attempt to use the cached directory if not too old.
+ if let Some((dir, cached_path, time)) = std::mem::take(&mut self.cached_dir) {
+ if cached_path == path && time.elapsed() < Duration::from_secs(1) {
self.close();
self.path_buffer.populate(cached_path.as_buffer());
- self.entry = Some((dir, cached_path));
+ self.entry = Some((dir, cached_path, time));
return Ok(());
}
}
@@ -118,7 +184,7 @@ impl FileDevice {
if let Some(listing) = DirectoryListing::from_path(&path) {
self.close();
self.path_buffer.populate(path.as_buffer());
- self.entry = Some((Entry::Directory(listing), path));
+ self.entry = Some((Entry::Directory(listing), path, Instant::now()));
return Ok(());
};
}
@@ -132,10 +198,16 @@ impl FileDevice {
pub fn write_to_entry_port(&mut self, byte: u8) {
if let Some(buffer) = self.entry_buffer.write(byte) {
self.close();
- match BedrockFilePath::from_buffer(buffer, &self.base_path) {
- Some(path) => self.success = self.open(path).is_ok(),
- None => self.success = false,
- };
+ // Attempt to open file if buffer was not empty.
+ if buffer[0] != 0 {
+ let success = match BedrockFilePath::from_buffer(buffer, &self.base_path) {
+ Some(path) => self.open(path).is_ok(),
+ None => false,
+ };
+ self.check_success(success);
+ } else {
+ self.check_success(true);
+ }
}
}
@@ -144,22 +216,23 @@ impl FileDevice {
if let Some(buffer) = self.action_buffer.write(byte) {
let destination_blank = buffer[0] == 0x00;
let destination = BedrockFilePath::from_buffer(buffer, &self.base_path);
- self.success = false;
- if let Some((_, source)) = &self.entry {
+ if let Some((_, source, _)) = &self.entry {
if destination_blank {
if self.enable_delete {
- self.success = delete_entry(&source.as_path());
+ self.check_success(delete_entry(&source.as_path()));
}
} else if let Some(dest) = destination {
if self.enable_move {
- self.success = move_entry(&source.as_path(), &dest.as_path());
+ self.check_success(move_entry(&source.as_path(), &dest.as_path()));
}
}
} else if let Some(dest) = destination {
if self.enable_create {
- self.success = create_file(&dest.as_path());
+ self.check_success(create_file(&dest.as_path()));
}
+ } else {
+ self.check_success(false);
}
self.close();
}
@@ -167,35 +240,38 @@ impl FileDevice {
/// Attempt to open the parent directory of the current entry.
pub fn ascend_to_parent(&mut self) {
- if let Some((_, path)) = &self.entry {
- match path.parent() {
- Some(parent) => self.success = self.open(parent).is_ok(),
- None => self.success = false,
+ if let Some((_, path, _)) = &self.entry {
+ let success = match path.parent() {
+ Some(parent) => self.open(parent).is_ok(),
+ None => false,
};
+ self.check_success(success);
} else {
- match BedrockFilePath::from_path(&self.default_path, &self.base_path) {
- Some(default) => self.success = self.open(default).is_ok(),
- None => self.success = false,
+ let success = match BedrockFilePath::from_path(&self.default_path, &self.base_path) {
+ Some(default) => self.open(default).is_ok(),
+ None => false,
};
+ self.check_success(success);
}
}
/// Attempt to open the selected child of the current directory.
pub fn descend_to_child(&mut self) {
- if let Some((Entry::Directory(dir), _)) = &self.entry {
- match dir.child_path() {
- Some(child) => self.success = self.open(child).is_ok(),
- None => self.success = false,
+ if let Some((Entry::Directory(dir), _, _)) = &self.entry {
+ let success = match dir.child_path() {
+ Some(child) => self.open(child).is_ok(),
+ None => false,
};
+ self.check_success(success);
} else {
- self.success = false;
+ self.check_success(false);
}
}
/// Return true if the current entry is a directory.
pub fn entry_type(&self) -> bool {
match self.entry {
- Some((Entry::Directory(_), _)) => true,
+ Some((Entry::Directory(_), _, _)) => true,
_ => false,
}
}
@@ -203,13 +279,13 @@ impl FileDevice {
/// Read a byte from the path buffer of the selected child.
pub fn read_child_path(&mut self) -> u8 {
match &mut self.entry {
- Some((Entry::Directory(dir), _)) => dir.child_path_buffer().read(),
+ Some((Entry::Directory(dir), _, _)) => dir.child_path_buffer().read(),
_ => 0,
}
}
pub fn set_child_path(&mut self, byte: u8) {
- if let Some((Entry::Directory(dir), _)) = &mut self.entry {
+ if let Some((Entry::Directory(dir), _, _)) = &mut self.entry {
dir.child_path_buffer().set_pointer(byte);
}
}
@@ -217,7 +293,7 @@ impl FileDevice {
/// Return true if the selected child is a directory.
pub fn child_type(&self) -> bool {
match &self.entry {
- Some((Entry::Directory(dir), _)) => match dir.child_type() {
+ Some((Entry::Directory(dir), _, _)) => match dir.child_type() {
Some(EntryType::Directory) => true,
_ => false,
}
@@ -228,7 +304,7 @@ impl FileDevice {
/// Read a byte from the current file.
pub fn read_byte(&mut self) -> u8 {
match &mut self.entry {
- Some((Entry::File(file), _)) => file.read(),
+ Some((Entry::File(file), _, _)) => file.read(),
_ => 0,
}
}
@@ -236,102 +312,100 @@ impl FileDevice {
/// Writes a byte to the currently-open file.
pub fn write_byte(&mut self, byte: u8) {
match &mut self.entry {
- Some((Entry::File(file), _)) => file.write(byte),
+ Some((Entry::File(file), _, _)) => file.write(byte),
_ => (),
}
}
pub fn pointer(&mut self) -> u32 {
match &mut self.entry {
- Some((Entry::File(file), _)) => file.pointer(),
- Some((Entry::Directory(dir), _)) => dir.selected(),
+ Some((Entry::File(file), _, _)) => file.pointer(),
+ Some((Entry::Directory(dir), _, _)) => dir.selected(),
_ => 0,
}
}
pub fn commit_pointer(&mut self) {
match &mut self.entry {
- Some((Entry::File(file), _)) => file.set_pointer(self.pointer_write),
- Some((Entry::Directory(dir), _)) => dir.set_selected(self.pointer_write),
+ Some((Entry::File(file), _, _)) => file.set_pointer(self.pointer_write),
+ Some((Entry::Directory(dir), _, _)) => dir.set_selected(self.pointer_write),
_ => (),
}
}
pub fn length(&mut self) -> u32 {
match &mut self.entry {
- Some((Entry::File(file), _)) => file.length(),
- Some((Entry::Directory(dir), _)) => dir.length(),
+ Some((Entry::File(file), _, _)) => file.length(),
+ Some((Entry::Directory(dir), _, _)) => dir.length(),
_ => 0,
}
}
pub fn commit_length(&mut self) {
match &mut self.entry {
- Some((Entry::File(file), _)) => file.set_length(self.length_write),
+ Some((Entry::File(file), _, _)) => file.set_length(self.length_write),
_ => (),
}
}
pub fn flush(&mut self) {
- if let Some((Entry::File(buffered_file), _)) = &mut self.entry {
+ if let Some((Entry::File(buffered_file), _, _)) = &mut self.entry {
let _ = buffered_file;
}
}
}
+
impl Drop for FileDevice {
fn drop(&mut self) {
self.flush();
}
}
-impl Device for FileDevice {
- fn read(&mut self, port: u8) -> u8 {
- match port {
- 0x0 => read_b!(self.entry.is_some()),
- 0x1 => read_b!(self.success),
- 0x2 => self.path_buffer.read(),
- 0x3 => read_b!(self.entry_type()),
- 0x4 => self.read_byte(),
- 0x5 => self.read_byte(),
- 0x6 => self.read_child_path(),
- 0x7 => read_b!(self.child_type()),
- 0x8 => read_hh!(self.pointer()),
- 0x9 => read_hl!(self.pointer()),
- 0xa => read_lh!(self.pointer()),
- 0xb => read_ll!(self.pointer()),
- 0xc => read_hh!(self.length()),
- 0xd => read_hl!(self.length()),
- 0xe => read_lh!(self.length()),
- 0xf => read_ll!(self.length()),
- _ => unreachable!(),
+
+/// Create a new file if it doesn't already exist, returning true if successful.
+pub fn create_file(destination: &Path) -> bool {
+ if entry_exists(destination) {
+ false
+ } else {
+ if let Some(parent_path) = destination.parent() {
+ let _ = std::fs::create_dir_all(parent_path);
}
+ std::fs::OpenOptions::new().write(true).create_new(true)
+ .open(destination).is_ok()
}
+}
- fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
- match port {
- 0x0 => self.write_to_entry_port(value),
- 0x1 => self.write_to_action_port(value),
- 0x2 => self.path_buffer.set_pointer(value),
- 0x3 => self.ascend_to_parent(),
- 0x4 => self.write_byte(value),
- 0x5 => self.write_byte(value),
- 0x6 => self.set_child_path(value),
- 0x7 => self.descend_to_child(),
- 0x8 => write_hh!(self.pointer_write, value),
- 0x9 => write_hl!(self.pointer_write, value),
- 0xa => write_lh!(self.pointer_write, value),
- 0xb => {write_ll!(self.pointer_write, value); self.commit_pointer()},
- 0xc => write_hh!(self.length_write, value),
- 0xd => write_hl!(self.length_write, value),
- 0xe => write_lh!(self.length_write, value),
- 0xf => {write_ll!(self.length_write, value); self.commit_length()},
- _ => unreachable!(),
- };
- return None;
+/// Move an entry from one location to another, returning true if successful.
+pub fn move_entry(source: &Path, destination: &Path) -> bool {
+ if !entry_exists(source) || entry_exists(destination) {
+ return false;
}
+ std::fs::rename(source, destination).is_ok()
+}
- fn wake(&mut self) -> bool {
- false
+/// Delete an entry, returning true if successful.
+pub fn delete_entry(source: &Path) -> bool {
+ use std::fs::{remove_file, remove_dir_all};
+ use std::io::ErrorKind;
+
+ match remove_file(source) {
+ Ok(_) => true,
+ Err(error) => match error.kind() {
+ ErrorKind::NotFound => true,
+ ErrorKind::IsADirectory => match remove_dir_all(source) {
+ Ok(_) => true,
+ Err(error) => match error.kind() {
+ ErrorKind::NotFound => true,
+ _ => false,
+ }
+ }
+ _ => false,
+ }
}
}
+
+/// Returns true if an entry already exists at the given path.
+fn entry_exists(source: &Path) -> bool {
+ std::fs::metadata(source).is_ok()
+}