#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; fs->at_root = false; } 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++; }; // If this is not the root directory, reduce len by 2 for . and .. dirs. if (path[1] == 0) { fs->at_root = true; } else { fs->dir_i -= 2; } 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) { // Increment past the . and .. directories. if (!fs->at_root) { pointer += 2; } fs->pointer = pointer; rewinddir(fs->dir); for (fs->dir_i=0; fs->dir_idir_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; ichild->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 } }