From f77f6d86ce1b75cb47edec701dcda1d3410e0b98 Mon Sep 17 00:00:00 2001 From: IonutParau Date: Wed, 28 May 2025 18:10:56 +0200 Subject: [PATCH] filesystem vtable --- build.zig | 1 + src/components/eeprom.c | 6 +- src/components/filesystem.c | 422 ++++++++++++++++++++++++++++++++++++ src/emulator.c | 5 +- src/neonucleus.h | 35 +-- src/universe.c | 1 + 6 files changed, 452 insertions(+), 18 deletions(-) create mode 100644 src/components/filesystem.c diff --git a/build.zig b/build.zig index 75d2544..d13ff59 100644 --- a/build.zig +++ b/build.zig @@ -14,6 +14,7 @@ fn addEngineSources(c: *std.Build.Step.Compile) void { "src/universe.c", // components "src/components/eeprom.c", + "src/components/filesystem.c", }, }); } diff --git a/src/components/eeprom.c b/src/components/eeprom.c index 296ce35..5f23c94 100644 --- a/src/components/eeprom.c +++ b/src/components/eeprom.c @@ -8,7 +8,7 @@ void nn_eeprom_destroy(void *_, nn_component *component, nn_eeprom *eeprom) { if(!nn_decRef(&eeprom->refc)) return; if(eeprom->deinit == NULL) { - eeprom->deinit(component, eeprom); + eeprom->deinit(component, eeprom->userdata); } } @@ -68,7 +68,7 @@ void nn_eeprom_setLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_ nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); nn_busySleep(control.writeLatency); nn_removeEnergy(computer, control.writeEnergyCost); - nn_removeHeat(computer, control.writeHeatCost); + nn_addHeat(computer, control.writeHeatCost); nn_callCost(computer, control.writeCost); } @@ -104,7 +104,7 @@ void nn_eeprom_set(nn_eeprom *eeprom, void *_, nn_component *component, nn_compu nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); nn_busySleep(control.writeLatency); nn_removeEnergy(computer, control.writeEnergyCost); - nn_removeHeat(computer, control.writeHeatCost); + nn_addHeat(computer, control.writeHeatCost); nn_callCost(computer, control.writeCost); } diff --git a/src/components/filesystem.c b/src/components/filesystem.c new file mode 100644 index 0000000..e873a82 --- /dev/null +++ b/src/components/filesystem.c @@ -0,0 +1,422 @@ +#include "../neonucleus.h" +#include + +void nn_fs_destroy(void *_, nn_component *component, nn_filesystem *fs) { + if(!nn_decRef(&fs->refc)) return; + + if(fs->deinit == NULL) { + fs->deinit(component, fs->userdata); + } +} + +bool nn_fs_illegalPath(const char *path) { + // absolute disaster + const char *illegal = "\"\\:*?<>|"; + + for(size_t i = 0; illegal[i] != '\0'; i++) { + if(strchr(path, illegal[i]) != NULL) return true; + } + return false; +} + +nn_filesystemControl nn_fs_getControl(nn_component *component, nn_filesystem *fs) { + return fs->control(component, fs->userdata); +} + +size_t nn_fs_countChunks(nn_filesystem *fs, size_t bytes, nn_component *component) { + nn_filesystemControl control = nn_fs_getControl(component, fs); + + size_t chunks = bytes / control.pretendChunkSize; + if(bytes % control.pretendChunkSize != 0) chunks++; + return chunks; +} + +void nn_fs_readCost(nn_filesystem *fs, size_t count, nn_component *component, nn_computer *computer) { + nn_filesystemControl control = nn_fs_getControl(component, fs); + nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); + nn_busySleep(control.readLatencyPerChunk * count); + nn_removeEnergy(computer, control.readEnergyCost * count); + nn_callCost(computer, control.readCostPerChunk * count); +} + +void nn_fs_writeCost(nn_filesystem *fs, size_t count, nn_component *component, nn_computer *computer) { + nn_filesystemControl control = nn_fs_getControl(component, fs); + nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); + nn_busySleep(control.writeLatencyPerChunk * count); + nn_removeEnergy(computer, control.writeEnergyCost * count); + nn_addHeat(computer, control.writeHeatPerChunk * count); + nn_callCost(computer, control.writeCostPerChunk * count); +} + +void nn_fs_seekCost(nn_filesystem *fs, size_t count, nn_component *component, nn_computer *computer) { + nn_filesystemControl control = nn_fs_getControl(component, fs); + double seekLatency = ((double)control.pretendRPM / 60) * control.pretendChunkSize / fs->spaceTotal(component, fs->userdata); + + nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); + nn_busySleep(seekLatency * count); + nn_removeEnergy(computer, control.writeEnergyCost * count); + nn_addHeat(computer, control.writeHeatPerChunk * count); + nn_callCost(computer, control.writeCostPerChunk * count); +} + +void nn_fs_getLabel(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + char buf[NN_LABEL_SIZE]; + size_t l = NN_LABEL_SIZE; + fs->getLabel(component, fs->userdata, buf, &l); + if(l == 0) { + nn_return(computer, nn_values_nil()); + } else { + nn_return(computer, nn_values_string(buf, l)); + } + + // Latency, energy costs and stuff + nn_filesystemControl control = nn_fs_getControl(component, fs); + nn_randomLatency(control.randomLatencyMin, control.randomLatencyMax); + nn_busySleep(control.readLatencyPerChunk); + nn_removeEnergy(computer, control.readEnergyCost); + nn_callCost(computer, control.readCostPerChunk); +} + +void nn_fs_setLabel(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + size_t l = 0; + nn_value label = nn_getArgument(computer, 0); + const char *buf = nn_toString(label, &l); + if(buf == NULL) { + nn_setCError(computer, "bad label (string expected)"); + return; + } + l = fs->setLabel(component, fs->userdata, buf, l); + nn_return(computer, nn_values_string(buf, l)); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_spaceUsed(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + size_t space = fs->spaceUsed(component, fs->userdata); + nn_return(computer, nn_values_integer(space)); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_spaceTotal(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + size_t space = fs->spaceUsed(component, fs->userdata); + nn_return(computer, nn_values_integer(space)); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_isReadOnly(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_return(computer, nn_values_boolean(fs->isReadOnly(component, fs->userdata))); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_size(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + size_t byteSize = fs->size(component, fs->userdata, path); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_remove(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + nn_return(computer, nn_values_boolean(fs->remove(component, fs->userdata, path))); + + // Considered 1 safety check + the actual write + nn_fs_readCost(fs, 1, component, computer); + nn_fs_writeCost(fs, 1, component, computer); +} + +void nn_fs_lastModified(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + nn_return(computer, nn_values_integer(fs->remove(component, fs->userdata, path))); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_rename(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value fromValue = nn_getArgument(computer, 0); + const char *from = nn_toCString(fromValue); + if(from == NULL) { + nn_setCError(computer, "bad path #1 (string expected)"); + return; + } + if(nn_fs_illegalPath(from)) { + nn_setCError(computer, "bad path #1 (illegal path)"); + return; + } + + nn_value toValue = nn_getArgument(computer, 0); + const char *to = nn_toCString(toValue); + if(to == NULL) { + nn_setCError(computer, "bad path #2 (string expected)"); + return; + } + if(nn_fs_illegalPath(to)) { + nn_setCError(computer, "bad path #2 (illegal path)"); + return; + } + + size_t movedCount = fs->rename(component, fs->userdata, from, to); + nn_return(computer, nn_values_boolean(movedCount > 0)); + + // Considered 2 safety checks + 1 read per file + 1 write per file + nn_fs_readCost(fs, 2 + movedCount, component, computer); + nn_fs_writeCost(fs, movedCount, component, computer); +} + +void nn_fs_exists(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + nn_return(computer, nn_values_boolean(fs->exists(component, fs->userdata, path))); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_isDirectory(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + nn_return(computer, nn_values_boolean(fs->isDirectory(component, fs->userdata, path))); + + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_makeDirectory(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + nn_return(computer, nn_values_boolean(fs->makeDirectory(component, fs->userdata, path))); + + nn_fs_readCost(fs, 1, component, computer); + nn_fs_writeCost(fs, 1, component, computer); +} + +void nn_fs_list(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + size_t fileCount = 0; + char **files = fs->list(component, fs->userdata, path, &fileCount); + + if(files != NULL) { + // operation succeeded + nn_value arr = nn_values_array(fileCount); + for(size_t i = 0; i < fileCount; i++) { + nn_values_set(arr, i, nn_values_cstring(files[i])); + nn_free(files[i]); + } + nn_free(files); + nn_return(computer, arr); + } + + nn_fs_readCost(fs, 1 + fileCount, component, computer); +} + +void nn_fs_open(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value pathValue = nn_getArgument(computer, 0); + const char *path = nn_toCString(pathValue); + if(path == NULL) { + nn_setCError(computer, "bad path (string expected)"); + return; + } + if(nn_fs_illegalPath(path)) { + nn_setCError(computer, "bad path (illegal path)"); + return; + } + + nn_value modeValue = nn_getArgument(computer, 1); + const char *mode = nn_toCString(modeValue); + if(mode == NULL) { + mode = "r"; + } + + size_t fd = fs->open(component, fs->userdata, path, mode); + nn_return(computer, nn_values_integer(fd)); + + // 1 safety check + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_close(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value fdValue = nn_getArgument(computer, 0); + size_t fd = nn_toInt(fdValue); + + bool closed = fs->close(component, fs->userdata, fd); + nn_return(computer, nn_values_boolean(closed)); + + // do not ask where it comes from, balance is hard + nn_fs_readCost(fs, 1, component, computer); +} + +void nn_fs_write(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value fdValue = nn_getArgument(computer, 0); + size_t fd = nn_toInt(fdValue); + + size_t spaceRemaining = fs->spaceTotal(component, fs->userdata) - fs->spaceUsed(component, fs->userdata); + + nn_value bufferValue = nn_getArgument(computer, 1); + size_t len = 0; + const char *buf = nn_toString(bufferValue, &len); + if(buf == NULL) { + nn_setCError(computer, "bad buffer (string expected)"); + return; + } + + bool closed = fs->write(component, fs->userdata, fd, buf, len); + nn_return(computer, nn_values_boolean(closed)); + + // do not ask where it comes from, balance is hard + nn_fs_writeCost(fs, nn_fs_countChunks(fs, len, component), component, computer); + nn_fs_seekCost(fs, nn_fs_countChunks(fs, len, component), component, computer); +} + +void nn_fs_read(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + nn_value fdValue = nn_getArgument(computer, 0); + size_t fd = nn_toInt(fdValue); + + nn_value lenValue = nn_getArgument(computer, 1); + double len = nn_toNumber(lenValue); + size_t capacity = fs->spaceTotal(component, fs->userdata); + if(len > capacity) len = capacity; + size_t byteLen = len; + + char *buf = nn_malloc(byteLen); + if(buf == NULL) { + nn_setCError(computer, "out of memory"); + return; + } + + size_t readLen = fs->read(component, fs->userdata, fd, buf, byteLen); + if(readLen > 0) { + // Nothing read means EoF. + nn_return(computer, nn_values_string(buf, readLen)); + } + nn_free(buf); + + // do not ask where it comes from, balance is hard + nn_fs_readCost(fs, nn_fs_countChunks(fs, readLen, component), component, computer); + nn_fs_seekCost(fs, nn_fs_countChunks(fs, readLen, component), component, computer); +} + +bool nn_fs_validWhence(const char *s) { + return + strcmp(s, "set") == 0 || + strcmp(s, "cur") == 0 || + strcmp(s, "end") == 0; +} + +void nn_fs_seek(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { + size_t fd = nn_toInt(nn_getArgument(computer, 0)); + + const char *whence = nn_toCString(nn_getArgument(computer, 1)); + + int off = nn_toInt(nn_getArgument(computer, 2)); + + if(whence == NULL) { + nn_setCError(computer, "bad whence (string expected)"); + return; + } + + if(!nn_fs_validWhence(whence)) { + nn_setCError(computer, "bad whence"); + return; + } + + size_t capacity = fs->spaceTotal(component, fs->userdata); + int moved = 0; + + size_t pos = fs->seek(component, fs->userdata, fd, whence, off, &moved); + if(moved < 0) moved = -moved; + + // do not ask where it comes from, balance is hard + nn_fs_readCost(fs, 1, component, computer); + nn_fs_seekCost(fs, nn_fs_countChunks(fs, moved, component), component, computer); +} + +void nn_loadFilesystemTable(nn_universe *universe) { + nn_componentTable *fsTable = nn_newComponentTable("filesystem", NULL, NULL, (void *)nn_fs_destroy); + nn_storeUserdata(universe, "NN:FILESYSTEM", fsTable); + + nn_defineMethod(fsTable, "getLabel", false, (void *)nn_fs_getLabel, NULL, "getLabel(): string - Returns the label of the filesystem."); + nn_defineMethod(fsTable, "setLabel", false, (void *)nn_fs_setLabel, NULL, "setLabel(label: string): string - Sets a new label for the filesystem and returns the new label of the filesystem, which may have been truncated."); + nn_defineMethod(fsTable, "spaceUsed", false, (void *)nn_fs_spaceUsed, NULL, "spaceUsed(): integer - Returns the amounts of bytes used."); + nn_defineMethod(fsTable, "spaceTotal", false, (void *)nn_fs_spaceTotal, NULL, "spaceTotal(): integer - Returns the capacity of the filesystem."); + nn_defineMethod(fsTable, "isReadOnly", true, (void *)nn_fs_isReadOnly, NULL, "isReadOnly(): boolean - Returns whether the filesystem is in read-only mode."); + nn_defineMethod(fsTable, "size", true, (void *)nn_fs_size, NULL, "size(path: string): integer - Gets the size, in bytes, of a file."); + nn_defineMethod(fsTable, "remove", true, (void *)nn_fs_remove, NULL, "remove(path: string): boolean - Removes a file. Returns whether the operation succeeded."); + nn_defineMethod(fsTable, "lastModified", true, (void *)nn_fs_lastModified, NULL, "remove(path: string): boolean - Removes a file. Returns whether the operation succeeded."); + nn_defineMethod(fsTable, "rename", true, (void *)nn_fs_rename, NULL, "rename(from: string, to: string): boolean - Moves files from one path to another."); + nn_defineMethod(fsTable, "exists", true, (void *)nn_fs_exists, NULL, "exists(path: string): boolean - Checks whether a file exists."); + nn_defineMethod(fsTable, "isDirectory", true, (void *)nn_fs_isDirectory, NULL, "isDirectory(path: string): boolean - Returns whether a file is actually a directory."); + nn_defineMethod(fsTable, "makeDirectory", true, (void *)nn_fs_makeDirectory, NULL, "makeDirectory(path: string): boolean - Creates a new directory at the given path. Returns whether it succeeded."); + nn_defineMethod(fsTable, "list", true, (void *)nn_fs_list, NULL, "list(path: string): string[] - Returns a list of file paths. Directories will have a / after them"); + nn_defineMethod(fsTable, "open", true, (void *)nn_fs_open, NULL, "open(path: string[, mode: string = \"r\"]): integer - Opens a file, may create it."); + nn_defineMethod(fsTable, "close", true, (void *)nn_fs_close, NULL, "close(fd: integer): boolean - Closes a file."); + nn_defineMethod(fsTable, "write", true, (void *)nn_fs_write, NULL, "write(fd: integer, data: string): boolean - Writes data to a file."); + nn_defineMethod(fsTable, "read", true, (void *)nn_fs_read, NULL, "read(fd: integer, len: number): string - Reads bytes from a file. Infinity is a valid length, in which case it reads as much as possible."); + nn_defineMethod(fsTable, "seek", true, (void *)nn_fs_seek, NULL, "seek(fd: integer, whence: string, offset: integer): integer - Seeks a file. Returns the new position. Valid whences are set, cur and end."); +} diff --git a/src/emulator.c b/src/emulator.c index f8c68a0..b808890 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -110,7 +110,10 @@ int main() { // remove some heat per second nn_removeHeat(computer, dt * (rand() % 12)); - if(nn_isOverheating(computer)) continue; + if(nn_isOverheating(computer)) { + printf("Machine overheating.\n"); + continue; + } int state = nn_tickComputer(computer); if(state == NN_STATE_SWITCH) { diff --git a/src/neonucleus.h b/src/neonucleus.h index 3b7dc5e..51a1a6d 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -347,6 +347,7 @@ void nn_loadCoreComponentTables(nn_universe *universe); // loading each component void nn_loadEepromTable(nn_universe *universe); void nn_loadFilesystemTable(nn_universe *universe); +void nn_loadDriveTable(nn_universe *universe); // the helpers @@ -395,10 +396,8 @@ typedef struct nn_filesystemControl { // speed // used to calculate the latency of seeking a file. It will treat the file as continuous within the storage medium, which is completely - // unrealistic. Essentially, after a seek, it will check how much the file pointer was changed. If it went backwards, it will pretend - // the whole drive had to spin. You can imagine the drive spinning counter-clockwise. - // Seeks are assumed to be *chunked* too, so seeking within a chunk, even backwards, won't actually do anything. - // Set it to 0 to disable seek latency. + // unrealistic. Essentially, after a seek, it will check how much the file pointer was changed. If it went backwards, the drive spins + // in the opposite direction. Drives work the same way. int pretendRPM; double readLatencyPerChunk; double writeLatencyPerChunk; @@ -408,13 +407,19 @@ typedef struct nn_filesystemControl { double randomLatencyMax; // thermals - double motorHeat; // this times how many chunks have been seeked will be the heat addres, +/- the heat range. - double heatRange; + double motorHeat; // this times how many chunks have been seeked will be the heat addres, +/- the motor heat range. + double motorHeatRange; + double writeHeatPerChunk; // call budget size_t readCostPerChunk; size_t writeCostPerChunk; size_t seekCostPerChunk; + + // energy cost + double readEnergyCost; + double writeEnergyCost; + double motorEnergyCost; } nn_filesystemControl; typedef struct nn_filesystem { @@ -434,7 +439,7 @@ typedef struct nn_filesystem { size_t (*size)(nn_component *component, void *userdata, const char *path); bool (*remove)(nn_component *component, void *userdata, const char *path); size_t (*lastModified)(nn_component *component, void *userdata, const char *path); - bool (*rename)(nn_component *component, void *userdata, const char *from, const char *to); + size_t (*rename)(nn_component *component, void *userdata, const char *from, const char *to); bool (*exists)(nn_component *component, void *userdata, const char *path); // directory operations @@ -447,10 +452,11 @@ typedef struct nn_filesystem { // file operations size_t (*open)(nn_component *component, void *userdata, const char *path, const char *mode); - void (*close)(nn_component *component, void *userdata, int fd); - size_t (*seek)(nn_component *component, void *userdata, int fd, int whence, int off); + bool (*close)(nn_component *component, void *userdata, int fd); + bool (*write)(nn_component *component, void *userdata, int fd, const char *buf, size_t len); size_t (*read)(nn_component *component, void *userdata, int fd, char *buf, size_t required); - bool (*write)(nn_component *component, void *userdata, int fd, char *buf, size_t len); + // moved is an out pointer that says how many bytes the pointer moved. + size_t (*seek)(nn_component *component, void *userdata, int fd, const char *whence, int off, int *moved); } nn_filesystem; nn_filesystem *nn_volatileFileSystem(size_t capacity, nn_filesystemControl *control); @@ -468,7 +474,8 @@ typedef struct nn_driveControl { double randomLatencyMax; double motorHeat; - double heatRange; + double motorHeatRange; + double writeHeatPerSector; // These are per sector double motorEnergyCost; @@ -476,9 +483,9 @@ typedef struct nn_driveControl { double writeEnergyCost; // call budget - size_t readCostPerChunk; - size_t writeCostPerChunk; - size_t seekCostPerChunk; + size_t readCostPerSector; + size_t writeCostPerSector; + size_t seekCostPerSector; } nn_driveControl; typedef struct nn_drive { diff --git a/src/universe.c b/src/universe.c index d8cfb4b..abfe736 100644 --- a/src/universe.c +++ b/src/universe.c @@ -46,4 +46,5 @@ double nn_getTime(nn_universe *universe) { void nn_loadCoreComponentTables(nn_universe *universe) { nn_loadEepromTable(universe); + nn_loadFilesystemTable(universe); }