huge progress on ncomplib
This commit is contained in:
@@ -23,7 +23,7 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
|
|||||||
dataMod.addCSourceFiles(.{
|
dataMod.addCSourceFiles(.{
|
||||||
.files = &[_][]const u8{
|
.files = &[_][]const u8{
|
||||||
"src/neonucleus.c",
|
"src/neonucleus.c",
|
||||||
"src/ncomplib.h",
|
"src/ncomplib.c",
|
||||||
},
|
},
|
||||||
.flags = &.{
|
.flags = &.{
|
||||||
if (opts.baremetal) "-DNN_BAREMETAL" else "",
|
if (opts.baremetal) "-DNN_BAREMETAL" else "",
|
||||||
|
|||||||
65
src/main.c
65
src/main.c
@@ -4,6 +4,7 @@
|
|||||||
// Error handling has been omitted in most places.
|
// Error handling has been omitted in most places.
|
||||||
|
|
||||||
#include "neonucleus.h"
|
#include "neonucleus.h"
|
||||||
|
#include "ncomplib.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -376,34 +377,14 @@ int main(int argc, char **argv) {
|
|||||||
.isReadonly = false,
|
.isReadonly = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
printf("%zu bytes logically used by OpenOS\n", ncl_spaceUsedIn(ncl_defaultFS, "data/OpenOS"));
|
||||||
|
printf("%zu bytes physically used by OpenOS\n", ncl_spaceUsedBy(ncl_defaultFS, "data/OpenOS"));
|
||||||
|
|
||||||
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]);
|
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]);
|
||||||
|
|
||||||
size_t ramTotal = 0;
|
size_t ramTotal = 0;
|
||||||
ramTotal += nn_ramSizes[5];
|
ramTotal += nn_ramSizes[5];
|
||||||
|
|
||||||
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
|
|
||||||
if(showStats) {
|
|
||||||
// collects stats
|
|
||||||
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
|
|
||||||
}
|
|
||||||
|
|
||||||
// default for 64-bit, we just assume we're 64-bit.
|
|
||||||
nn_setMemoryScale(c, 1.8);
|
|
||||||
|
|
||||||
nn_setArchitecture(c, &arch);
|
|
||||||
nn_addSupportedArchitecture(c, &arch);
|
|
||||||
|
|
||||||
nn_mountComponent(c, ocelotCard, -1);
|
|
||||||
nn_mountComponent(c, eepromCard, 0);
|
|
||||||
|
|
||||||
const char *driveData = "error('unmanaged drive')";
|
|
||||||
nn_VDrive vdrive = {
|
|
||||||
.data = driveData,
|
|
||||||
.datalen = strlen(driveData),
|
|
||||||
.label = "",
|
|
||||||
.labellen = 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
SetExitKey(KEY_NULL);
|
SetExitKey(KEY_NULL);
|
||||||
|
|
||||||
Font font = LoadFont("unscii-16-full.ttf");
|
Font font = LoadFont("unscii-16-full.ttf");
|
||||||
@@ -413,11 +394,38 @@ int main(int argc, char **argv) {
|
|||||||
tickDelay = atof(getenv("NN_TICKDELAY"));
|
tickDelay = atof(getenv("NN_TICKDELAY"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *driveData = "error('unmanaged drive')";
|
||||||
|
nn_VDrive vdrive = {
|
||||||
|
.data = driveData,
|
||||||
|
.datalen = strlen(driveData),
|
||||||
|
.label = "",
|
||||||
|
.labellen = 0,
|
||||||
|
};
|
||||||
|
|
||||||
struct {int key; nn_codepoint unicode;} keybuf[512];
|
struct {int key; nn_codepoint unicode;} keybuf[512];
|
||||||
memset(keybuf, 0, sizeof(keybuf));
|
memset(keybuf, 0, sizeof(keybuf));
|
||||||
size_t keycap = sizeof(keybuf) / sizeof(keybuf[0]);
|
size_t keycap = sizeof(keybuf) / sizeof(keybuf[0]);
|
||||||
|
|
||||||
double nextTick = 0;
|
double nextTick = 0;
|
||||||
|
double nextSecond = 0;
|
||||||
|
double wattage = 0;
|
||||||
|
|
||||||
|
|
||||||
|
restart:;
|
||||||
|
nn_Computer *c = nn_createComputer(u, NULL, "computer0", ramTotal, 256, 256);
|
||||||
|
if(showStats) {
|
||||||
|
// collects stats
|
||||||
|
nn_setEnergyHandler(c, NULL, ne_energy_accumulator);
|
||||||
|
}
|
||||||
|
|
||||||
|
// default for 64-bit
|
||||||
|
if(sizeof(void *) > 4) nn_setMemoryScale(c, 1.8);
|
||||||
|
|
||||||
|
nn_setArchitecture(c, &arch);
|
||||||
|
nn_addSupportedArchitecture(c, &arch);
|
||||||
|
|
||||||
|
nn_mountComponent(c, ocelotCard, -1);
|
||||||
|
nn_mountComponent(c, eepromCard, 0);
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
if(WindowShouldClose()) break;
|
if(WindowShouldClose()) break;
|
||||||
@@ -431,8 +439,6 @@ int main(int argc, char **argv) {
|
|||||||
statY += 20;
|
statY += 20;
|
||||||
}
|
}
|
||||||
if(showStats) {
|
if(showStats) {
|
||||||
double wattage = accumulatedEnergyCost;
|
|
||||||
if(tickDelay > 0) wattage /= tickDelay;
|
|
||||||
double memUsagePercent = (double)nn_getUsedMemory(c) * 100 / nn_getTotalMemory(c);
|
double memUsagePercent = (double)nn_getUsedMemory(c) * 100 / nn_getTotalMemory(c);
|
||||||
DrawText(TextFormat("power usage: %.2f W", wattage), 10, statY, 20, GREEN);
|
DrawText(TextFormat("power usage: %.2f W", wattage), 10, statY, 20, GREEN);
|
||||||
statY += 20;
|
statY += 20;
|
||||||
@@ -481,6 +487,12 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
double tickNow = GetTime();
|
double tickNow = GetTime();
|
||||||
|
|
||||||
|
if(tickNow >= nextSecond) {
|
||||||
|
nextSecond = tickNow + 1;
|
||||||
|
wattage = accumulatedEnergyCost;
|
||||||
|
accumulatedEnergyCost = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if(tickNow >= nextTick) {
|
if(tickNow >= nextTick) {
|
||||||
accumulatedEnergyCost = 0;
|
accumulatedEnergyCost = 0;
|
||||||
nextTick = tickNow + tickDelay;
|
nextTick = tickNow + tickDelay;
|
||||||
@@ -511,7 +523,8 @@ int main(int argc, char **argv) {
|
|||||||
}
|
}
|
||||||
if(state == NN_RESTART) {
|
if(state == NN_RESTART) {
|
||||||
printf("restart requested\n");
|
printf("restart requested\n");
|
||||||
goto cleanup;
|
nn_destroyComputer(c);
|
||||||
|
goto restart;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
476
src/ncomplib.c
476
src/ncomplib.c
@@ -1,8 +1,226 @@
|
|||||||
#include "neonucleus.h"
|
#include "neonucleus.h"
|
||||||
#include "ncomplib.h"
|
#include "ncomplib.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
|
|
||||||
|
static bool ncl_defaultHandler(ncl_VFSRequest *request);
|
||||||
|
|
||||||
|
#ifdef NN_BAREMETAL
|
||||||
|
bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
||||||
|
return false; // all fails
|
||||||
|
}
|
||||||
|
#else
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#ifdef NN_POSIX
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#elif defined(NN_WINDOWS)
|
||||||
|
#error "Windows is not supported yet"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
||||||
|
if(request->action == NCL_VFS_OPEN) {
|
||||||
|
char mode[3] = "rb";
|
||||||
|
mode[0] = request->open.mode[0];
|
||||||
|
FILE *f = fopen(request->open.path, mode);
|
||||||
|
request->open.file = f;
|
||||||
|
return f != NULL;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_CLOSE) {
|
||||||
|
fclose(request->close);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_READ) {
|
||||||
|
FILE *f = request->read.file;
|
||||||
|
if(feof(f)) {
|
||||||
|
request->read.buf = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
request->read.len = fread(request->read.buf, sizeof(char), request->read.len, f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_WRITE) {
|
||||||
|
FILE *f = request->write.file;
|
||||||
|
size_t written = fwrite(request->write.buf, sizeof(char), request->write.len, f);
|
||||||
|
return written == request->write.len;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_SEEK) {
|
||||||
|
FILE *f = request->seek.file;
|
||||||
|
nn_FSWhence wanted = request->seek.whence;
|
||||||
|
int whence = SEEK_SET;
|
||||||
|
if(wanted == NN_SEEK_SET) whence = SEEK_SET;
|
||||||
|
if(wanted == NN_SEEK_CUR) whence = SEEK_CUR;
|
||||||
|
if(wanted == NN_SEEK_END) whence = SEEK_END;
|
||||||
|
if(fseek(f, whence, request->seek.off) < 0) return false;
|
||||||
|
request->seek.off = ftell(f);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_REMOVE) {
|
||||||
|
return remove(request->remove) == 0;
|
||||||
|
}
|
||||||
|
#ifdef NN_POSIX
|
||||||
|
if(request->action == NCL_VFS_OPENDIR) {
|
||||||
|
DIR *d = opendir(request->opendir.path);
|
||||||
|
request->opendir.dir = d;
|
||||||
|
return d != NULL;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_CLOSEDIR) {
|
||||||
|
DIR *d = request->closedir;
|
||||||
|
closedir(d);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_READDIR) {
|
||||||
|
DIR *d = request->readdir.dir;
|
||||||
|
struct dirent *ent = readdir(d);
|
||||||
|
if(ent == NULL) {
|
||||||
|
request->readdir.name = NULL;
|
||||||
|
} else {
|
||||||
|
strncpy(request->readdir.name, ent->d_name, NN_MAX_PATH-1);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_STAT) {
|
||||||
|
struct stat s;
|
||||||
|
if(stat(request->stat.path, &s) != 0) {
|
||||||
|
request->stat.path = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ncl_Stat *stat = request->stat.stat;
|
||||||
|
stat->isDirectory = S_ISDIR(s.st_mode);
|
||||||
|
stat->diskSize = s.st_blocks * 512;
|
||||||
|
stat->size = 0;
|
||||||
|
if(!stat->isDirectory) {
|
||||||
|
FILE *f = fopen(request->stat.path, "r");
|
||||||
|
if(f == NULL) {
|
||||||
|
// horribly off but don't care atp
|
||||||
|
stat->size = s.st_size;
|
||||||
|
} else {
|
||||||
|
fseek(f, SEEK_END, 0);
|
||||||
|
stat->size = ftell(f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stat->lastModified = s.st_mtime;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return false; // not supported
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ncl_VFS ncl_defaultFS = (ncl_VFS) {
|
||||||
|
.state = NULL,
|
||||||
|
.handler = ncl_defaultHandler,
|
||||||
|
#ifdef NN_WINDOWS
|
||||||
|
.pathsep = '\\',
|
||||||
|
#else
|
||||||
|
.pathsep = '/',
|
||||||
|
#endif
|
||||||
|
.fileCost = 512,
|
||||||
|
};
|
||||||
|
|
||||||
|
void *ncl_openfile(ncl_VFS vfs, const char *path, const char *mode) {
|
||||||
|
ncl_VFSRequest req;
|
||||||
|
req.state = vfs.state;
|
||||||
|
req.action = NCL_VFS_OPEN;
|
||||||
|
req.open.path = path;
|
||||||
|
req.open.mode = mode;
|
||||||
|
if(!vfs.handler(&req)) return NULL;
|
||||||
|
return req.open.file;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ncl_closefile(ncl_VFS vfs, void *file) {
|
||||||
|
ncl_VFSRequest req;
|
||||||
|
req.state = vfs.state;
|
||||||
|
req.action = NCL_VFS_CLOSE;
|
||||||
|
req.close = file;
|
||||||
|
vfs.handler(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ncl_readfile(ncl_VFS vfs, void *file, char *buf, size_t *len);
|
||||||
|
bool ncl_writefile(ncl_VFS vfs, void *file, const char *data, size_t len);
|
||||||
|
bool ncl_seekfile(ncl_VFS vfs, void *file, nn_FSWhence whence, int *off);
|
||||||
|
bool ncl_stat(ncl_VFS vfs, const char *path, ncl_Stat *stat) {
|
||||||
|
ncl_VFSRequest req;
|
||||||
|
req.state = vfs.state;
|
||||||
|
req.action = NCL_VFS_STAT;
|
||||||
|
req.stat.path = path;
|
||||||
|
req.stat.stat = stat;
|
||||||
|
if(!vfs.handler(&req)) return false;
|
||||||
|
if(req.stat.path == NULL) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *ncl_opendir(ncl_VFS vfs, const char *path) {
|
||||||
|
ncl_VFSRequest req;
|
||||||
|
req.state = vfs.state;
|
||||||
|
req.action = NCL_VFS_OPENDIR;
|
||||||
|
req.opendir.path = path;
|
||||||
|
if(!vfs.handler(&req)) return NULL;
|
||||||
|
return req.opendir.dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ncl_closedir(ncl_VFS vfs, void *dir) {
|
||||||
|
ncl_VFSRequest req;
|
||||||
|
req.state = vfs.state;
|
||||||
|
req.action = NCL_VFS_CLOSEDIR;
|
||||||
|
req.closedir = dir;
|
||||||
|
vfs.handler(&req);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ncl_readdir(ncl_VFS vfs, void *dir, char name[NN_MAX_PATH]) {
|
||||||
|
ncl_VFSRequest req;
|
||||||
|
req.state = vfs.state;
|
||||||
|
req.action = NCL_VFS_READDIR;
|
||||||
|
req.readdir.dir = dir;
|
||||||
|
req.readdir.name = name;
|
||||||
|
if(!vfs.handler(&req)) return false;
|
||||||
|
if(req.readdir.name == NULL) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ncl_spaceUsedIn(ncl_VFS vfs, const char *path) {
|
||||||
|
ncl_Stat s;
|
||||||
|
if(!ncl_stat(vfs, path, &s)) return 0;
|
||||||
|
if(!s.isDirectory) return vfs.fileCost + s.size;
|
||||||
|
size_t spaceUsed = vfs.fileCost;
|
||||||
|
void *dir = ncl_opendir(vfs, path);
|
||||||
|
if(dir == NULL) return spaceUsed;
|
||||||
|
char name[NN_MAX_PATH];
|
||||||
|
while(ncl_readdir(vfs, dir, name)) {
|
||||||
|
if(strcmp(name, ".") != 0 && strcmp(name, "..") != 0) {
|
||||||
|
char subpath[NN_MAX_PATH];
|
||||||
|
snprintf(subpath, sizeof(subpath), "%s%c%s", path, vfs.pathsep, name);
|
||||||
|
spaceUsed += ncl_spaceUsedIn(vfs, subpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ncl_closedir(vfs, dir);
|
||||||
|
return spaceUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ncl_spaceUsedBy(ncl_VFS vfs, const char *path) {
|
||||||
|
ncl_Stat s;
|
||||||
|
if(!ncl_stat(vfs, path, &s)) return 0;
|
||||||
|
size_t spaceUsed = s.diskSize;
|
||||||
|
if(!s.isDirectory) return s.diskSize;
|
||||||
|
void *dir = ncl_opendir(vfs, path);
|
||||||
|
if(dir == NULL) return spaceUsed;
|
||||||
|
char name[NN_MAX_PATH];
|
||||||
|
while(ncl_readdir(vfs, dir, name)) {
|
||||||
|
if(strcmp(name, ".") != 0 && strcmp(name, "..") != 0) {
|
||||||
|
char subpath[NN_MAX_PATH];
|
||||||
|
snprintf(subpath, sizeof(subpath), "%s%c%s", path, vfs.pathsep, name);
|
||||||
|
spaceUsed += ncl_spaceUsedBy(vfs, subpath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ncl_closedir(vfs, dir);
|
||||||
|
return spaceUsed;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct ncl_ScreenPixel {
|
typedef struct ncl_ScreenPixel {
|
||||||
nn_codepoint codepoint;
|
nn_codepoint codepoint;
|
||||||
int storedFg;
|
int storedFg;
|
||||||
@@ -24,11 +242,151 @@ typedef struct ncl_ScreenState {
|
|||||||
int *palette;
|
int *palette;
|
||||||
int *resolvedPalette;
|
int *resolvedPalette;
|
||||||
ncl_ScreenPixel *pixels;
|
ncl_ScreenPixel *pixels;
|
||||||
|
ncl_ScreenFlags flags;
|
||||||
} ncl_ScreenState;
|
} ncl_ScreenState;
|
||||||
|
|
||||||
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs);
|
typedef struct nn_VRAMBuf {
|
||||||
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive);
|
int width;
|
||||||
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
|
int height;
|
||||||
|
ncl_ScreenPixel pixels[];
|
||||||
|
} ncl_VRAMBuf;
|
||||||
|
|
||||||
|
typedef struct ncl_GPUState {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_Lock *lock;
|
||||||
|
nn_GPU conf;
|
||||||
|
size_t vramFree;
|
||||||
|
ncl_VRAMBuf *vram[NCL_MAX_VRAMBUF];
|
||||||
|
char *screenAddress;
|
||||||
|
int currentFg;
|
||||||
|
int currentBg;
|
||||||
|
int activeBuffer;
|
||||||
|
bool isFgPalette;
|
||||||
|
bool isBgPalette;
|
||||||
|
} ncl_GPUState;
|
||||||
|
|
||||||
|
static void ncl_freeVRAM(nn_Context *ctx, ncl_VRAMBuf *buf) {
|
||||||
|
nn_free(ctx, buf, sizeof(ncl_VRAMBuf) + sizeof(ncl_ScreenPixel) * buf->width * buf->height);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ncl_VRAMBuf *ncl_allocVRAM(nn_Context *ctx, int width, int height) {
|
||||||
|
ncl_VRAMBuf *buf = nn_alloc(ctx, sizeof(ncl_VRAMBuf) + sizeof(ncl_ScreenPixel) * width * height);
|
||||||
|
if(buf == NULL) return NULL;
|
||||||
|
buf->width = width;
|
||||||
|
buf->height = height;
|
||||||
|
for(int i = 0; i < width*height; i++) {
|
||||||
|
buf->pixels[i] = (ncl_ScreenPixel) {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.storedFg = 0xFFFFFF,
|
||||||
|
.storedBg = 0x000000,
|
||||||
|
.realFg = 0xFFFFFF,
|
||||||
|
.realBg = 0x000000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct ncl_FSState {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_Lock *lock;
|
||||||
|
nn_Filesystem conf;
|
||||||
|
char *path;
|
||||||
|
ncl_VFS vfs;
|
||||||
|
// if 0, needs to be recomputed
|
||||||
|
size_t spaceUsed;
|
||||||
|
// if 0, needs to be recomputed
|
||||||
|
size_t realSpaceUsed;
|
||||||
|
atomic_size_t usage;
|
||||||
|
bool isReadonly;
|
||||||
|
// all the arrays
|
||||||
|
FILE *fds[NN_MAX_OPENFILES];
|
||||||
|
void *dirs[NN_MAX_OPENFILES];
|
||||||
|
char label[NN_MAX_LABEL];
|
||||||
|
size_t labellen;
|
||||||
|
} ncl_FSState;
|
||||||
|
|
||||||
|
typedef struct ncl_DriveState {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_Lock *lock;
|
||||||
|
nn_Drive conf;
|
||||||
|
bool isReadonly;
|
||||||
|
atomic_size_t usage;
|
||||||
|
size_t lastSector;
|
||||||
|
char *path;
|
||||||
|
FILE *file;
|
||||||
|
char label[NN_MAX_LABEL];
|
||||||
|
size_t labellen;
|
||||||
|
} ncl_DriveState;
|
||||||
|
|
||||||
|
typedef struct ncl_EEState {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_Lock *lock;
|
||||||
|
nn_EEPROM conf;
|
||||||
|
bool isReadonly;
|
||||||
|
atomic_size_t usage;
|
||||||
|
char *codepath;
|
||||||
|
char *datapath;
|
||||||
|
char label[NN_MAX_LABEL];
|
||||||
|
size_t labellen;
|
||||||
|
} ncl_EEState;
|
||||||
|
|
||||||
|
static void ncl_fixPath(ncl_VFS vfs, const char *path, char buf[NN_MAX_PATH]) {
|
||||||
|
size_t len = 0;
|
||||||
|
while(path[len]) {
|
||||||
|
if(len == NN_MAX_PATH) break;
|
||||||
|
if(path[len] == '/') buf[len] = vfs.pathsep;
|
||||||
|
else buf[len] = path[len];
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
buf[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes locked
|
||||||
|
static size_t ncl_fsGetUsage(ncl_FSState *fs) {
|
||||||
|
if(fs->spaceUsed == 0) {
|
||||||
|
fs->spaceUsed = ncl_spaceUsedIn(fs->vfs, fs->path);
|
||||||
|
}
|
||||||
|
if(fs->spaceUsed > fs->conf.spaceTotal) fs->spaceUsed = fs->conf.spaceTotal;
|
||||||
|
return fs->spaceUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// assumes locked
|
||||||
|
static size_t ncl_fsGetRealUsage(ncl_FSState *fs) {
|
||||||
|
if(fs->realSpaceUsed == 0) {
|
||||||
|
fs->realSpaceUsed = ncl_spaceUsedBy(fs->vfs, fs->path);
|
||||||
|
}
|
||||||
|
return fs->realSpaceUsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
static nn_Exit ncl_fsHandler(nn_FSRequest *req) {
|
||||||
|
ncl_FSState *state = req->state;
|
||||||
|
nn_Context *ctx = req->ctx;
|
||||||
|
nn_Computer *C = req->computer;
|
||||||
|
const nn_Filesystem *fs = req->fs;
|
||||||
|
|
||||||
|
if(req->action == NN_FS_DROP) {
|
||||||
|
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||||
|
if(state->fds[i] != NULL) ncl_closefile(state->vfs, state->fds[i]);
|
||||||
|
if(state->dirs[i] != NULL) ncl_closedir(state->vfs, state->dirs[i]);
|
||||||
|
}
|
||||||
|
nn_strfree(ctx, state->path);
|
||||||
|
nn_free(ctx, state, sizeof(*state));
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
if(req->action == NN_FS_SPACEUSED) {
|
||||||
|
nn_lock(ctx, state->lock);
|
||||||
|
req->spaceUsed = ncl_fsGetUsage(state);
|
||||||
|
nn_unlock(ctx, state->lock);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(C) nn_setError(C, "not implemented yet");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly);
|
||||||
|
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive, bool isReadonly);
|
||||||
|
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath, bool isReadonly);
|
||||||
|
|
||||||
static ncl_ScreenPixel ncl_getRealScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
static ncl_ScreenPixel ncl_getRealScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
||||||
if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
|
if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
|
||||||
@@ -60,7 +418,7 @@ static ncl_ScreenPixel *ncl_getRealScreenPixelPointer(const ncl_ScreenState *sta
|
|||||||
return &state->pixels[x + y * state->conf.maxWidth];
|
return &state->pixels[x + y * state->conf.maxWidth];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ncl_setRealScreenPixel(const ncl_ScreenState *state, int x, int y, ncl_ScreenPixel pixel) {
|
static void ncl_setRealScreenPixel(ncl_ScreenState *state, int x, int y, ncl_ScreenPixel pixel) {
|
||||||
if(x < 1 || y < 1 || x >= state->width || y >= state->height) return;
|
if(x < 1 || y < 1 || x >= state->width || y >= state->height) return;
|
||||||
x--;
|
x--;
|
||||||
y--;
|
y--;
|
||||||
@@ -90,6 +448,28 @@ static void ncl_recomputeScreen(const ncl_ScreenState *state) {
|
|||||||
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
||||||
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
||||||
|
|
||||||
|
void ncl_resetScreen(ncl_ScreenState *state) {
|
||||||
|
state->width = state->conf.maxWidth;
|
||||||
|
state->height = state->conf.maxHeight;
|
||||||
|
state->viewportWidth = state->conf.maxWidth;
|
||||||
|
state->viewportHeight = state->conf.maxHeight;
|
||||||
|
|
||||||
|
for(int y = 1; y <= state->height; y++) {
|
||||||
|
for(int x = 1; x <= state->width; x++) {
|
||||||
|
ncl_setRealScreenPixel(state, x, y, (ncl_ScreenPixel) {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.storedFg = 0xFFFFFF,
|
||||||
|
.storedBg = 0x000000,
|
||||||
|
.realFg = 0xFFFFFF,
|
||||||
|
.realBg = 0x000000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(state->palette, state->conf.defaultPalette, sizeof(int) * state->conf.paletteColors);
|
||||||
|
ncl_recomputeScreen(state);
|
||||||
|
}
|
||||||
|
|
||||||
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height) {
|
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height) {
|
||||||
*width = state->width;
|
*width = state->width;
|
||||||
*height = state->height;
|
*height = state->height;
|
||||||
@@ -109,8 +489,96 @@ ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ncl_ScreenFlags ncl_getScreenFlags(const ncl_ScreenState *state) {
|
||||||
|
return state->flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ncl_setScreenFlags(ncl_ScreenState *state, ncl_ScreenFlags flags) {
|
||||||
|
state->flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// general stuff
|
||||||
|
|
||||||
|
void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat) {
|
||||||
|
stat->labellen = 0;
|
||||||
|
stat->isReadonly = false;
|
||||||
|
const char *ty = nn_getComponentTypeID(component);
|
||||||
|
void *state = nn_getComponentState(component);
|
||||||
|
if(strcmp(ty, NCL_FS) == 0) {
|
||||||
|
ncl_FSState *fs = state;
|
||||||
|
nn_lock(fs->ctx, fs->lock);
|
||||||
|
stat->isReadonly = fs->isReadonly;
|
||||||
|
stat->usageCounter = fs->usage;
|
||||||
|
stat->labellen = fs->labellen;
|
||||||
|
memcpy(stat->label, fs->label, stat->labellen);
|
||||||
|
stat->fs.spaceUsed = ncl_fsGetUsage(fs);
|
||||||
|
stat->fs.realDiskUsage = ncl_fsGetRealUsage(fs);
|
||||||
|
stat->fs.conf = &fs->conf;
|
||||||
|
stat->fs.path = fs->path;
|
||||||
|
stat->fs.filesOpen = 0;
|
||||||
|
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||||
|
if(fs->fds[i] != NULL) stat->fs.filesOpen++;
|
||||||
|
}
|
||||||
|
nn_unlock(fs->ctx, fs->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(strcmp(ty, NCL_DRIVE) == 0) {
|
||||||
|
ncl_DriveState *drv = state;
|
||||||
|
nn_lock(drv->ctx, drv->lock);
|
||||||
|
stat->isReadonly = drv->isReadonly;
|
||||||
|
stat->usageCounter = drv->usage;
|
||||||
|
stat->labellen = drv->labellen;
|
||||||
|
memcpy(stat->label, drv->label, stat->labellen);
|
||||||
|
stat->drive.path = drv->path;
|
||||||
|
stat->drive.lastSector = drv->lastSector;
|
||||||
|
stat->drive.conf = &drv->conf;
|
||||||
|
nn_unlock(drv->ctx, drv->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(strcmp(ty, NCL_EEPROM) == 0) {
|
||||||
|
ncl_EEState *ee = state;
|
||||||
|
nn_lock(ee->ctx, ee->lock);
|
||||||
|
stat->isReadonly = ee->isReadonly;
|
||||||
|
stat->usageCounter = ee->usage;
|
||||||
|
stat->labellen = ee->labellen;
|
||||||
|
memcpy(stat->label, ee->label, stat->labellen);
|
||||||
|
stat->eeprom.conf = &ee->conf;
|
||||||
|
stat->eeprom.codepath = ee->codepath;
|
||||||
|
stat->eeprom.datapath = ee->datapath;
|
||||||
|
nn_unlock(ee->ctx, ee->lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For EEPROMs, filesystems, drives
|
||||||
|
// Returns whether it was successful or not.
|
||||||
|
bool ncl_makeReadonly(nn_Component *component) {
|
||||||
|
const char *ty = nn_getComponentTypeID(component);
|
||||||
|
void *state = nn_getComponentState(component);
|
||||||
|
if(strcmp(ty, NCL_FS) == 0) {
|
||||||
|
ncl_FSState *fs = state;
|
||||||
|
fs->isReadonly = true;
|
||||||
|
fs->usage++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(strcmp(ty, NCL_DRIVE) == 0) {
|
||||||
|
ncl_DriveState *drv = state;
|
||||||
|
drv->isReadonly = true;
|
||||||
|
drv->usage++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(strcmp(ty, NCL_EEPROM) == 0) {
|
||||||
|
ncl_EEState *ee = state;
|
||||||
|
ee->isReadonly = true;
|
||||||
|
ee->usage++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// all of these are encoding states
|
// all of these are encoding states
|
||||||
|
|
||||||
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
|
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
|
||||||
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
||||||
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
||||||
|
|
||||||
|
|||||||
217
src/ncomplib.h
217
src/ncomplib.h
@@ -3,6 +3,144 @@
|
|||||||
|
|
||||||
#include "neonucleus.h"
|
#include "neonucleus.h"
|
||||||
|
|
||||||
|
#define NCL_EEPROM "ncl-eeprom"
|
||||||
|
#define NCL_FS "ncl-filesystem"
|
||||||
|
#define NCL_DRIVE "ncl-drive"
|
||||||
|
#define NCL_GPU "ncl-gpu"
|
||||||
|
#define NCL_SCREEN "ncl-screen"
|
||||||
|
|
||||||
|
#define NCL_MAX_VRAMBUF 128
|
||||||
|
|
||||||
|
// very low-level actions
|
||||||
|
// some environment have VFSes so
|
||||||
|
// we support wrapping those
|
||||||
|
|
||||||
|
typedef struct ncl_Stat {
|
||||||
|
// whether the entry is a directory
|
||||||
|
bool isDirectory;
|
||||||
|
// the logical size of the entry
|
||||||
|
// as in, for files it is how many bytes are in there.
|
||||||
|
// For directories, it should be 0.
|
||||||
|
// Every entry has a base cost, and thus fear not,
|
||||||
|
// it will not lead to infinite disk usage.
|
||||||
|
// Instead, make their size representative of the
|
||||||
|
// size on disk / number of entries.
|
||||||
|
size_t size;
|
||||||
|
// the real size.
|
||||||
|
// This is for realSpaceUsed, and is a safety mechanism
|
||||||
|
// against disk-hogging.
|
||||||
|
size_t diskSize;
|
||||||
|
// The UNIX timestamp of the last modified date
|
||||||
|
// of the entry.
|
||||||
|
size_t lastModified;
|
||||||
|
} ncl_Stat;
|
||||||
|
|
||||||
|
typedef enum ncl_VFSAction {
|
||||||
|
NCL_VFS_OPEN,
|
||||||
|
NCL_VFS_CLOSE,
|
||||||
|
NCL_VFS_READ,
|
||||||
|
NCL_VFS_SEEK,
|
||||||
|
NCL_VFS_WRITE,
|
||||||
|
|
||||||
|
NCL_VFS_OPENDIR,
|
||||||
|
NCL_VFS_CLOSEDIR,
|
||||||
|
NCL_VFS_READDIR,
|
||||||
|
|
||||||
|
// non-recursively remove entry
|
||||||
|
NCL_VFS_REMOVE,
|
||||||
|
|
||||||
|
NCL_VFS_STAT,
|
||||||
|
} ncl_VFSAction;
|
||||||
|
|
||||||
|
typedef struct ncl_VFSRequest {
|
||||||
|
void *state;
|
||||||
|
ncl_VFSAction action;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
const char *path;
|
||||||
|
// same r, w and a modes as regular filesystem component
|
||||||
|
const char *mode;
|
||||||
|
void *file;
|
||||||
|
} open;
|
||||||
|
struct {
|
||||||
|
// set to NULL for EoF
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
void *file;
|
||||||
|
} read;
|
||||||
|
struct {
|
||||||
|
const char *buf;
|
||||||
|
size_t len;
|
||||||
|
void *file;
|
||||||
|
} write;
|
||||||
|
struct {
|
||||||
|
nn_FSWhence whence;
|
||||||
|
int off;
|
||||||
|
void *file;
|
||||||
|
} seek;
|
||||||
|
void *close;
|
||||||
|
struct {
|
||||||
|
const char *path;
|
||||||
|
void *dir;
|
||||||
|
} opendir;
|
||||||
|
struct {
|
||||||
|
// set to NULL for EoF
|
||||||
|
// buffer size is NN_MAX_PATH.
|
||||||
|
// Remember to account for terminator
|
||||||
|
char *name;
|
||||||
|
void *dir;
|
||||||
|
} readdir;
|
||||||
|
void *closedir;
|
||||||
|
const char *remove;
|
||||||
|
struct {
|
||||||
|
// set to NULL if missing
|
||||||
|
const char *path;
|
||||||
|
ncl_Stat *stat;
|
||||||
|
} stat;
|
||||||
|
struct {
|
||||||
|
// path of directory that / represents
|
||||||
|
const char *path;
|
||||||
|
// get the estimated amount of space
|
||||||
|
// used up by an empty entry.
|
||||||
|
// Used for enforcing capacity.
|
||||||
|
size_t size;
|
||||||
|
} entrysize;
|
||||||
|
};
|
||||||
|
} ncl_VFSRequest;
|
||||||
|
|
||||||
|
typedef struct ncl_VFS {
|
||||||
|
// the internal state
|
||||||
|
void *state;
|
||||||
|
// the handler.
|
||||||
|
// True on success, false on failure.
|
||||||
|
bool (*handler)(ncl_VFSRequest *request);
|
||||||
|
// the path separator
|
||||||
|
char pathsep;
|
||||||
|
// the assumed cost of a file in spaceUsedIn.
|
||||||
|
size_t fileCost;
|
||||||
|
} ncl_VFS;
|
||||||
|
|
||||||
|
extern ncl_VFS ncl_defaultFS;
|
||||||
|
|
||||||
|
void *ncl_openfile(ncl_VFS vfs, const char *path, const char *mode);
|
||||||
|
void ncl_closefile(ncl_VFS vfs, void *file);
|
||||||
|
// returns false on EoF
|
||||||
|
bool ncl_readfile(ncl_VFS vfs, void *file, char *buf, size_t *len);
|
||||||
|
bool ncl_writefile(ncl_VFS vfs, void *file, const char *data, size_t len);
|
||||||
|
bool ncl_seekfile(ncl_VFS vfs, void *file, nn_FSWhence whence, int *off);
|
||||||
|
bool ncl_stat(ncl_VFS vfs, const char *path, ncl_Stat *stat);
|
||||||
|
|
||||||
|
void *ncl_opendir(ncl_VFS vfs, const char *path);
|
||||||
|
void ncl_closedir(ncl_VFS vfs, void *dir);
|
||||||
|
// returns false on EoF
|
||||||
|
bool ncl_readdir(ncl_VFS vfs, void *dir, char name[NN_MAX_PATH]);
|
||||||
|
size_t ncl_spaceUsedIn(ncl_VFS vfs, const char *path);
|
||||||
|
// gets the real space used
|
||||||
|
size_t ncl_spaceUsedBy(ncl_VFS vfs, const char *path);
|
||||||
|
|
||||||
|
bool ncl_remove(ncl_VFS vfs, const char *path);
|
||||||
|
bool ncl_removeRecursive(ncl_VFS vfs, const char *path);
|
||||||
|
|
||||||
typedef struct ncl_EncodedState {
|
typedef struct ncl_EncodedState {
|
||||||
char *buf;
|
char *buf;
|
||||||
size_t len;
|
size_t len;
|
||||||
@@ -12,11 +150,17 @@ nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_
|
|||||||
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
||||||
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
||||||
|
|
||||||
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs);
|
size_t ncl_getLabel(nn_Component *c, char buf[NN_MAX_LABEL]);
|
||||||
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive);
|
size_t ncl_setLabel(nn_Component *c, const char *label, size_t len);
|
||||||
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
|
|
||||||
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs, bool isReadonly);
|
||||||
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive, bool isReadonly);
|
||||||
|
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath, bool isReadonly);
|
||||||
|
|
||||||
|
// Sets the VFS bound to a filesystem, drive or eeprom.
|
||||||
|
// This determines the filesystem the operations are run in.
|
||||||
|
// Returns the old VFS.
|
||||||
|
ncl_VFS ncl_setVFS(nn_Component *component, ncl_VFS vfs);
|
||||||
|
|
||||||
// TODO, stuff we could implement:
|
// TODO, stuff we could implement:
|
||||||
// redstone, hologram, oled, ipu, vt, led, tape_drive, cd_drive, serial, colorful_lamp
|
// redstone, hologram, oled, ipu, vt, led, tape_drive, cd_drive, serial, colorful_lamp
|
||||||
@@ -32,8 +176,71 @@ typedef struct ncl_Pixel {
|
|||||||
|
|
||||||
typedef struct ncl_ScreenState ncl_ScreenState;
|
typedef struct ncl_ScreenState ncl_ScreenState;
|
||||||
|
|
||||||
|
typedef enum ncl_ScreenFlags {
|
||||||
|
NCL_SCREEN_ON = 1<<0,
|
||||||
|
NCL_SCREEN_PRECISE = 1<<1,
|
||||||
|
NCL_SCREEN_TOUCHINVERTED = 1<<2,
|
||||||
|
} ncl_ScreenFlags;
|
||||||
|
|
||||||
|
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
||||||
|
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
||||||
|
|
||||||
|
typedef struct ncl_ComponentStat {
|
||||||
|
// common ones
|
||||||
|
char label[NN_MAX_LABEL];
|
||||||
|
size_t labellen;
|
||||||
|
// used for indicating usage. If higher than last time, something happened.
|
||||||
|
// This can be used to show a light or play a sound.
|
||||||
|
size_t usageCounter;
|
||||||
|
bool isReadonly;
|
||||||
|
// specific properties
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
const nn_EEPROM *conf;
|
||||||
|
size_t codeUsed;
|
||||||
|
size_t dataUsed;
|
||||||
|
const char *codepath;
|
||||||
|
const char *datapath;
|
||||||
|
} eeprom;
|
||||||
|
struct {
|
||||||
|
const nn_Filesystem *conf;
|
||||||
|
size_t spaceUsed;
|
||||||
|
size_t realDiskUsage;
|
||||||
|
size_t filesOpen;
|
||||||
|
const char *path;
|
||||||
|
} fs;
|
||||||
|
struct {
|
||||||
|
const nn_Drive *conf;
|
||||||
|
size_t lastSector;
|
||||||
|
const char *path;
|
||||||
|
} drive;
|
||||||
|
struct {
|
||||||
|
const nn_GPU *conf;
|
||||||
|
size_t vramFree;
|
||||||
|
size_t bufferCount;
|
||||||
|
// can be NULL if there is none
|
||||||
|
const char *boundScreen;
|
||||||
|
} gpu;
|
||||||
|
struct {
|
||||||
|
const nn_ScreenConfig *conf;
|
||||||
|
ncl_ScreenState *state;
|
||||||
|
ncl_ScreenFlags flags;
|
||||||
|
int viewportWidth;
|
||||||
|
int viewportHeight;
|
||||||
|
} screen;
|
||||||
|
};
|
||||||
|
} ncl_ComponentStat;
|
||||||
|
|
||||||
|
void ncl_statComponent(nn_Component *component, ncl_ComponentStat *stat);
|
||||||
|
// For EEPROMs, filesystems, drives
|
||||||
|
// Returns whether it was successful or not.
|
||||||
|
bool ncl_makeReadonly(nn_Component *component);
|
||||||
|
|
||||||
|
void ncl_resetScreen(ncl_ScreenState *state);
|
||||||
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
|
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
|
||||||
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height);
|
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height);
|
||||||
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y);
|
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y);
|
||||||
|
ncl_ScreenFlags ncl_getScreenFlags(const ncl_ScreenState *state);
|
||||||
|
void ncl_setScreenFlags(ncl_ScreenState *state, ncl_ScreenFlags flags);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
384
src/neonucleus.c
384
src/neonucleus.c
@@ -1688,6 +1688,11 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
|
|||||||
}
|
}
|
||||||
nn_MethodEntry *m = nn_getComponentMethodEntry(c, method);
|
nn_MethodEntry *m = nn_getComponentMethodEntry(c, method);
|
||||||
|
|
||||||
|
while(nn_getstacksize(computer) > 0) {
|
||||||
|
if(!nn_isnull(computer, nn_getstacksize(computer) - 1)) break;
|
||||||
|
nn_pop(computer);
|
||||||
|
}
|
||||||
|
|
||||||
nn_ComponentRequest req;
|
nn_ComponentRequest req;
|
||||||
req.ctx = &c->universe->ctx;
|
req.ctx = &c->universe->ctx;
|
||||||
req.computer = computer;
|
req.computer = computer;
|
||||||
@@ -1696,7 +1701,10 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
|
|||||||
req.methodIdx = m->idx;
|
req.methodIdx = m->idx;
|
||||||
req.returnCount = 0;
|
req.returnCount = 0;
|
||||||
nn_Exit e = c->handler(&req);
|
nn_Exit e = c->handler(&req);
|
||||||
if(e) return e;
|
if(e) {
|
||||||
|
nn_clearstack(computer);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
size_t endOfTrim = computer->stackSize - req.returnCount;
|
size_t endOfTrim = computer->stackSize - req.returnCount;
|
||||||
for(size_t i = 0; i < endOfTrim; i++) {
|
for(size_t i = 0; i < endOfTrim; i++) {
|
||||||
@@ -3190,7 +3198,8 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
|
|||||||
if(ereq.buflen == 0) return nn_pushnull(C);
|
if(ereq.buflen == 0) return nn_pushnull(C);
|
||||||
return nn_pushlstring(C, ereq.buf, ereq.buflen);
|
return nn_pushlstring(C, ereq.buf, ereq.buflen);
|
||||||
}
|
}
|
||||||
return NN_OK;
|
nn_setError(C, "not implemented yet");
|
||||||
|
return NN_EBADCALL;
|
||||||
}
|
}
|
||||||
|
|
||||||
nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler) {
|
nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler) {
|
||||||
@@ -3303,3 +3312,374 @@ fail:
|
|||||||
nn_free(ctx, state, sizeof(*state));
|
nn_free(ctx, state, sizeof(*state));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef enum nn_FSNum {
|
||||||
|
// drive stuff
|
||||||
|
NN_FSNUM_SPACETOTAL,
|
||||||
|
NN_FSNUM_SPACEUSED,
|
||||||
|
NN_FSNUM_GETLABEL,
|
||||||
|
NN_FSNUM_SETLABEL,
|
||||||
|
NN_FSNUM_ISRO,
|
||||||
|
|
||||||
|
// file I/O
|
||||||
|
NN_FSNUM_OPEN,
|
||||||
|
NN_FSNUM_READ,
|
||||||
|
NN_FSNUM_WRITE,
|
||||||
|
NN_FSNUM_SEEK,
|
||||||
|
NN_FSNUM_CLOSE,
|
||||||
|
|
||||||
|
// metadata
|
||||||
|
NN_FSNUM_LIST,
|
||||||
|
NN_FSNUM_EXISTS,
|
||||||
|
NN_FSNUM_ISDIR,
|
||||||
|
NN_FSNUM_SIZE,
|
||||||
|
NN_FSNUM_LASTMODIFIED,
|
||||||
|
|
||||||
|
// exotic
|
||||||
|
NN_FSNUM_MKDIR,
|
||||||
|
NN_FSNUM_REMOVE,
|
||||||
|
NN_FSNUM_RENAME,
|
||||||
|
|
||||||
|
NN_FSNUM_COUNT,
|
||||||
|
} nn_FSNum;
|
||||||
|
|
||||||
|
typedef struct nn_FSState {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_Filesystem fs;
|
||||||
|
void *state;
|
||||||
|
nn_FSHandler *handler;
|
||||||
|
} nn_FSState;
|
||||||
|
|
||||||
|
static nn_Exit nn_fsPathCheck(nn_Computer *C, char buf[NN_MAX_PATH], const char *path) {
|
||||||
|
size_t l = nn_strlen(path);
|
||||||
|
if(l >= NN_MAX_PATH) {
|
||||||
|
nn_setError(C, "path too long");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
nn_simplifyPath(path, buf);
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static nn_Exit nn_fsHandler(nn_ComponentRequest *req) {
|
||||||
|
if(req->action == NN_COMP_SIGNAL) return NN_OK;
|
||||||
|
if(req->action == NN_COMP_CHECKMETHOD) return NN_OK;
|
||||||
|
nn_FSState *state = req->state;
|
||||||
|
nn_FSRequest freq;
|
||||||
|
freq.ctx = req->ctx;
|
||||||
|
freq.computer = req->computer;
|
||||||
|
freq.state = state->state;
|
||||||
|
freq.fs = &state->fs;
|
||||||
|
if(req->action == NN_COMP_DROP) {
|
||||||
|
freq.action = NN_FS_DROP;
|
||||||
|
state->handler(&freq);
|
||||||
|
nn_free(req->ctx, state, sizeof(*state));
|
||||||
|
return NN_OK;
|
||||||
|
}
|
||||||
|
nn_Computer *C = req->computer;
|
||||||
|
nn_FSNum method = req->methodIdx;
|
||||||
|
nn_Exit e = NN_OK;
|
||||||
|
if(method == NN_FSNUM_SPACETOTAL) {
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, state->fs.spaceTotal);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_SPACEUSED) {
|
||||||
|
freq.action = NN_FS_SPACEUSED;
|
||||||
|
freq.spaceUsed = 0;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, freq.spaceUsed);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_GETLABEL) {
|
||||||
|
char buf[NN_MAX_LABEL];
|
||||||
|
freq.action = NN_FS_GETLABEL;
|
||||||
|
freq.getlabel.buf = buf;
|
||||||
|
freq.getlabel.len = NN_MAX_LABEL;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
if(freq.getlabel.len == 0) return nn_pushnull(C);
|
||||||
|
return nn_pushlstring(C, freq.getlabel.buf, freq.getlabel.len);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_SETLABEL) {
|
||||||
|
e = nn_defaultstring(C, 0, "");
|
||||||
|
if(e) return e;
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (label expected)")) return NN_EBADCALL;
|
||||||
|
freq.action = NN_FS_SETLABEL;
|
||||||
|
freq.setlabel.buf = nn_tolstring(C, 0, &freq.setlabel.len);
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
if(freq.setlabel.len == 0) return nn_pushnull(C);
|
||||||
|
return nn_pushlstring(C, freq.setlabel.buf, freq.setlabel.len);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_ISRO) {
|
||||||
|
freq.action = NN_FS_ISRO;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, freq.isReadonly);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_OPEN) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
e = nn_defaultstring(C, 1, "r");
|
||||||
|
if(e) return e;
|
||||||
|
if(nn_checkstring(C, 1, "bad argument #2 (mode expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_OPEN;
|
||||||
|
freq.open.path = truepath;
|
||||||
|
freq.open.mode = nn_tostring(C, 1);
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, freq.fd);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_READ) {
|
||||||
|
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
|
||||||
|
e = nn_defaultinteger(C, 1, NN_MAX_READ);
|
||||||
|
if(e) return e;
|
||||||
|
if(nn_checknumber(C, 1, "bad argument #2 (number expected)")) return NN_EBADCALL;
|
||||||
|
double requested = nn_tonumber(C, 1);
|
||||||
|
if(requested > NN_MAX_READ) requested = NN_MAX_READ;
|
||||||
|
freq.action = NN_FS_CLOSE;
|
||||||
|
freq.fd = nn_tointeger(C, 0);
|
||||||
|
char buf[NN_MAX_READ];
|
||||||
|
freq.read.buf = buf;
|
||||||
|
freq.read.len = requested;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
if(freq.read.buf == NULL) return NN_OK;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_WRITE) {
|
||||||
|
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
|
||||||
|
if(nn_checkstring(C, 1, "bad argument #2 (string expected)")) return NN_EBADCALL;
|
||||||
|
freq.action = NN_FS_WRITE;
|
||||||
|
freq.fd = nn_tointeger(C, 0);
|
||||||
|
freq.write.buf = nn_tolstring(C, 1, &freq.write.len);
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_SEEK) {
|
||||||
|
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
|
||||||
|
e = nn_defaultstring(C, 1, "cur");
|
||||||
|
if(e) return e;
|
||||||
|
if(nn_checkinteger(C, 1, "bad argument #2 (whence expected)")) return NN_EBADCALL;
|
||||||
|
e = nn_defaultinteger(C, 2, 0);
|
||||||
|
if(e) return e;
|
||||||
|
if(nn_checkinteger(C, 2, "bad argument #3 (integer expected)")) return NN_EBADCALL;
|
||||||
|
const char *whence = nn_tostring(C, 1);
|
||||||
|
nn_FSWhence seek = NN_SEEK_SET;
|
||||||
|
if(nn_strcmp(whence, "set") == 0) {
|
||||||
|
seek = NN_SEEK_SET;
|
||||||
|
}
|
||||||
|
if(nn_strcmp(whence, "cur") == 0) {
|
||||||
|
seek = NN_SEEK_CUR;
|
||||||
|
}
|
||||||
|
if(nn_strcmp(whence, "end") == 0) {
|
||||||
|
seek = NN_SEEK_END;
|
||||||
|
}
|
||||||
|
freq.action = NN_FS_CLOSE;
|
||||||
|
freq.fd = nn_tointeger(C, 0);
|
||||||
|
freq.seek.whence = seek;
|
||||||
|
freq.seek.off = nn_tointeger(C, 2);
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_CLOSE) {
|
||||||
|
if(nn_checkinteger(C, 0, "bad argument #1 (fd expected)")) return NN_EBADCALL;
|
||||||
|
freq.action = NN_FS_CLOSE;
|
||||||
|
freq.fd = nn_tointeger(C, 0);
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_LIST) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_OPENDIR;
|
||||||
|
freq.opendir = truepath;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
int dirfd = freq.fd;
|
||||||
|
size_t entCount = 0;
|
||||||
|
while(true) {
|
||||||
|
char name[NN_MAX_PATH];
|
||||||
|
freq.action = NN_FS_READDIR;
|
||||||
|
freq.fd = dirfd;
|
||||||
|
freq.readdir.buf = name;
|
||||||
|
freq.readdir.len = NN_MAX_PATH;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) goto done;
|
||||||
|
if(freq.readdir.buf == NULL) break;
|
||||||
|
if(nn_isLiterallyJust(freq.readdir.buf, freq.readdir.len, '.')) continue;
|
||||||
|
e = nn_pushlstring(C, freq.readdir.buf, freq.readdir.len);
|
||||||
|
if(e) goto done;
|
||||||
|
entCount++;
|
||||||
|
}
|
||||||
|
done:;
|
||||||
|
freq.action = NN_FS_CLOSEDIR;
|
||||||
|
freq.fd = dirfd;
|
||||||
|
state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pusharraytable(C, entCount);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_EXISTS) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_STAT;
|
||||||
|
freq.stat.path = truepath;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, freq.stat.path != NULL);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_ISDIR) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_STAT;
|
||||||
|
freq.stat.path = truepath;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
if(freq.stat.path == NULL) {
|
||||||
|
nn_setError(C, "no such file or directory");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, freq.stat.isDirectory);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_SIZE) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_STAT;
|
||||||
|
freq.stat.path = truepath;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
if(freq.stat.path == NULL) {
|
||||||
|
nn_setError(C, "no such file or directory");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, freq.stat.size);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_LASTMODIFIED) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_STAT;
|
||||||
|
freq.stat.path = truepath;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
if(freq.stat.path == NULL) {
|
||||||
|
nn_setError(C, "no such file or directory");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushinteger(C, freq.stat.lastModified * 1000);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_MKDIR) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_MKDIR;
|
||||||
|
freq.mkdir = truepath;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_REMOVE) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truepath[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truepath, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_RENAME;
|
||||||
|
freq.rename.from = truepath;
|
||||||
|
freq.rename.to = NULL;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
if(method == NN_FSNUM_RENAME) {
|
||||||
|
if(nn_checkstring(C, 0, "bad argument #1 (path expected)")) return NN_EBADCALL;
|
||||||
|
if(nn_checkstring(C, 1, "bad argument #2 (path expected)")) return NN_EBADCALL;
|
||||||
|
char truefrom[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, truefrom, nn_tostring(C, 0));
|
||||||
|
if(e) return e;
|
||||||
|
char trueto[NN_MAX_PATH];
|
||||||
|
e = nn_fsPathCheck(C, trueto, nn_tostring(C, 1));
|
||||||
|
if(e) return e;
|
||||||
|
freq.action = NN_FS_RENAME;
|
||||||
|
freq.rename.from = truefrom;
|
||||||
|
freq.rename.to = trueto;
|
||||||
|
e = state->handler(&freq);
|
||||||
|
if(e) return e;
|
||||||
|
req->returnCount = 1;
|
||||||
|
return nn_pushbool(C, true);
|
||||||
|
}
|
||||||
|
nn_setError(C, "not implemented yet");
|
||||||
|
return NN_EBADCALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler) {
|
||||||
|
nn_Component *c = nn_createComponent(universe, address, "filesystem");
|
||||||
|
if(c == NULL) return NULL;
|
||||||
|
const nn_Method methods[NN_FSNUM_COUNT] = {
|
||||||
|
[NN_FSNUM_SPACETOTAL] = {"spaceTotal", "function(): integer - Capacity of the drive", NN_DIRECT},
|
||||||
|
[NN_FSNUM_SPACEUSED] = {"spaceUsed", "function(): integer - Amount of space used", NN_DIRECT},
|
||||||
|
[NN_FSNUM_GETLABEL] = {"getLabel", "function(): string? - Gets the label of the drive, if any", NN_DIRECT},
|
||||||
|
[NN_FSNUM_SETLABEL] = {"setLabel", "function(label?: string): string - Sets the label of the drive. Returns the new label, which may be truncated", NN_DIRECT},
|
||||||
|
[NN_FSNUM_ISRO] = {"isReadOnly", "function(): boolean - Returns whether the drive is read-only", NN_DIRECT},
|
||||||
|
[NN_FSNUM_OPEN] = {"open", "function(path: string, mode?: 'r'|'w'|'a'): integer - Open a file", NN_DIRECT},
|
||||||
|
[NN_FSNUM_READ] = {"read", "function(fd: integer, len?: integer): string? - Read from a file, returns nothing on EoF", NN_DIRECT},
|
||||||
|
[NN_FSNUM_WRITE] = {"write", "function(fd: integer, data: string): boolean - Writes to a file, returns whether the operation succeeded", NN_DIRECT},
|
||||||
|
[NN_FSNUM_SEEK] = {"seek", "function(fd: integer, whence?: 'set'|'cur'|'end', off?: integer): integer - Seeks a file, returns new position", NN_DIRECT},
|
||||||
|
[NN_FSNUM_CLOSE] = {"close", "function(fd: integer): boolean - Close a file", NN_DIRECT},
|
||||||
|
[NN_FSNUM_LIST] = {"list", "function(path: string): string[] - Returns the entries in a directory", NN_DIRECT},
|
||||||
|
[NN_FSNUM_EXISTS] = {"exists", "function(path: string): boolean - Returns whether an entry exists", NN_DIRECT},
|
||||||
|
[NN_FSNUM_ISDIR] = {"isDirectory", "function(path: string): boolean - Returns whether an entry is a directory", NN_DIRECT},
|
||||||
|
[NN_FSNUM_SIZE] = {"size", "function(path: string): integer - Returns the size of an entry", NN_DIRECT},
|
||||||
|
[NN_FSNUM_LASTMODIFIED] = {"lastModified", "function(path: string): integer - Returns the UNIX timestamp of the last modified time", NN_DIRECT},
|
||||||
|
[NN_FSNUM_MKDIR] = {"makeDirectory", "function(path: string): boolean - Create a directory, recursively. Does not fail if directory already exists", NN_DIRECT},
|
||||||
|
[NN_FSNUM_REMOVE] = {"remove", "function(path: string): boolean - Recursively deletes an entry", NN_DIRECT},
|
||||||
|
[NN_FSNUM_RENAME] = {"rename", "function(from: string, to: string): boolean - Renames/moves an entry", NN_DIRECT},
|
||||||
|
};
|
||||||
|
nn_Exit e = nn_setComponentMethodsArray(c, methods, NN_FSNUM_COUNT);
|
||||||
|
if(e) {
|
||||||
|
nn_dropComponent(c);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
nn_Context *ctx = &universe->ctx;
|
||||||
|
nn_FSState *fsstate = nn_alloc(ctx, sizeof(*fsstate));
|
||||||
|
if(fsstate == NULL) {
|
||||||
|
nn_dropComponent(c);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
fsstate->ctx = ctx;
|
||||||
|
fsstate->fs = *fs;
|
||||||
|
fsstate->state = state;
|
||||||
|
fsstate->handler = handler;
|
||||||
|
nn_setComponentState(c, fsstate);
|
||||||
|
nn_setComponentHandler(c, nn_fsHandler);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|||||||
125
src/neonucleus.h
125
src/neonucleus.h
@@ -50,13 +50,13 @@ extern "C" {
|
|||||||
#define NN_MAX_PATH 256
|
#define NN_MAX_PATH 256
|
||||||
// the maximum amount of bytes which can be read from a file.
|
// the maximum amount of bytes which can be read from a file.
|
||||||
// You are given a buffer you are meant to fill at least partially, this is simply the limit of that buffer's size.
|
// You are given a buffer you are meant to fill at least partially, this is simply the limit of that buffer's size.
|
||||||
#define NN_MAX_READ 65536
|
#define NN_MAX_READ 8192
|
||||||
// the maximum size of a label
|
// the maximum size of a label
|
||||||
#define NN_MAX_LABEL 256
|
#define NN_MAX_LABEL 256
|
||||||
// maximum size of a wakeup message
|
// maximum size of a wakeup message
|
||||||
#define NN_MAX_WAKEUPMSG 2048
|
#define NN_MAX_WAKEUPMSG 2048
|
||||||
// the maximum amount of file descriptors that can be open simultaneously
|
// the maximum amount of file descriptors that can be open simultaneously
|
||||||
#define NN_MAX_OPENFILES 128
|
#define NN_MAX_OPENFILES 16
|
||||||
// the maximum amount of userdata that can be sent simultaneously.
|
// the maximum amount of userdata that can be sent simultaneously.
|
||||||
#define NN_MAX_USERDATA 64
|
#define NN_MAX_USERDATA 64
|
||||||
// maximum size of a signal, computed the same as modem packet costs.
|
// maximum size of a signal, computed the same as modem packet costs.
|
||||||
@@ -227,6 +227,22 @@ void *nn_alloc(nn_Context *ctx, size_t size);
|
|||||||
void nn_free(nn_Context *ctx, void *memory, size_t size);
|
void nn_free(nn_Context *ctx, void *memory, size_t size);
|
||||||
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize);
|
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
|
char *nn_strdup(nn_Context *ctx, const char *s);
|
||||||
|
void nn_strfree(nn_Context *ctx, char *s);
|
||||||
|
|
||||||
|
typedef struct nn_Lock nn_Lock;
|
||||||
|
|
||||||
|
nn_Lock *nn_createLock(nn_Context *ctx);
|
||||||
|
void nn_destroyLock(nn_Context *ctx, nn_Lock *lock);
|
||||||
|
void nn_lock(nn_Context *ctx, nn_Lock *lock);
|
||||||
|
void nn_unlock(nn_Context *ctx, nn_Lock *lock);
|
||||||
|
|
||||||
|
double nn_currentTime(nn_Context *ctx);
|
||||||
|
|
||||||
|
size_t nn_rand(nn_Context *ctx);
|
||||||
|
double nn_randf(nn_Context *ctx);
|
||||||
|
double nn_randfi(nn_Context *ctx);
|
||||||
|
|
||||||
typedef char nn_uuid[37];
|
typedef char nn_uuid[37];
|
||||||
void nn_randomUUID(nn_Context *ctx, nn_uuid uuid);
|
void nn_randomUUID(nn_Context *ctx, nn_uuid uuid);
|
||||||
|
|
||||||
@@ -895,16 +911,114 @@ typedef struct nn_Filesystem {
|
|||||||
// the maximum capacity of the filesystem
|
// the maximum capacity of the filesystem
|
||||||
size_t spaceTotal;
|
size_t spaceTotal;
|
||||||
// how many read calls can be done per tick
|
// how many read calls can be done per tick
|
||||||
// list, exists, size, lastModified, isDirectory, seek also count as reads.
|
// seek also count as reads.
|
||||||
double readsPerTick;
|
double readsPerTick;
|
||||||
// how many write calls can be done per tick
|
// how many write calls can be done per tick
|
||||||
// makeDirectory, open, remove and rename also count as writes.
|
|
||||||
double writesPerTick;
|
double writesPerTick;
|
||||||
// The energy cost of an actual read/write.
|
// The energy cost of an actual read/write.
|
||||||
// It is per-byte, so if a read returns 4096 bytes, then this cost is multiplied by 4096.
|
// It is per-byte, so if a read returns 4096 bytes, then this cost is multiplied by 4096.
|
||||||
double dataEnergyCost;
|
double dataEnergyCost;
|
||||||
} nn_Filesystem;
|
} nn_Filesystem;
|
||||||
|
|
||||||
|
typedef enum nn_FSAction {
|
||||||
|
NN_FS_DROP,
|
||||||
|
|
||||||
|
// drive metadata
|
||||||
|
NN_FS_SPACEUSED,
|
||||||
|
NN_FS_GETLABEL,
|
||||||
|
NN_FS_SETLABEL,
|
||||||
|
NN_FS_ISRO,
|
||||||
|
|
||||||
|
// for file I/O
|
||||||
|
NN_FS_OPEN,
|
||||||
|
NN_FS_READ,
|
||||||
|
NN_FS_WRITE,
|
||||||
|
NN_FS_SEEK,
|
||||||
|
NN_FS_CLOSE,
|
||||||
|
|
||||||
|
// for list
|
||||||
|
NN_FS_OPENDIR,
|
||||||
|
NN_FS_READDIR,
|
||||||
|
NN_FS_CLOSEDIR,
|
||||||
|
|
||||||
|
// checking metadata
|
||||||
|
NN_FS_STAT,
|
||||||
|
// make directory, recursively
|
||||||
|
NN_FS_MKDIR,
|
||||||
|
|
||||||
|
// rename, if renamed to NULL then remove
|
||||||
|
NN_FS_RENAME,
|
||||||
|
} nn_FSAction;
|
||||||
|
|
||||||
|
typedef enum nn_FSWhence {
|
||||||
|
NN_SEEK_SET,
|
||||||
|
NN_SEEK_CUR,
|
||||||
|
NN_SEEK_END,
|
||||||
|
} nn_FSWhence;
|
||||||
|
|
||||||
|
typedef struct nn_FSRequest {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_Computer *computer;
|
||||||
|
void *state;
|
||||||
|
const nn_Filesystem *fs;
|
||||||
|
nn_FSAction action;
|
||||||
|
int fd;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
const char *path;
|
||||||
|
const char *mode;
|
||||||
|
} open;
|
||||||
|
struct {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
} read;
|
||||||
|
struct {
|
||||||
|
const char *buf;
|
||||||
|
size_t len;
|
||||||
|
} write;
|
||||||
|
struct {
|
||||||
|
nn_FSWhence whence;
|
||||||
|
// set to new offset
|
||||||
|
int off;
|
||||||
|
} seek;
|
||||||
|
const char *opendir;
|
||||||
|
struct {
|
||||||
|
char *buf;
|
||||||
|
// set to length of entry name
|
||||||
|
size_t len;
|
||||||
|
} readdir;
|
||||||
|
struct {
|
||||||
|
// set to NULL if missing
|
||||||
|
const char *path;
|
||||||
|
// whether it is a directory
|
||||||
|
bool isDirectory;
|
||||||
|
// in seconds. Result will be multiplied by 1000.
|
||||||
|
// This is because OpenOS code is garbage.
|
||||||
|
size_t lastModified;
|
||||||
|
// size. 0 for directories.
|
||||||
|
size_t size;
|
||||||
|
} stat;
|
||||||
|
struct {
|
||||||
|
const char *from;
|
||||||
|
// if NULL, delete from, recursively.
|
||||||
|
const char *to;
|
||||||
|
} rename;
|
||||||
|
const char *mkdir;
|
||||||
|
struct {
|
||||||
|
const char *buf;
|
||||||
|
size_t len;
|
||||||
|
} setlabel;
|
||||||
|
struct {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
} getlabel;
|
||||||
|
bool isReadonly;
|
||||||
|
size_t spaceUsed;
|
||||||
|
};
|
||||||
|
} nn_FSRequest;
|
||||||
|
|
||||||
|
typedef nn_Exit (nn_FSHandler)(nn_FSRequest *request);
|
||||||
|
|
||||||
// 4 Tiers.
|
// 4 Tiers.
|
||||||
// 0 - Tier 1 equivalent
|
// 0 - Tier 1 equivalent
|
||||||
// 1 - Tier 2 equivalent
|
// 1 - Tier 2 equivalent
|
||||||
@@ -950,6 +1064,9 @@ typedef struct nn_VFilesystem {
|
|||||||
nn_VFileNode *image;
|
nn_VFileNode *image;
|
||||||
} nn_VFilesystem;
|
} nn_VFilesystem;
|
||||||
|
|
||||||
|
nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler);
|
||||||
|
nn_Component *nn_createVFilesystem(nn_Universe *universe, const char *address, const nn_VFilesystem *vfs, const nn_Filesystem *fs);
|
||||||
|
|
||||||
// Drive class
|
// Drive class
|
||||||
|
|
||||||
typedef struct nn_Drive {
|
typedef struct nn_Drive {
|
||||||
|
|||||||
Reference in New Issue
Block a user