use std::{ffi::{CString, CStr}, convert::TryInto, mem::{transmute, size_of}};
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 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 FileStream {
pub fn open(path: &String, mode: FileMode) -> Result<FileStream, IOError> {
unsafe {
let path_cstr = CString::new(path.as_str()).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: &String, icondata: &[u8;128], iconpalette: &[u16;16], blocks: i32) -> Result<FileStream, IOError> {
unsafe {
let path_cstr = CString::new(path.as_str()).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 read_to_string(&self, buffer: &mut String) -> Result<usize, IOError> {
let mut buf: [u8;32] = [0;32];
while !self.end_of_file() {
let result = self.read(&mut buf)?;
if result == 0 { break; }
let str = &buf[0..result];
match std::str::from_utf8(str) {
Ok(v) => { buffer.push_str(v) }
Err(_) => { break; }
}
}
return Ok(0);
}
pub fn read(&self, buffer: &mut[u8]) -> Result<usize, IOError> {
unsafe {
let result = fs_read(self.handle, transmute(buffer.as_mut_ptr()), buffer.len().try_into().unwrap());
if result == -1 {
match crate::db_internal::ERRNO {
EACCESS => {
return Err(IOError::NotSupported);
}
_ => {
panic!("Unhandled errno");
}
}
}
return Ok(result.try_into().unwrap());
}
}
pub fn read_element<T: Copy>(&self) -> Result<T, IOError> {
let mut buf : Vec<u8> = vec![0;size_of::<T>()];
let result = TryInto::<usize>::try_into(self.read(buf.as_mut_slice())?).unwrap();
if result != buf.len() {
return Err(IOError::ReachedEndOfFile);
}
unsafe {
let data = *(transmute::<*const u8, *const T>(buf.as_ptr()));
return Ok(data);
}
}
pub fn write(&self, buffer: &[u8]) -> Result<i32, IOError> {
unsafe {
let result = fs_write(self.handle, transmute(buffer.as_ptr()), buffer.len().try_into().unwrap());
if result == -1 {
match crate::db_internal::ERRNO {
EACCESS => {
return Err(IOError::NotSupported);
}
EFBIG => {
return Err(IOError::FileTooBig);
}
_ => {
panic!("Unhandled errno");
}
}
}
return Ok(result);
}
}
pub fn seek(&self, position: i32, origin: SeekOrigin) -> Result<i32, IOError> {
unsafe {
let result = fs_seek(self.handle, position, origin);
if result == -1 {
match crate::db_internal::ERRNO {
ESPIPE => {
return Err(IOError::InvalidSeek);
}
_ => {
panic!("Unhandled errno");
}
}
}
return Ok(result);
}
}
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: &String) -> Result<DirectoryInfo, IOError> {
unsafe {
let path_cstr = CString::new(path.as_str()).expect("Failed creating C string");
let result = fs_openDir(path_cstr.as_ptr());
if result == -1 {
match crate::db_internal::ERRNO {
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: String::from(name_str),
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: &String) -> bool {
unsafe {
let path_cstr = CString::new(device.as_str()).expect("Failed creating C string");
return fs_deviceExists(path_cstr.as_ptr());
}
}
pub fn device_eject(device: &String) {
unsafe {
let path_cstr = CString::new(device.as_str()).expect("Failed creating C string");
fs_deviceEject(path_cstr.as_ptr());
}
}
pub fn file_exists(path: &String) -> bool {
unsafe {
let path_cstr = CString::new(path.as_str()).expect("Failed creating C string");
return fs_fileExists(path_cstr.as_ptr());
}
}