use std::{ffi::{CString, CStr}, convert::TryInto};
use crate::{db_internal::{fs_close, fs_open, fs_read, fs_write, fs_seek, fs_tell, fs_eof, fs_deviceExists, fs_deviceEject, fs_fileExists, fs_closeDir, fs_openDir, fs_readDir, clock_timestampToDatetime, fs_rewindDir, fs_allocMemoryCard}, clock::DateTime};
const ESUCCESS: i32 = 0;
const EACCESS: i32 = 2;
const EEXIST: i32 = 20;
const EFBIG: i32 = 22;
const ENFILE: i32 = 41;
const ENODEV: i32 = 43;
const ENOENT: i32 = 44;
const ENOSPC: i32 = 51;
const EROFS: i32 = 69;
const ESPIPE: i32 = 70;
#[repr(C)]
#[derive(Clone, Copy)]
pub enum FileMode {
Read,
Write
}
#[repr(C)]
#[derive(Clone, Copy)]
pub enum SeekOrigin {
Begin,
Current,
End,
}
#[derive(Debug)]
pub enum IOError {
TooManyFilesOpen,
ReadOnlyFileSystem,
FileNotFound,
DirectoryNotFound,
NoSuchDevice,
NotSupported,
InvalidSeek,
FileTooBig,
FileAlreadyExists,
NoSpaceOnDevice,
ReachedEndOfFile
}
pub struct FileStream {
handle: i32,
}
impl std::io::Read for FileStream {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
unsafe {
let result = fs_read(self.handle, buf.as_mut_ptr().cast(), buf.len().try_into().unwrap());
match crate::db_internal::ERRNO {
ESUCCESS => {
}
EACCESS => {
return Err(std::io::Error::from(std::io::ErrorKind::PermissionDenied));
}
_ => {
panic!("Unhandled errno");
}
}
return Ok(result.try_into().unwrap());
}
}
}
impl std::io::Write for FileStream {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
unsafe {
let result = fs_write(self.handle, buf.as_ptr().cast(), buf.len().try_into().unwrap());
match crate::db_internal::ERRNO {
ESUCCESS => {
}
EACCESS => {
return Err(std::io::Error::from(std::io::ErrorKind::PermissionDenied));
}
EFBIG => {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "File size limit reached"));
}
_ => {
panic!("Unhandled errno");
}
}
return Ok(result.try_into().unwrap());
}
}
fn flush(&mut self) -> std::io::Result<()> {
unsafe {
crate::db_internal::fs_flush(self.handle);
match crate::db_internal::ERRNO {
ESUCCESS => {
return Ok(());
},
EACCESS => {
return Err(std::io::Error::from(std::io::ErrorKind::PermissionDenied));
},
_ => {
panic!("Unhandled errno");
}
}
}
}
}
impl std::io::Seek for FileStream {
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
unsafe {
let result = match pos {
std::io::SeekFrom::Start(position) => {
fs_seek(self.handle, position.try_into().unwrap(), SeekOrigin::Begin)
},
std::io::SeekFrom::Current(position) => {
fs_seek(self.handle, position.try_into().unwrap(), SeekOrigin::Current)
},
std::io::SeekFrom::End(position) => {
fs_seek(self.handle, position.try_into().unwrap(), SeekOrigin::End)
}
};
match crate::db_internal::ERRNO {
ESUCCESS => {
}
ESPIPE => {
return Err(std::io::Error::from(std::io::ErrorKind::BrokenPipe));
}
_ => {
panic!("Unhandled errno");
}
}
return Ok(result.try_into().unwrap());
}
}
}
impl FileStream {
pub fn open(path: &str, mode: FileMode) -> Result<FileStream, IOError> {
unsafe {
let path_cstr = CString::new(path).expect("Failed creating C string");
let handle = fs_open(path_cstr.as_ptr(), mode);
if handle == 0 {
match crate::db_internal::ERRNO {
ENFILE => {
return Err(IOError::TooManyFilesOpen);
}
ENOENT => {
return Err(IOError::FileNotFound);
}
EROFS => {
return Err(IOError::ReadOnlyFileSystem);
}
ENODEV => {
return Err(IOError::NoSuchDevice);
}
_ => {
panic!("Unhandled errno");
}
}
}
return Ok(FileStream {
handle: handle
});
}
}
pub fn allocate_memory_card(path: &str, icondata: &[u8;128], iconpalette: &[u16;16], blocks: i32) -> Result<FileStream, IOError> {
unsafe {
let path_cstr = CString::new(path).expect("Failed creating C string");
let handle = fs_allocMemoryCard(path_cstr.as_ptr(), icondata.as_ptr(), iconpalette.as_ptr(), blocks);
if handle == 0 {
match crate::db_internal::ERRNO {
EEXIST => {
return Err(IOError::FileAlreadyExists);
}
ENOSPC => {
return Err(IOError::NoSpaceOnDevice);
}
ENODEV => {
return Err(IOError::NoSuchDevice);
}
_ => {
panic!("Unhandled errno");
}
}
}
return Ok(FileStream {
handle: handle
});
}
}
pub fn position(&self) -> i32 {
unsafe {
return fs_tell(self.handle);
}
}
pub fn end_of_file(&self) -> bool {
unsafe {
return fs_eof(self.handle);
}
}
}
impl Drop for FileStream {
fn drop(&mut self) {
unsafe { fs_close(self.handle); }
}
}
pub struct DirectoryEntry {
pub name: String,
pub is_directory: bool,
pub size: i32,
pub created: DateTime,
pub modified: DateTime,
}
pub struct DirectoryInfo {
handle: i32,
}
impl DirectoryInfo {
pub fn open(path: &str) -> Result<DirectoryInfo, IOError> {
unsafe {
let path_cstr = CString::new(path).expect("Failed creating C string");
let result = fs_openDir(path_cstr.as_ptr());
match crate::db_internal::ERRNO {
ESUCCESS => {
}
ENOENT => {
return Err(IOError::DirectoryNotFound);
}
ENODEV => {
return Err(IOError::NoSuchDevice);
}
_ => {
panic!("Unhandled errno");
}
}
return Ok(DirectoryInfo {
handle: result
});
}
}
pub fn read(self) -> Option<DirectoryEntry> {
unsafe {
let dir_info_ptr = fs_readDir(self.handle);
if dir_info_ptr.is_null() {
return None;
}
let name_cstr = CStr::from_ptr((*dir_info_ptr).name.as_ptr());
let name_str = name_cstr.to_str().unwrap();
let mut created_dt = DateTime {
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
};
clock_timestampToDatetime((*dir_info_ptr).created, &mut created_dt);
let mut modified_dt = DateTime {
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
};
clock_timestampToDatetime((*dir_info_ptr).modified, &mut modified_dt);
return Some(DirectoryEntry {
name: name_str.to_string(),
is_directory: (*dir_info_ptr).is_directory != 0,
size: (*dir_info_ptr).size,
created: created_dt,
modified: modified_dt,
});
}
}
pub fn rewind(self) {
unsafe {
fs_rewindDir(self.handle);
}
}
}
impl Drop for DirectoryInfo {
fn drop(&mut self) {
unsafe { fs_closeDir(self.handle); }
}
}
pub fn device_exists(device: &str) -> bool {
unsafe {
let path_cstr = CString::new(device).expect("Failed creating C string");
return fs_deviceExists(path_cstr.as_ptr());
}
}
pub fn device_eject(device: &str) {
unsafe {
let path_cstr = CString::new(device).expect("Failed creating C string");
fs_deviceEject(path_cstr.as_ptr());
}
}
pub fn file_exists(path: &str) -> bool {
unsafe {
let path_cstr = CString::new(path).expect("Failed creating C string");
return fs_fileExists(path_cstr.as_ptr());
}
}