PR-09 - VirtualFS
This commit is contained in:
parent
a8119d71f0
commit
5e5b703a56
3
.gitignore
vendored
3
.gitignore
vendored
@ -46,3 +46,6 @@ ehthumbs.db
|
||||
.env
|
||||
.env.*
|
||||
|
||||
mnt
|
||||
mnt/**
|
||||
|
||||
|
||||
112
crates/host-desktop/src/fs_desktop_backend.rs
Normal file
112
crates/host-desktop/src/fs_desktop_backend.rs
Normal file
@ -0,0 +1,112 @@
|
||||
use std::path::PathBuf;
|
||||
use std::fs;
|
||||
use prometeu_core::fs::{FsBackend, FsEntry, FsError};
|
||||
|
||||
pub struct HostDirBackend {
|
||||
root: PathBuf,
|
||||
}
|
||||
|
||||
impl HostDirBackend {
|
||||
pub fn new(root: impl Into<PathBuf>) -> Self {
|
||||
Self { root: root.into() }
|
||||
}
|
||||
|
||||
fn resolve(&self, path: &str) -> PathBuf {
|
||||
let path = path.trim_start_matches('/');
|
||||
self.root.join(path)
|
||||
}
|
||||
}
|
||||
|
||||
impl FsBackend for HostDirBackend {
|
||||
fn mount(&mut self) -> Result<(), FsError> {
|
||||
if !self.root.exists() {
|
||||
return Err(FsError::NotFound);
|
||||
}
|
||||
|
||||
for dir in &["system", "apps", "media", "user"] {
|
||||
let path = self.root.join(dir);
|
||||
if !path.exists() {
|
||||
fs::create_dir_all(&path).map_err(|e| FsError::IOError(e.to_string()))?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmount(&mut self) {}
|
||||
|
||||
fn list_dir(&self, path: &str) -> Result<Vec<FsEntry>, FsError> {
|
||||
let full_path = self.resolve(path);
|
||||
let entries = fs::read_dir(full_path).map_err(|e| FsError::IOError(e.to_string()))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for entry in entries {
|
||||
let entry = entry.map_err(|e| FsError::IOError(e.to_string()))?;
|
||||
let metadata = entry.metadata().map_err(|e| FsError::IOError(e.to_string()))?;
|
||||
result.push(FsEntry {
|
||||
name: entry.file_name().to_string_lossy().into_owned(),
|
||||
is_dir: metadata.is_dir(),
|
||||
size: metadata.len(),
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError> {
|
||||
let full_path = self.resolve(path);
|
||||
fs::read(full_path).map_err(|e| match e.kind() {
|
||||
std::io::ErrorKind::NotFound => FsError::NotFound,
|
||||
_ => FsError::IOError(e.to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError> {
|
||||
let full_path = self.resolve(path);
|
||||
if let Some(parent) = full_path.parent() {
|
||||
fs::create_dir_all(parent).map_err(|e| FsError::IOError(e.to_string()))?;
|
||||
}
|
||||
fs::write(full_path, data).map_err(|e| FsError::IOError(e.to_string()))
|
||||
}
|
||||
|
||||
fn delete(&mut self, path: &str) -> Result<(), FsError> {
|
||||
let full_path = self.resolve(path);
|
||||
if full_path.is_dir() {
|
||||
fs::remove_dir_all(full_path).map_err(|e| FsError::IOError(e.to_string()))
|
||||
} else {
|
||||
fs::remove_file(full_path).map_err(|e| FsError::IOError(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
fn exists(&self, path: &str) -> bool {
|
||||
self.resolve(path).exists()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
use std::env;
|
||||
|
||||
fn get_temp_dir(name: &str) -> PathBuf {
|
||||
let mut path = env::temp_dir();
|
||||
path.push(format!("prometeu_host_test_{}_{}", name, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()));
|
||||
fs::create_dir_all(&path).unwrap();
|
||||
path
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_host_dir_backend_mount_and_dirs() {
|
||||
let root = get_temp_dir("mount");
|
||||
let mut backend = HostDirBackend::new(root.clone());
|
||||
|
||||
backend.mount().unwrap();
|
||||
|
||||
assert!(root.join("system").is_dir());
|
||||
assert!(root.join("apps").is_dir());
|
||||
assert!(root.join("media").is_dir());
|
||||
assert!(root.join("user").is_dir());
|
||||
|
||||
let _ = fs::remove_dir_all(root);
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,25 @@
|
||||
mod audio_mixer;
|
||||
mod prometeu_runner;
|
||||
mod fs_desktop_backend;
|
||||
|
||||
use crate::prometeu_runner::PrometeuRunner;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let mut fs_root = None;
|
||||
let mut i = 0;
|
||||
while i < args.len() {
|
||||
if args[i] == "--fs-root" && i + 1 < args.len() {
|
||||
fs_root = Some(args[i + 1].clone());
|
||||
i += 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
let event_loop = EventLoop::new()?;
|
||||
|
||||
let mut runner = PrometeuRunner::new();
|
||||
let mut runner = PrometeuRunner::new(fs_root);
|
||||
event_loop.run_app(&mut runner)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::audio_mixer::AudioMixer;
|
||||
use crate::fs_desktop_backend::HostDirBackend;
|
||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||
use pixels::{Pixels, SurfaceTexture};
|
||||
use prometeu_core::firmware::Firmware;
|
||||
@ -23,6 +24,7 @@ pub struct PrometeuRunner {
|
||||
firmware: Firmware,
|
||||
|
||||
input_signals: InputSignals,
|
||||
fs_root: Option<String>,
|
||||
|
||||
frame_target_dt: Duration,
|
||||
last_frame_time: Instant,
|
||||
@ -39,15 +41,22 @@ pub struct PrometeuRunner {
|
||||
}
|
||||
|
||||
impl PrometeuRunner {
|
||||
pub(crate) fn new() -> Self {
|
||||
pub(crate) fn new(fs_root: Option<String>) -> Self {
|
||||
let target_fps = 60;
|
||||
|
||||
let mut firmware = Firmware::new();
|
||||
if let Some(root) = &fs_root {
|
||||
let backend = HostDirBackend::new(root);
|
||||
firmware.os.mount_fs(Box::new(backend));
|
||||
}
|
||||
|
||||
Self {
|
||||
window: None,
|
||||
pixels: None,
|
||||
hardware: Hardware::new(),
|
||||
firmware: Firmware::new(),
|
||||
firmware,
|
||||
input_signals: InputSignals::default(),
|
||||
fs_root,
|
||||
frame_target_dt: Duration::from_nanos(1_000_000_000 / target_fps),
|
||||
last_frame_time: Instant::now(),
|
||||
accumulator: Duration::ZERO,
|
||||
@ -243,6 +252,17 @@ impl ApplicationHandler for PrometeuRunner {
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||
// Atualiza estado do Filesystem no OS (específico do host-desktop)
|
||||
if let Some(root) = &self.fs_root {
|
||||
use prometeu_core::fs::FsState;
|
||||
if matches!(self.firmware.os.fs_state, FsState::Unmounted | FsState::Error(_)) {
|
||||
if std::path::Path::new(root).exists() {
|
||||
let backend = HostDirBackend::new(root);
|
||||
self.firmware.os.mount_fs(Box::new(backend));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let now = Instant::now();
|
||||
let mut frame_delta = now.duration_since(self.last_frame_time);
|
||||
|
||||
|
||||
12
crates/prometeu-core/src/fs/fs_backend.rs
Normal file
12
crates/prometeu-core/src/fs/fs_backend.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::fs::{FsEntry, FsError};
|
||||
|
||||
pub trait FsBackend: Send + Sync {
|
||||
fn mount(&mut self) -> Result<(), FsError>;
|
||||
fn unmount(&mut self);
|
||||
fn list_dir(&self, path: &str) -> Result<Vec<FsEntry>, FsError>;
|
||||
fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError>;
|
||||
fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError>;
|
||||
fn delete(&mut self, path: &str) -> Result<(), FsError>;
|
||||
fn exists(&self, path: &str) -> bool;
|
||||
fn is_healthy(&self) -> bool { true }
|
||||
}
|
||||
5
crates/prometeu-core/src/fs/fs_entry.rs
Normal file
5
crates/prometeu-core/src/fs/fs_entry.rs
Normal file
@ -0,0 +1,5 @@
|
||||
pub struct FsEntry {
|
||||
pub name: String,
|
||||
pub is_dir: bool,
|
||||
pub size: u64,
|
||||
}
|
||||
24
crates/prometeu-core/src/fs/fs_error.rs
Normal file
24
crates/prometeu-core/src/fs/fs_error.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FsError {
|
||||
NotFound,
|
||||
AlreadyExists,
|
||||
PermissionDenied,
|
||||
NotMounted,
|
||||
IOError(String),
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for FsError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
FsError::NotFound => write!(f, "File or directory not found"),
|
||||
FsError::AlreadyExists => write!(f, "Already exists"),
|
||||
FsError::PermissionDenied => write!(f, "Permission denied"),
|
||||
FsError::NotMounted => write!(f, "Filesystem not mounted"),
|
||||
FsError::IOError(s) => write!(f, "IO Error: {}", s),
|
||||
FsError::Other(s) => write!(f, "Error: {}", s),
|
||||
}
|
||||
}
|
||||
}
|
||||
8
crates/prometeu-core/src/fs/fs_state.rs
Normal file
8
crates/prometeu-core/src/fs/fs_state.rs
Normal file
@ -0,0 +1,8 @@
|
||||
use crate::fs::FsError;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum FsState {
|
||||
Unmounted,
|
||||
Mounted,
|
||||
Error(FsError),
|
||||
}
|
||||
11
crates/prometeu-core/src/fs/mod.rs
Normal file
11
crates/prometeu-core/src/fs/mod.rs
Normal file
@ -0,0 +1,11 @@
|
||||
mod fs_error;
|
||||
mod fs_state;
|
||||
mod fs_entry;
|
||||
mod fs_backend;
|
||||
mod virtual_fs;
|
||||
|
||||
pub use fs_error::FsError;
|
||||
pub use fs_state::FsState;
|
||||
pub use fs_entry::FsEntry;
|
||||
pub use fs_backend::FsBackend;
|
||||
pub use virtual_fs::VirtualFS;
|
||||
147
crates/prometeu-core/src/fs/virtual_fs.rs
Normal file
147
crates/prometeu-core/src/fs/virtual_fs.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use crate::fs::{FsBackend, FsEntry, FsError};
|
||||
|
||||
pub struct VirtualFS {
|
||||
backend: Option<Box<dyn FsBackend>>,
|
||||
}
|
||||
|
||||
impl VirtualFS {
|
||||
pub fn new() -> Self {
|
||||
Self { backend: None }
|
||||
}
|
||||
|
||||
pub fn mount(&mut self, mut backend: Box<dyn FsBackend>) -> Result<(), FsError> {
|
||||
backend.mount()?;
|
||||
self.backend = Some(backend);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unmount(&mut self) {
|
||||
if let Some(mut backend) = self.backend.take() {
|
||||
backend.unmount();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mounted(&self) -> bool {
|
||||
self.backend.is_some()
|
||||
}
|
||||
|
||||
fn normalize_path(&self, path: &str) -> String {
|
||||
let mut normalized = path.replace('\\', "/");
|
||||
if !normalized.starts_with('/') {
|
||||
normalized = format!("/{}", normalized);
|
||||
}
|
||||
normalized
|
||||
}
|
||||
|
||||
pub fn list_dir(&self, path: &str) -> Result<Vec<FsEntry>, FsError> {
|
||||
let normalized = self.normalize_path(path);
|
||||
let backend = self.backend.as_ref().ok_or(FsError::NotMounted)?;
|
||||
backend.list_dir(&normalized)
|
||||
}
|
||||
|
||||
pub fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError> {
|
||||
let normalized = self.normalize_path(path);
|
||||
let backend = self.backend.as_ref().ok_or(FsError::NotMounted)?;
|
||||
backend.read_file(&normalized)
|
||||
}
|
||||
|
||||
pub fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError> {
|
||||
let normalized = self.normalize_path(path);
|
||||
let backend = self.backend.as_mut().ok_or(FsError::NotMounted)?;
|
||||
backend.write_file(&normalized, data)
|
||||
}
|
||||
|
||||
pub fn delete(&mut self, path: &str) -> Result<(), FsError> {
|
||||
let normalized = self.normalize_path(path);
|
||||
let backend = self.backend.as_mut().ok_or(FsError::NotMounted)?;
|
||||
backend.delete(&normalized)
|
||||
}
|
||||
|
||||
pub fn exists(&self, path: &str) -> bool {
|
||||
let normalized = self.normalize_path(path);
|
||||
self.backend.as_ref().map(|b| b.exists(&normalized)).unwrap_or(false)
|
||||
}
|
||||
|
||||
pub fn is_healthy(&self) -> bool {
|
||||
self.backend.as_ref().map(|b| b.is_healthy()).unwrap_or(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::collections::HashMap;
|
||||
|
||||
struct MockBackend {
|
||||
files: HashMap<String, Vec<u8>>,
|
||||
healthy: bool,
|
||||
}
|
||||
|
||||
impl MockBackend {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
files: HashMap::new(),
|
||||
healthy: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FsBackend for MockBackend {
|
||||
fn mount(&mut self) -> Result<(), FsError> { Ok(()) }
|
||||
fn unmount(&mut self) {}
|
||||
fn list_dir(&self, _path: &str) -> Result<Vec<FsEntry>, FsError> {
|
||||
Ok(self.files.keys().map(|name| FsEntry {
|
||||
name: name.clone(),
|
||||
is_dir: false,
|
||||
size: 0,
|
||||
}).collect())
|
||||
}
|
||||
fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError> {
|
||||
self.files.get(path).cloned().ok_or(FsError::NotFound)
|
||||
}
|
||||
fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError> {
|
||||
self.files.insert(path.to_string(), data.to_vec());
|
||||
Ok(())
|
||||
}
|
||||
fn delete(&mut self, path: &str) -> Result<(), FsError> {
|
||||
self.files.remove(path);
|
||||
Ok(())
|
||||
}
|
||||
fn exists(&self, path: &str) -> bool {
|
||||
self.files.contains_key(path)
|
||||
}
|
||||
fn is_healthy(&self) -> bool {
|
||||
self.healthy
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtual_fs_operations() {
|
||||
let mut vfs = VirtualFS::new();
|
||||
let backend = MockBackend::new();
|
||||
|
||||
vfs.mount(Box::new(backend)).unwrap();
|
||||
|
||||
let test_file = "/user/test.txt";
|
||||
let content = b"hello world";
|
||||
|
||||
vfs.write_file(test_file, content).unwrap();
|
||||
assert!(vfs.exists(test_file));
|
||||
|
||||
let read_content = vfs.read_file(test_file).unwrap();
|
||||
assert_eq!(read_content, content);
|
||||
|
||||
vfs.delete(test_file).unwrap();
|
||||
assert!(!vfs.exists(test_file));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtual_fs_health() {
|
||||
let mut vfs = VirtualFS::new();
|
||||
let mut backend = MockBackend::new();
|
||||
backend.healthy = false;
|
||||
|
||||
vfs.mount(Box::new(backend)).unwrap();
|
||||
assert!(!vfs.is_healthy());
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ pub mod hardware;
|
||||
pub mod virtual_machine;
|
||||
mod model;
|
||||
pub mod firmware;
|
||||
pub mod fs;
|
||||
mod prometeu_os;
|
||||
mod prometeu_hub;
|
||||
|
||||
|
||||
@ -2,7 +2,9 @@ use crate::hardware::{HardwareBridge, InputSignals};
|
||||
use crate::model::{Cartridge, Color, Sample};
|
||||
use crate::prometeu_os::NativeInterface;
|
||||
use crate::virtual_machine::{Value, VirtualMachine};
|
||||
use crate::fs::{VirtualFS, FsBackend, FsState};
|
||||
use std::sync::Arc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// PrometeuOS (POS): O firmware/base do sistema.
|
||||
/// Autoridade máxima do boot, periféricos, execução da PVM e tratamento de falhas.
|
||||
@ -17,6 +19,12 @@ pub struct PrometeuOS {
|
||||
pub sample_square: Option<Arc<Sample>>,
|
||||
pub sample_kick: Option<Arc<Sample>>,
|
||||
pub sample_snare: Option<Arc<Sample>>,
|
||||
|
||||
// Filesystem
|
||||
pub fs: VirtualFS,
|
||||
pub fs_state: FsState,
|
||||
pub open_files: HashMap<u32, String>,
|
||||
pub next_handle: u32,
|
||||
}
|
||||
|
||||
impl PrometeuOS {
|
||||
@ -33,6 +41,10 @@ impl PrometeuOS {
|
||||
sample_square: None,
|
||||
sample_kick: None,
|
||||
sample_snare: None,
|
||||
fs: VirtualFS::new(),
|
||||
fs_state: FsState::Unmounted,
|
||||
open_files: HashMap::new(),
|
||||
next_handle: 1,
|
||||
};
|
||||
|
||||
// Inicializa samples básicos (mesma lógica do LogicalHardware)
|
||||
@ -41,6 +53,30 @@ impl PrometeuOS {
|
||||
os
|
||||
}
|
||||
|
||||
pub fn mount_fs(&mut self, backend: Box<dyn FsBackend>) {
|
||||
match self.fs.mount(backend) {
|
||||
Ok(_) => {
|
||||
self.fs_state = FsState::Mounted;
|
||||
}
|
||||
Err(e) => {
|
||||
self.fs_state = FsState::Error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmount_fs(&mut self) {
|
||||
self.fs.unmount();
|
||||
self.fs_state = FsState::Unmounted;
|
||||
}
|
||||
|
||||
fn update_fs(&mut self) {
|
||||
if self.fs_state == FsState::Mounted {
|
||||
if !self.fs.is_healthy() {
|
||||
self.unmount_fs();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self, vm: &mut VirtualMachine) {
|
||||
*vm = VirtualMachine::default();
|
||||
self.tick_index = 0;
|
||||
@ -60,6 +96,8 @@ impl PrometeuOS {
|
||||
let start = std::time::Instant::now();
|
||||
self.tick_index += 1;
|
||||
|
||||
self.update_fs();
|
||||
|
||||
if !self.logical_frame_active {
|
||||
self.logical_frame_active = true;
|
||||
self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME;
|
||||
@ -311,6 +349,107 @@ impl NativeInterface for PrometeuOS {
|
||||
}
|
||||
Ok(300)
|
||||
}
|
||||
|
||||
// --- Filesystem Syscalls (0x4000) ---
|
||||
|
||||
// FS_OPEN(path) -> handle
|
||||
0x4001 => {
|
||||
let path = match vm.pop()? {
|
||||
Value::String(s) => s,
|
||||
_ => return Err("Expected string path".into()),
|
||||
};
|
||||
if self.fs_state != FsState::Mounted {
|
||||
vm.push(Value::Integer(-1));
|
||||
return Ok(100);
|
||||
}
|
||||
let handle = self.next_handle;
|
||||
self.open_files.insert(handle, path);
|
||||
self.next_handle += 1;
|
||||
vm.push(Value::Integer(handle as i64));
|
||||
Ok(200)
|
||||
}
|
||||
// FS_READ(handle) -> content
|
||||
0x4002 => {
|
||||
let handle = vm.pop_integer()? as u32;
|
||||
let path = self.open_files.get(&handle).ok_or("Invalid handle")?;
|
||||
match self.fs.read_file(path) {
|
||||
Ok(data) => {
|
||||
let s = String::from_utf8_lossy(&data).into_owned();
|
||||
vm.push(Value::String(s));
|
||||
Ok(1000)
|
||||
}
|
||||
Err(_e) => {
|
||||
vm.push(Value::Null);
|
||||
Ok(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
// FS_WRITE(handle, content)
|
||||
0x4003 => {
|
||||
let content = match vm.pop()? {
|
||||
Value::String(s) => s,
|
||||
_ => return Err("Expected string content".into()),
|
||||
};
|
||||
let handle = vm.pop_integer()? as u32;
|
||||
let path = self.open_files.get(&handle).ok_or("Invalid handle")?;
|
||||
match self.fs.write_file(path, content.as_bytes()) {
|
||||
Ok(_) => {
|
||||
vm.push(Value::Boolean(true));
|
||||
Ok(1000)
|
||||
}
|
||||
Err(_) => {
|
||||
vm.push(Value::Boolean(false));
|
||||
Ok(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
// FS_CLOSE(handle)
|
||||
0x4004 => {
|
||||
let handle = vm.pop_integer()? as u32;
|
||||
self.open_files.remove(&handle);
|
||||
Ok(100)
|
||||
}
|
||||
// FS_LISTDIR(path)
|
||||
0x4005 => {
|
||||
let path = match vm.pop()? {
|
||||
Value::String(s) => s,
|
||||
_ => return Err("Expected string path".into()),
|
||||
};
|
||||
match self.fs.list_dir(&path) {
|
||||
Ok(entries) => {
|
||||
// Por enquanto, retorna uma string separada por ';'
|
||||
let names: Vec<String> = entries.into_iter().map(|e| e.name).collect();
|
||||
vm.push(Value::String(names.join(";")));
|
||||
Ok(500)
|
||||
}
|
||||
Err(_) => {
|
||||
vm.push(Value::Null);
|
||||
Ok(100)
|
||||
}
|
||||
}
|
||||
}
|
||||
// FS_EXISTS(path) -> bool
|
||||
0x4006 => {
|
||||
let path = match vm.pop()? {
|
||||
Value::String(s) => s,
|
||||
_ => return Err("Expected string path".into()),
|
||||
};
|
||||
vm.push(Value::Boolean(self.fs.exists(&path)));
|
||||
Ok(100)
|
||||
}
|
||||
// FS_DELETE(path)
|
||||
0x4007 => {
|
||||
let path = match vm.pop()? {
|
||||
Value::String(s) => s,
|
||||
_ => return Err("Expected string path".into()),
|
||||
};
|
||||
match self.fs.delete(&path) {
|
||||
Ok(_) => vm.push(Value::Boolean(true)),
|
||||
Err(_) => vm.push(Value::Boolean(false)),
|
||||
}
|
||||
Ok(500)
|
||||
}
|
||||
|
||||
_ => Err(format!("Unknown syscall: 0x{:08X}", id)),
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user