#include "nds.h"
#include "fat.h"
#include "file.h"
static bool FS_ENABLE = false;
static u8 buffer[255];
void init_filesystem() {
FS_ENABLE = fatInitDefault();
}
bool filesystem_enabled() {
return FS_ENABLE;
}
void fs_close(FileDevice *fs) {
fclose(fs->file);
fs->file = NULL;
closedir(fs->dir);
fs->dir = NULL;
fs->open = false;
fs->success = false;
fs->is_dir = false;
fs->child_is_dir = false;
pb_clear(&fs->path);
pb_clear(&fs->child_path);
fs->pointer = 0;
fs->length = 0;
fs->child = NULL;
fs->dir_i = 0;
}
void fs_clear(FileDevice *fs) {
pb_clear(&fs->entry);
pb_clear(&fs->action);
}
void fs_open_entry(FileDevice* fs, u8 *path) {
fs->success = false;
// Prevent non-absolute paths from being opened.
if (path[0] != '/') {
return;
}
// Remove trailing slash if any, without removing leading slash.
for (int i=2; i<255; i++) {
if (path[i] == 0) {
if (path[i-1] == '/') {
path[i-1] = 0;
}
break;
}
}
// Prevent paths with relative components from being opened.
for (int i=0; i<255; i++) {
if (path[i] == '/') {
if (path[i+1]=='.') {
// Check for '.' components.
if (path[i+2]=='/' || path[i+2]==0) {
return;
}
// Check for '..' components.
if (path[i+2]=='.' && (path[i+3]=='/' || path[i+3]==0)) {
return;
}
}
} else if (path[i] == 0) {
break;
}
}
DIR *tmpdir = opendir((char*) path);
if (tmpdir) {
fs_close(fs);
fs->dir = tmpdir;
fs->open = true;
fs->success = true;
fs->is_dir = true;
fs->dir_i = 0;
// Find length of directory
while ((fs->child = readdir(fs->dir))) {
fs->dir_i++;
};
fs->length = fs->dir_i;
fs_select_child(fs, 0);
pb_populate(&fs->path, path);
return;
}
// MEMORY LEAK HERE ONLY
// for (int i=0; i<500; i++) {
// fs->tmpfile = fopen((char*) path, "r+");
// fclose(fs->tmpfile);
// }
// END OF MEMORY LEAK CODE
// FILE CODE MUST COME AFTER DIR CODE TO AVOID MEMORY LEAK
FILE *tmpfile = fopen((char*) path, "r+");
if (tmpfile) {
fs_close(fs);
fs->file = tmpfile;
fs->open = true;
fs->success = true;
fs->is_dir = false;
// TODO: Only read length when we need to. Keep a bool around.
// fseek(fs->file, 0, SEEK_END);
// fs->length = ftell(fs->file);
// rewind(fs->file);
pb_populate(&fs->path, path);
return;
}
}
void fs_select_child(FileDevice *fs, u32 pointer) {
fs->pointer = pointer;
rewinddir(fs->dir);
fs->dir_i = 0;
for (fs->dir_i=0; fs->dir_i<pointer+1; fs->dir_i++) {
fs->child = readdir(fs->dir);
};
if (fs->child) {
pb_populate(&fs->child_path, (u8*) &fs->child->d_name);
fs->child_is_dir = fs->child->d_type == DT_DIR;
} else {
pb_clear(&fs->child_path);
fs->child_is_dir = false;
}
}
void fs_push_entry(FileDevice *fs, u8 byte) {
if (pb_push_byte(&fs->entry, byte)) {
fs_close(fs);
if (pb_is_terminated(&fs->entry) && fs->entry.mem[0] != 0) {
fs_open_entry(fs, &fs->entry.mem[0]);
}
fs_clear(fs);
}
}
void fs_push_action(FileDevice *fs, u8 byte) {
if (pb_push_byte(&fs->action, byte)) {
if (!pb_is_terminated(&fs->action)) return;
bool action = fs->action.mem[0] != 0;
if (fs->open) {
if (action) {
fs->success = rename((char*) fs->path.mem, (char*) fs->action.mem);
} else {
// TODO: DELETE
}
} else if (action) {
FILE *tmpfile = fopen((char*) fs->action.mem, "w");
fs->success = tmpfile != NULL;
fclose(tmpfile);
}
fs_close(fs);
fs_clear(fs);
}
}
u8 fs_read_byte(FileDevice *fs) {
return fgetc(fs->file);
}
void fs_write_byte(FileDevice *fs, u8 byte) {
fputc(byte, fs->file);
}
void fs_ascend(FileDevice *fs) {
fs->success = false;
if (fs->open) {
// Don't ascend if we're already at the root.
if (fs->path.mem[1] == 0) {
return;
}
// Path is guaranteed to have no trailing slash.
memcpy(&buffer, &fs->path.mem[0], 255);
u8 slash_i = 0;
for (int i=0; i<255; i++) {
if (buffer[i] == '/') {
slash_i = i;
} else if (buffer[i] == 0) {
// Trim to leave the slash
buffer[slash_i+1] = 0;
break;
}
}
fs_open_entry(fs, &buffer[0]);
} else {
fs_open_entry(fs, (u8*) "/");
}
}
void fs_descend(FileDevice *fs) {
fs->success = false;
if (!(fs->open && fs->is_dir && fs->child)) {
return;
}
memcpy(&buffer, &fs->path.mem[0], 255);
// Find location of terminating null in buffer.
int start = 0;
for (int i=0; i<255; i++) {
start = i;
if (buffer[i] == 0) break;
}
// Find length of child name.
int len = 0;
for (int i=0; i<255; i++) {
len = i;
if (fs->child->d_name[i] == 0) break;
}
// Check that buffer has capacity for child name.
if (start+len > 255) {
fs->success = false;
return;
}
// Add a slash if not at root.
if (start != 1) {
buffer[start] = '/';
start++;
}
// Copy child name into buffer.
for (int i=0; i<len; i++) {
buffer[start+i] = fs->child->d_name[i];
}
fs_open_entry(fs, &buffer[0]);
}
void fs_seek(FileDevice *fs) {
if (fs->open) {
if (fs->is_dir) {
fs_select_child(fs, fs->pointer_write);
} else {
fseek(fs->file, fs->pointer_write, SEEK_SET);
fs->pointer = ftell(fs->file);
}
}
}
void fs_resize(FileDevice *fs) {
if (fs->open && !fs->is_dir) {
// truncate(fs->file);
// TODO
}
}