use core::ptr;
use heapless;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StorageError {
InvalidStorage,
FileNotFound,
InsufficientSpace,
InvalidInput,
InvalidMagicNumber { expected: u32, found: u32 },
StorageFull,
StorageOverflow { available: usize, needed: usize },
}
pub type Result<T> = core::result::Result<T, StorageError>;
pub fn to_cstring(s: &str) -> Result<heapless::Vec<u8, 256>> {
let mut cstr = heapless::Vec::new();
cstr.extend_from_slice(s.as_bytes()).map_err(|_| StorageError::InvalidInput)?;
cstr.push(0).map_err(|_| StorageError::InvalidInput)?; Ok(cstr)
}
fn cstring_to_str(s: *const u8) -> Result<&'static str> {
unsafe {
let len = strlen(s);
let slice = core::slice::from_raw_parts(s, len);
match core::str::from_utf8(slice) {
Ok(str_ref) => Ok(str_ref),
Err(_) => Err(StorageError::InvalidInput),
}
}
}
unsafe fn strlen(s: *const u8) -> usize {
let mut len = 0;
let mut p = s;
while unsafe { *p != 0 } { len += 1;
p = unsafe { p.add(1) };
}
len
}
unsafe fn strcmp(s1: *const u8, s2: *const u8) -> bool {
let mut p1 = s1;
let mut p2 = s2;
while unsafe { *p1 != 0 && *p1 == *p2 } { p1 = unsafe { p1.add(1) };
p2 = unsafe { p2.add(1) };
}
unsafe { ((*p1 as i32) - (*p2 as i32)) == 0 }
}
#[cfg(target_os = "none")]
unsafe fn memcpy(dest: *mut u8, src: *const u8, n: usize) {
unsafe { ptr::copy_nonoverlapping(src, dest, n) }
}
#[cfg(target_os = "none")]
unsafe fn memmove(dest: *mut u8, src: *const u8, n: usize) {
unsafe { ptr::copy(src, dest, n) }
}
#[cfg(target_os = "none")]
unsafe fn memset(s: *mut u8, c: u8, n: usize) {
for i in 0..n {
unsafe { *s.add(i) = c };
}
}
#[cfg(target_os = "none")]
pub unsafe fn file_write_raw(filename: &str, content: &[u8]) -> Result<()> {
let filename_cstr = to_cstring(filename)?;
let filename_ptr = filename_cstr.as_ptr();
let filename_len = filename_cstr.len();
let content_ptr = content.as_ptr();
let content_len = content.len();
unsafe {
let free_pos = next_free();
if free_pos.is_null() {
return Err(StorageError::StorageFull);
}
let total_size = 2 + filename_len + content_len; let storage_end = (address() + size()) as usize;
let free_pos_usize = free_pos as usize;
let needed_end = free_pos_usize + total_size;
if needed_end > storage_end {
return Err(StorageError::StorageOverflow {
available: storage_end.saturating_sub(free_pos_usize),
needed: total_size,
});
}
let write_pos = free_pos as *mut u8;
ptr::write_unaligned(write_pos as *mut u16, total_size as u16);
let name_pos = write_pos.add(2);
memcpy(name_pos, filename_ptr, filename_len);
let content_pos = name_pos.add(filename_len);
memcpy(content_pos, content_ptr, content_len);
let cleanup_pos = content_pos.add(content_len);
let cleanup_size = ((address() + size()) as *mut u8).offset_from(cleanup_pos) as usize;
memset(cleanup_pos, 0, cleanup_size);
Ok(())
}
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_write_raw(_filename: &str, _content: &[u8]) -> Result<()> {
Ok(())
}
#[cfg(target_os = "none")]
pub unsafe fn file_read_raw(filename: &str) -> Result<(*const u8, usize)> {
let filename_cstr = to_cstring(filename)?;
let filename_ptr = filename_cstr.as_ptr();
unsafe {
let storage_addr = address();
let mut offset = (storage_addr as *mut u8).add(4); let end_addr = (storage_addr + size()) as *mut u8;
let magic_expected = 0xBADD0BEEu32.swap_bytes();
let magic_found = ptr::read_unaligned(storage_addr as *const u32);
if magic_found != magic_expected {
return Err(StorageError::InvalidMagicNumber {
expected: magic_expected,
found: magic_found
});
}
while offset < end_addr {
let size = ptr::read_unaligned(offset as *const u16);
if size == 0 { break; }
let name = offset.add(2);
if strcmp(name, filename_ptr) { let name_size = strlen(name) + 1;
let content_size = size as usize - 2 - name_size;
return Ok((offset.add(2 + name_size), content_size));
}
offset = offset.add(size as usize);
}
Err(StorageError::FileNotFound)
}
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_read_raw(_filename: &str) -> Result<(*const u8, usize)> {
Err(StorageError::InvalidStorage)
}
#[cfg(target_os = "none")]
pub fn file_exists(filename: &str) -> bool {
match unsafe { file_read_raw(filename) } {
Ok(_) => true,
Err(StorageError::FileNotFound) => false,
Err(_) => false,
}
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_exists(_filename: &str) -> bool {
false
}
#[cfg(target_os = "none")]
pub unsafe fn file_erase(filename: &str) -> Result<()> {
let filename_cstr = to_cstring(filename)?;
let filename_ptr = filename_cstr.as_ptr();
unsafe {
let storage_addr = address();
let mut offset = (storage_addr as *mut u8).add(4);
let end_addr = (storage_addr + size()) as *mut u8;
let magic_expected = 0xBADD0BEEu32.swap_bytes();
let magic_found = ptr::read_unaligned(storage_addr as *const u32);
if magic_found != magic_expected {
return Err(StorageError::InvalidMagicNumber {
expected: magic_expected,
found: magic_found
});
}
while offset < end_addr {
let size = ptr::read_unaligned(offset as *const u16);
if size == 0 { break; }
let name = offset.add(2);
if strcmp(name, filename_ptr) { let next_free_pos = next_free() as *mut u8;
let move_size = next_free_pos.offset_from(offset) as usize;
memmove(offset, offset.add(size as usize), move_size);
memset(next_free_pos.sub(size as usize), 0, size as usize);
return Ok(());
}
offset = offset.add(size as usize);
}
Err(StorageError::FileNotFound)
}
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_erase(_filename: &str) -> Result<()> {
Ok(())
}
#[cfg(target_os = "none")]
pub unsafe fn file_write_string(filename: &str, content: &str) -> Result<()> {
let content_cstr = to_cstring(content)?;
let content_bytes = content_cstr.as_slice();
unsafe { file_write_raw(filename, content_bytes) }
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_write_string(_filename: &str, _content: &str) -> Result<()> {
Ok(())
}
#[cfg(target_os = "none")]
pub unsafe fn file_read_string(filename: &str) -> Result<&'static str> {
let (content_ptr, content_len) = unsafe { file_read_raw(filename)? };
let content_slice = unsafe { core::slice::from_raw_parts(content_ptr, content_len) };
if content_slice.is_empty() || content_slice.last() != Some(&0) {
return Err(StorageError::InvalidInput);
}
let cstr_ptr = content_slice.as_ptr();
return cstring_to_str(cstr_ptr)
}
#[cfg(not(target_os = "none"))]
pub unsafe fn file_read_string(_filename: &str) -> Result<&'static str> {
Ok("Dummy content")
}
#[cfg(target_os = "none")]
unsafe fn address() -> u32 {
unsafe { ptr::read_unaligned((userland_address() + 0xC) as *const u32) }
}
#[cfg(target_os = "none")]
unsafe fn size() -> u32 {
unsafe { ptr::read_unaligned((userland_address() + 0x10) as *const u32) }
}
#[cfg(target_os = "none")]
unsafe fn next_free() -> *const u32 {
unsafe {
let storage_addr = address();
let mut offset = (storage_addr as *mut u8).add(4);
let end_addr = (storage_addr + size()) as *mut u8;
if is_valid(storage_addr as *const u32).is_err() { return ptr::null(); }
while offset < end_addr {
let size = ptr::read_unaligned(offset as *const u16);
if size == 0 { return offset as *const u32; }
offset = offset.add(size as usize);
}
end_addr as *const u32
}
}
#[cfg(target_os = "none")]
unsafe fn is_valid(addr: *const u32) -> Result<()> {
let magic_expected = 0xBADD0BEEu32.swap_bytes();
let magic_found = unsafe { ptr::read_unaligned(addr) };
if magic_found == magic_expected {
Ok(())
} else {
Err(StorageError::InvalidMagicNumber { expected: magic_expected, found: magic_found })
}
}
#[cfg(target_os = "none")]
unsafe fn userland_address() -> u32 {
unsafe {
let slots_n0110 = [0x90010000 as *const u32, 0x90410000 as *const u32];
let slots_n0120 = [0x90020000 as *const u32, 0x90420000 as *const u32];
let magic = 0xfeedc0deu32.swap_bytes();
let count_n0110 = slots_n0110.iter().filter(|&&slot| ptr::read_unaligned(slot) == magic).count();
let count_n0120 = slots_n0120.iter().filter(|&&slot| ptr::read_unaligned(slot) == magic).count();
let base_addr = if count_n0110 > count_n0120 {
ptr::read_unaligned(0x20000004 as *const u32).wrapping_add(0x10000) } else {
ptr::read_unaligned(0x24000004 as *const u32).wrapping_add(0x20000) };
base_addr.wrapping_sub(0x8)
}
}