// We need 64 * 32 bytes (2K) of SRAM for file indexes. To avoid corruption // issues we ignore the first file (32 bytes). // Note that the filename should include the null terminator if we want to use // strcmp. #define FILE_NAME_SIZE 27 #define FILE_CAPACITY 4 #define FILE_HEADER_OFFSET 2 #define FILE_INDEX_OFFSET 32 #define FILE_DATA_OFFSET KB(2) #define FILE_MAX_SIZE KB(16) #define SRAM ((vu8*)(MEM_CART)) typedef struct File { char name[FILE_NAME_SIZE + 1]; u16 size; u16 mem_offset; // NOTE: Unused... } File; // The filesystem header. typedef struct FileSystem { u16 num_files; u16 data_size; // NOTE: Unused... u16 data_capacity; // NOTE: Unused... File files[FILE_CAPACITY]; } FileSystem; static FileSystem filesystem; void _fs_read(u8 *dst, size_t pos, size_t n_bytes) { for (size_t i = 0; i < n_bytes; ++i) { dst[i] = SRAM[pos + i]; } } void _fs_write(u8 *src, size_t pos, size_t n_bytes) { for (size_t i = 0; i < n_bytes; ++i) { SRAM[pos + i] = src[i]; } } void fs_init() { // Load header if existing. _fs_read(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); if (filesystem.num_files == 0xFFFF && filesystem.data_capacity == 0xFFFF && filesystem.data_size == 0xFFFF) { // Clear SRAM. for (size_t i = 0; i < KB(64); ++i) { SRAM[i] = 0; } // Initialize filesystem. filesystem.num_files = 0; filesystem.data_size = 0; filesystem.data_capacity = (u16)(FILE_CAPACITY * FILE_MAX_SIZE); memset(&filesystem.files, 0, FILE_CAPACITY * sizeof(File)); _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); } else { _fs_read(&filesystem.files, FILE_INDEX_OFFSET, sizeof(File) * filesystem.num_files); } } typedef enum { OPEN_READ, OPEN_WRITE, } OpenMode; int fs_open_file(char *name, OpenMode mode) { // Try to find an existing file. for (size_t i = 0; i < filesystem.num_files; ++i) { // TODO: Replace strcmp with vectorized fixed size char comparison. if (strcmp(name, filesystem.files[i].name) == 0) { return i; } } if (mode == OPEN_READ) { return -1; } // Create a new file if there is space. if (filesystem.num_files < FILE_CAPACITY) { size_t index = filesystem.num_files++; size_t k = 0; while(*name) { filesystem.files[index].name[k++] = *name++; } filesystem.files[index].size = 0; filesystem.files[index].mem_offset = 0; // Update file index. _fs_write(&filesystem.files[index], FILE_INDEX_OFFSET + index * sizeof(File), sizeof(File)); // Update header. _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); return index; } return -1; } size_t fs_write(u8 *src, size_t n_bytes, u16 file_index, u16 offset, bool append) { File *file = &filesystem.files[file_index]; // Check if there is enough capacity for this write operation. if (offset + n_bytes >= FILE_MAX_SIZE) { return 0; } // Write data to file block. _fs_write(src, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes); // Update file index. if (append) { if (offset + n_bytes > file->size) { file->size = offset + n_bytes; } } else { file->size = offset + n_bytes; } _fs_write(file, FILE_INDEX_OFFSET + file_index * sizeof(File), sizeof(File)); // Update header. _fs_write(&filesystem, FILE_HEADER_OFFSET, offsetof(FileSystem, files)); return n_bytes; } size_t fs_read(u8 *dst, size_t n_bytes, u16 file_index, u16 offset) { File *file = &filesystem.files[file_index]; // Check if the offset is within limits. if (file->size == 0 || offset >= file->size - 1) { return 0; } // Read as much as we can. if (offset + n_bytes > file->size) { n_bytes = file->size - offset; } // Copy n_bytes to destination. _fs_read(dst, FILE_DATA_OFFSET + FILE_MAX_SIZE * file_index + offset, n_bytes); return n_bytes; }