mirror of
https://github.com/NeoFlock/neonucleus.git
synced 2026-02-15 04:03:49 +01:00
WIP fs impl
This commit is contained in:
parent
c0d965be26
commit
9c5e4a3d5b
@ -73,7 +73,7 @@ local function dofile(file)
|
||||
status("> " .. file)
|
||||
local program, reason = raw_loadfile(file)
|
||||
if program then
|
||||
local result = table.pack(pcall(program))
|
||||
local result = table.pack(xpcall(program, debug.traceback))
|
||||
if result[1] then
|
||||
return table.unpack(result, 2, result.n)
|
||||
else
|
||||
|
||||
@ -31,13 +31,13 @@ void *luaArch_alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
|
||||
return NULL;
|
||||
}
|
||||
if(ptr == NULL) {
|
||||
if(arch->freeMem < nsize) return NULL;
|
||||
//if(arch->freeMem < nsize) return NULL;
|
||||
void *mem = malloc(nsize);
|
||||
if(mem == NULL) return NULL;
|
||||
arch->freeMem -= nsize;
|
||||
return mem;
|
||||
}
|
||||
if(arch->freeMem + osize < nsize) return NULL;
|
||||
//if(arch->freeMem + osize < nsize) return NULL;
|
||||
void *mem = realloc(ptr, nsize);
|
||||
if(mem == NULL) return NULL;
|
||||
arch->freeMem += osize;
|
||||
@ -101,6 +101,19 @@ static void luaArch_nnToLua(luaArch *arch, size_t nnIdx) {
|
||||
lua_pushboolean(L, nn_toboolean(C, nnIdx));
|
||||
return;
|
||||
}
|
||||
if(nn_istable(C, nnIdx)) {
|
||||
size_t start = nn_getstacksize(C);
|
||||
size_t len;
|
||||
nn_dumptable(C, nnIdx, &len);
|
||||
lua_createtable(L, 0, len);
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
luaArch_nnToLua(arch, start + i * 2);
|
||||
luaArch_nnToLua(arch, start + i * 2 + 1);
|
||||
lua_settable(L, -3);
|
||||
}
|
||||
nn_popn(C, len * 2);
|
||||
return;
|
||||
}
|
||||
|
||||
luaL_error(L, "bad NN value: %s", nn_typenameof(C, nnIdx));
|
||||
}
|
||||
@ -214,6 +227,12 @@ static int luaArch_computer_shutdown(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int luaArch_computer_isOverused(lua_State *L) {
|
||||
nn_Computer *c = luaArch_from(L)->computer;
|
||||
lua_pushboolean(L, nn_componentsOverused(c));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luaArch_component_list(lua_State *L) {
|
||||
luaArch *arch = luaArch_from(L);
|
||||
lua_createtable(L, 64, 0);
|
||||
@ -385,6 +404,8 @@ static void luaArch_loadEnv(lua_State *L) {
|
||||
lua_setfield(L, computer, "setArchitecture");
|
||||
lua_pushcfunction(L, luaArch_computer_shutdown);
|
||||
lua_setfield(L, computer, "shutdown");
|
||||
lua_pushcfunction(L, luaArch_computer_isOverused);
|
||||
lua_setfield(L, computer, "isOverused");
|
||||
lua_setglobal(L, "computer");
|
||||
lua_createtable(L, 0, 10);
|
||||
int component = lua_gettop(L);
|
||||
|
||||
@ -23,7 +23,8 @@ function coroutine.resume(co, ...)
|
||||
end
|
||||
end
|
||||
|
||||
local clist = component.list
|
||||
local clist, cinvoke, computer, component, print = component.list, component.invoke, computer, component, print
|
||||
debug.print = print
|
||||
|
||||
function component.list(ctype, exact)
|
||||
local list = clist()
|
||||
@ -41,10 +42,26 @@ function component.list(ctype, exact)
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(desired, {__call = next})
|
||||
local key = nil
|
||||
setmetatable(desired, {__call = function()
|
||||
local val
|
||||
key, val = next(desired, key)
|
||||
return key, val
|
||||
end})
|
||||
return desired
|
||||
end
|
||||
|
||||
function component.invoke(address, method, ...)
|
||||
local t = {pcall(cinvoke, address, method, ...)}
|
||||
if computer.energy() <= 0 then sysyield() end -- out of power
|
||||
if computer.isOverused() then sysyield() end -- overused
|
||||
|
||||
if t[1] then
|
||||
return table.unpack(t, 2)
|
||||
end
|
||||
return nil, t[2]
|
||||
end
|
||||
|
||||
local componentCallback = {
|
||||
__call = function(self, ...)
|
||||
return component.invoke(self.address, self.name, ...)
|
||||
@ -126,6 +143,35 @@ function computer.setArchitecture(arch)
|
||||
return ok, err
|
||||
end
|
||||
|
||||
function checkArg(arg, val, ...)
|
||||
local t = {...}
|
||||
for i=1,#t do
|
||||
if type(val) == t[i] then return end
|
||||
end
|
||||
error("bad argument #" .. arg .. " (" .. table.concat(t, ", ") .. ") expected", 2)
|
||||
end
|
||||
|
||||
-- HORRENDOUS approximation
|
||||
unicode = string
|
||||
|
||||
if os.getenv("NN_REPL") == "1" then
|
||||
while true do
|
||||
io.write("lua> ")
|
||||
io.flush()
|
||||
local l = io.read("l")
|
||||
if not l then break end
|
||||
local f, err = load("return " .. l, "=repl")
|
||||
if f then
|
||||
print(f())
|
||||
else
|
||||
f, err = load(l, "=repl")
|
||||
if f then f() else print(err) end
|
||||
end
|
||||
end
|
||||
io.write("\n")
|
||||
print("exiting repl")
|
||||
end
|
||||
|
||||
local eeprom = component.list("eeprom", true)()
|
||||
assert(eeprom, "missing firmware")
|
||||
|
||||
|
||||
214
rewrite/main.c
214
rewrite/main.c
@ -4,11 +4,44 @@
|
||||
// Error handling has been omitted in most places.
|
||||
|
||||
#include "neonucleus.h"
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
nn_Architecture getLuaArch();
|
||||
|
||||
#if defined(NN_WINDOWS)
|
||||
#define NE_PATHSEP '\\'
|
||||
#include <windows.h>
|
||||
#error "Windows is not supported yet"
|
||||
#elif defined(NN_POSIX)
|
||||
#define NE_PATHSEP '/'
|
||||
#include <dirent.h>
|
||||
#include <unistd.h>
|
||||
|
||||
typedef DIR ne_dir;
|
||||
|
||||
ne_dir *ne_opendir(const char *path) {
|
||||
return opendir(path);
|
||||
}
|
||||
|
||||
void ne_closedir(ne_dir *dir) {
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
bool ne_readdir(ne_dir *dir, char path[NN_MAX_PATH]) {
|
||||
struct dirent *ent = readdir(dir);
|
||||
if(ent == NULL) return true;
|
||||
strncpy(path, ent->d_name, NN_MAX_PATH-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ne_exists(const char *path) {
|
||||
return access(path, F_OK) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const char minBIOS[] = {
|
||||
#embed "minBIOS.lua"
|
||||
,'\0'
|
||||
@ -38,6 +71,161 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
typedef struct ne_FsState {
|
||||
char path[NN_MAX_PATH];
|
||||
bool isReadonly;
|
||||
FILE *files[NN_MAX_OPENFILES];
|
||||
ne_dir *dir;
|
||||
} ne_FsState;
|
||||
|
||||
void ne_fsState_truepath(ne_FsState *state, char truepath[NN_MAX_PATH], const char *path) {
|
||||
snprintf(truepath, sizeof(char) * NN_MAX_PATH, "%s%c%s", state->path, NE_PATHSEP, path);
|
||||
for(size_t i = 0; truepath[i] != 0; i++) {
|
||||
if(truepath[i] == '/') truepath[i] = NE_PATHSEP;
|
||||
}
|
||||
}
|
||||
|
||||
nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) {
|
||||
nn_Computer *C = req->computer;
|
||||
ne_FsState *state = req->instance;
|
||||
FILE *f;
|
||||
char truepath[NN_MAX_PATH];
|
||||
|
||||
switch(req->action) {
|
||||
case NN_FS_DROP:
|
||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||
if(state->files[i] != NULL) fclose(state->files[i]);
|
||||
}
|
||||
if(state->dir != NULL) {
|
||||
ne_closedir(state->dir);
|
||||
}
|
||||
free(state);
|
||||
return NN_OK;
|
||||
case NN_FS_SPACEUSED:
|
||||
req->size = 0;
|
||||
return NN_OK;
|
||||
case NN_FS_GETLABEL:
|
||||
req->strarg1 = NULL;
|
||||
return NN_OK;
|
||||
case NN_FS_SETLABEL:
|
||||
req->strarg1 = NULL;
|
||||
return NN_OK;
|
||||
case NN_FS_OPEN:;
|
||||
req->fd = NN_MAX_OPENFILES;
|
||||
|
||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||
if(state->files[i] == NULL) {
|
||||
req->fd = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(req->fd == NN_MAX_OPENFILES) {
|
||||
nn_setError(C, "too many open handles");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
|
||||
const char *path = req->strarg1;
|
||||
const char *mode = req->strarg2;
|
||||
switch(mode[0]) {
|
||||
case 'r':
|
||||
mode = "rb";
|
||||
case 'w':
|
||||
mode = "wb";
|
||||
case 'a':
|
||||
mode = "ab";
|
||||
default:
|
||||
mode = "rb";
|
||||
}
|
||||
ne_fsState_truepath(state, truepath, path);
|
||||
|
||||
f = fopen(truepath, mode);
|
||||
if(f == NULL) {
|
||||
nn_setError(C, strerror(errno));
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
state->files[req->fd] = f;
|
||||
return NN_OK;
|
||||
case NN_FS_CLOSE:
|
||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
||||
nn_setError(C, "bad file descriptor");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
f = state->files[req->fd];
|
||||
if(f == NULL) {
|
||||
nn_setError(C, "bad file descriptor");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fclose(f);
|
||||
state->files[req->fd] = NULL;
|
||||
return NN_OK;
|
||||
case NN_FS_READ:
|
||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
||||
nn_setError(C, "bad file descriptor");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
f = state->files[req->fd];
|
||||
if(f == NULL) {
|
||||
nn_setError(C, "bad file descriptor");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(feof(f)) {
|
||||
req->strarg1 = NULL;
|
||||
} else {
|
||||
req->strarg1len = fread(req->strarg1, sizeof(char), req->strarg1len, f);
|
||||
}
|
||||
return NN_OK;
|
||||
case NN_FS_WRITE:
|
||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
||||
nn_setError(C, "bad file descriptor");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
f = state->files[req->fd];
|
||||
if(f == NULL) {
|
||||
nn_setError(C, "bad file descriptor");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fwrite(req->strarg1, sizeof(char), req->strarg1len, f);
|
||||
return NN_OK;
|
||||
case NN_FS_OPENDIR:
|
||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
||||
state->dir = ne_opendir(truepath);
|
||||
if(state->dir == NULL) {
|
||||
nn_setError(C, strerror(errno));
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
return NN_OK;
|
||||
case NN_FS_READDIR:;
|
||||
char ent[NN_MAX_PATH];
|
||||
if(ne_readdir(state->dir, ent)) {
|
||||
req->strarg1 = NULL;
|
||||
return NN_OK;
|
||||
}
|
||||
strcpy(req->strarg1, ent);
|
||||
req->strarg1len = strlen(ent);
|
||||
return NN_OK;
|
||||
case NN_FS_CLOSEDIR:
|
||||
ne_closedir(state->dir);
|
||||
state->dir = NULL;
|
||||
return NN_OK;
|
||||
case NN_FS_EXISTS:
|
||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
||||
req->size = ne_exists(truepath) ? 1 : 0;
|
||||
return NN_OK;
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
ne_FsState *ne_newFS(const char *path, bool readonly) {
|
||||
ne_FsState *fs = malloc(sizeof(*fs));
|
||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
||||
fs->files[i] = NULL;
|
||||
}
|
||||
sprintf(fs->path, "data%c%s", NE_PATHSEP, path);
|
||||
fs->isReadonly = readonly;
|
||||
return fs;
|
||||
}
|
||||
|
||||
int main() {
|
||||
nn_Context ctx;
|
||||
nn_initContext(&ctx);
|
||||
@ -53,19 +241,6 @@ int main() {
|
||||
};
|
||||
nn_ComponentType *ctype = nn_createComponentType(u, "sandbox", NULL, sandboxMethods, sandbox_handler);
|
||||
|
||||
nn_EEPROM eeprom = {
|
||||
.size = 4 * NN_KiB,
|
||||
.dataSize = NN_KiB,
|
||||
.readCallCost = 1,
|
||||
.readEnergyCost = 0,
|
||||
.readDataCallCost = 1,
|
||||
.readDataEnergyCost = 0,
|
||||
.writeCallCost = 1,
|
||||
.writeEnergyCost = 0,
|
||||
.writeDataCallCost = 1,
|
||||
.writeDataEnergyCost = 0,
|
||||
.handler = NULL,
|
||||
};
|
||||
nn_VEEPROM veeprom = {
|
||||
.code = minBIOS,
|
||||
.codelen = strlen(minBIOS),
|
||||
@ -77,7 +252,12 @@ int main() {
|
||||
.isReadonly = false,
|
||||
};
|
||||
|
||||
nn_ComponentType *etype = nn_createVEEPROM(u, &eeprom, &veeprom);
|
||||
nn_ComponentType *etype = nn_createVEEPROM(u, &nn_defaultEEPROM, &veeprom);
|
||||
nn_ComponentType *fstype[5];
|
||||
fstype[0] = nn_createFilesystem(u, &nn_defaultFloppy, ne_fsState_handler, NULL);
|
||||
for(size_t i = 1; i < 5; i++) {
|
||||
fstype[i] = nn_createFilesystem(u, &nn_defaultFilesystems[i-1], ne_fsState_handler, NULL);
|
||||
}
|
||||
|
||||
nn_Computer *c = nn_createComputer(u, NULL, "computer0", 8 * NN_MiB, 256, 256);
|
||||
|
||||
@ -87,9 +267,12 @@ int main() {
|
||||
nn_addComponent(c, ctype, "sandbox", -1, NULL);
|
||||
nn_addComponent(c, etype, "eeprom", 0, etype);
|
||||
|
||||
ne_FsState *mainFS = ne_newFS("OpenOS", false);
|
||||
nn_addComponent(c, fstype[1], "mainFS", 2, mainFS);
|
||||
|
||||
while(true) {
|
||||
nn_Exit e = nn_tick(c);
|
||||
if(e != NN_OK && e != NN_EBUSY) {
|
||||
if(e != NN_OK) {
|
||||
nn_setErrorFromExit(c, e);
|
||||
printf("error: %s\n", nn_getError(c));
|
||||
goto cleanup;
|
||||
@ -120,6 +303,7 @@ cleanup:;
|
||||
nn_destroyComputer(c);
|
||||
nn_destroyComponentType(ctype);
|
||||
nn_destroyComponentType(etype);
|
||||
for(size_t i = 0; i < 5; i++) nn_destroyComponentType(fstype[i]);
|
||||
// rip the universe
|
||||
nn_destroyUniverse(u);
|
||||
return 0;
|
||||
|
||||
@ -12,6 +12,9 @@
|
||||
// we need the header.
|
||||
#include "neonucleus.h"
|
||||
|
||||
// to use the numerical accuracy better
|
||||
#define NN_COMPONENT_CALLBUDGET 10000
|
||||
|
||||
#ifdef NN_ATOMIC_NONE
|
||||
typedef size_t nn_refc_t;
|
||||
|
||||
@ -41,25 +44,6 @@ bool nn_decRef(nn_refc_t *refc) {
|
||||
}
|
||||
#endif
|
||||
|
||||
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
//define something for Windows (32-bit and 64-bit, this part is common)
|
||||
#define NN_WINDOWS
|
||||
#elif __APPLE__
|
||||
#define NN_MACOS
|
||||
#elif __linux__
|
||||
#define NN_LINUX
|
||||
#endif
|
||||
|
||||
#if __unix__ // all unices not caught above
|
||||
// Unix
|
||||
#define NN_UNIX
|
||||
#define NN_POSIX
|
||||
#elif defined(_POSIX_VERSION)
|
||||
// POSIX
|
||||
#define NN_POSIX
|
||||
#endif
|
||||
|
||||
typedef struct nn_Lock nn_Lock;
|
||||
|
||||
// the special includes
|
||||
@ -328,6 +312,43 @@ void nn_crc32ChecksumBytes(unsigned int checksum, char out[8]) {
|
||||
}
|
||||
}
|
||||
|
||||
bool nn_simplifyPath(const char original[NN_MAX_PATH], char simplified[NN_MAX_PATH]) {
|
||||
// pass 1: check for valid characters, and \ becomes /
|
||||
for(size_t i = 0; true; i++) {
|
||||
if(original[i] == '\\') simplified[i] = '/';
|
||||
else simplified[i] = original[i];
|
||||
if(original[i] == '\0') break;
|
||||
}
|
||||
// get rid of //, starting / and ending /
|
||||
{
|
||||
while(simplified[0] == '/') {
|
||||
for(size_t i = 1; true; i++) {
|
||||
simplified[i-1] = simplified[i];
|
||||
if(simplified[i] == '\0') break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t j = 0;
|
||||
for(size_t i = 0; simplified[i] != '\0'; i++) {
|
||||
if(simplified[i] == '/' && simplified[i+1] == '/') {
|
||||
// simply discard it
|
||||
continue;
|
||||
} else {
|
||||
simplified[j] = simplified[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
simplified[j] = '\0';
|
||||
while(simplified[j-1] == '/') {
|
||||
j--;
|
||||
simplified[j] = '\0';
|
||||
}
|
||||
}
|
||||
// TODO: handle ..
|
||||
// valid
|
||||
return true;
|
||||
}
|
||||
|
||||
int nn_memcmp(const char *a, const char *b, size_t len) {
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
char c = a[i];
|
||||
@ -549,6 +570,7 @@ typedef struct nn_Component {
|
||||
char *address;
|
||||
nn_ComponentType *ctype;
|
||||
int slot;
|
||||
float budgetUsed;
|
||||
void *userdata;
|
||||
void *state;
|
||||
} nn_Component;
|
||||
@ -1024,6 +1046,7 @@ nn_Exit nn_tick(nn_Computer *computer) {
|
||||
return NN_EBADSTATE;
|
||||
}
|
||||
nn_resetCallBudget(computer);
|
||||
nn_resetComponentBudgets(computer);
|
||||
computer->state = NN_RUNNING;
|
||||
nn_ArchitectureRequest req;
|
||||
req.computer = computer;
|
||||
@ -1031,8 +1054,6 @@ nn_Exit nn_tick(nn_Computer *computer) {
|
||||
req.localState = computer->archState;
|
||||
req.action = NN_ARCH_TICK;
|
||||
err = computer->arch.handler(&req);
|
||||
// dont crash on EBUSY because it just means go again
|
||||
if(err == NN_EBUSY) return err;
|
||||
if(err) {
|
||||
computer->state = NN_CRASHED;
|
||||
nn_setErrorFromExit(computer, err);
|
||||
@ -1284,7 +1305,7 @@ nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method)
|
||||
if(nn_strcmp(c.address, address) != 0) continue;
|
||||
|
||||
// minimum cost of a component call
|
||||
nn_callCost(computer, 1);
|
||||
if(computer->callBudget > 0) computer->callBudget--;
|
||||
|
||||
nn_ComponentRequest req;
|
||||
req.typeUserdata = c.ctype->userdata;
|
||||
@ -1340,10 +1361,32 @@ void nn_resetCallBudget(nn_Computer *computer) {
|
||||
}
|
||||
|
||||
bool nn_componentsOverused(nn_Computer *computer) {
|
||||
for(size_t i = 0; i < computer->componentLen; i++) {
|
||||
if(computer->components[i].budgetUsed >= NN_COMPONENT_CALLBUDGET) return true;
|
||||
}
|
||||
if(computer->totalCallBudget == 0) return false;
|
||||
return computer->callBudget == 0;
|
||||
}
|
||||
|
||||
void nn_resetComponentBudgets(nn_Computer *computer) {
|
||||
for(size_t i = 0; i < computer->componentLen; i++) {
|
||||
computer->components[i].budgetUsed = 0;
|
||||
}
|
||||
}
|
||||
bool nn_costComponent(nn_Computer *computer, const char *address, double perTick) {
|
||||
return nn_costComponentN(computer, address, 1, perTick);
|
||||
}
|
||||
|
||||
bool nn_costComponentN(nn_Computer *computer, const char *address, double amount, double perTick) {
|
||||
for(size_t i = 0; i < computer->componentLen; i++) {
|
||||
nn_Component *c = &computer->components[i];
|
||||
if(nn_strcmp(c->address, address) != 0) continue;
|
||||
c->budgetUsed += (NN_COMPONENT_CALLBUDGET * amount) / perTick;
|
||||
return c->budgetUsed >= NN_COMPONENT_CALLBUDGET;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nn_checkstack(nn_Computer *computer, size_t amount) {
|
||||
return computer->stackSize + amount <= NN_MAX_STACK;
|
||||
}
|
||||
@ -1647,6 +1690,7 @@ typedef struct nn_EEPROM_state {
|
||||
nn_Universe *universe;
|
||||
nn_EEPROM eeprom;
|
||||
void *userdata;
|
||||
nn_EEPROMHandler *handler;
|
||||
} nn_EEPROM_state;
|
||||
|
||||
typedef struct nn_VEEPROM_state {
|
||||
@ -1685,7 +1729,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
return NN_OK;
|
||||
case NN_COMP_DEINIT:
|
||||
ereq.action = NN_EEPROM_DROP;
|
||||
return state->eeprom.handler(&ereq);
|
||||
return state->handler(&ereq);
|
||||
case NN_COMP_ENABLED:
|
||||
req->methodEnabled = true;
|
||||
return NN_OK;
|
||||
@ -1698,19 +1742,20 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
}
|
||||
if(nn_strcmp(method, "isReadOnly") == 0) {
|
||||
ereq.action = NN_EEPROM_ISREADONLY;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushbool(computer, ereq.buflen != 0);
|
||||
}
|
||||
if(nn_strcmp(method, "getChecksum") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
// yup, on-stack.
|
||||
// Perhaps in the future we'll make it heap-allocated.
|
||||
char buf[state->eeprom.size];
|
||||
ereq.action = NN_EEPROM_GET;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = state->eeprom.size;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
unsigned int chksum = nn_computeCRC32(buf, ereq.buflen);
|
||||
char encoded[8];
|
||||
@ -1719,6 +1764,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
return nn_pushlstring(computer, encoded, 8);
|
||||
}
|
||||
if(nn_strcmp(method, "makeReadonly") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
// 1st argument is a string, which is the checksum we're meant to have
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
@ -1740,7 +1786,7 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
ereq.action = NN_EEPROM_GET;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = state->eeprom.size;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
unsigned int chksum = nn_computeCRC32(buf, ereq.buflen);
|
||||
char encoded[8];
|
||||
@ -1751,23 +1797,26 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
}
|
||||
|
||||
ereq.action = NN_EEPROM_MAKEREADONLY;
|
||||
e = state->eeprom.handler(&ereq);
|
||||
e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
|
||||
req->returnCount = 1;
|
||||
return nn_pushbool(computer, true);
|
||||
}
|
||||
if(nn_strcmp(method, "getLabel") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
char buf[NN_MAX_LABEL];
|
||||
ereq.action = NN_EEPROM_GETLABEL;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = NN_MAX_LABEL;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
if(ereq.buf == NULL) return nn_pushnull(computer);
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "setLabel") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
@ -1784,46 +1833,50 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
ereq.action = NN_EEPROM_SETLABEL;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = len;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "get") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
// yup, on-stack.
|
||||
// Perhaps in the future we'll make it heap-allocated.
|
||||
char buf[state->eeprom.size];
|
||||
ereq.action = NN_EEPROM_GET;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = state->eeprom.size;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "getData") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
// yup, on-stack.
|
||||
// Perhaps in the future we'll make it heap-allocated.
|
||||
char buf[state->eeprom.dataSize];
|
||||
ereq.action = NN_EEPROM_GETDATA;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = state->eeprom.dataSize;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "getArchitecture") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
char buf[NN_MAX_ARCHNAME];
|
||||
ereq.action = NN_EEPROM_GETARCH;
|
||||
ereq.buf = buf;
|
||||
ereq.buflen = NN_MAX_ARCHNAME;
|
||||
nn_Exit e = state->eeprom.handler(&ereq);
|
||||
nn_Exit e = state->handler(&ereq);
|
||||
if(e) return e;
|
||||
req->returnCount = 1;
|
||||
return nn_pushlstring(computer, buf, ereq.buflen);
|
||||
}
|
||||
if(nn_strcmp(method, "set") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
@ -1842,9 +1895,10 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
// DO NOT MODIFY IT!!!!
|
||||
ereq.buf = (char*)s;
|
||||
ereq.buflen = len;
|
||||
return state->eeprom.handler(&ereq);
|
||||
return state->handler(&ereq);
|
||||
}
|
||||
if(nn_strcmp(method, "setData") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
@ -1863,9 +1917,10 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
// DO NOT MODIFY IT!!!!
|
||||
ereq.buf = (char*)s;
|
||||
ereq.buflen = len;
|
||||
return state->eeprom.handler(&ereq);
|
||||
return state->handler(&ereq);
|
||||
}
|
||||
if(nn_strcmp(method, "setArchitecture") == 0) {
|
||||
nn_costComponent(computer, req->compAddress, 1);
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
@ -1884,20 +1939,30 @@ nn_Exit nn_eeprom_handler(nn_ComponentRequest *req) {
|
||||
// DO NOT MODIFY IT!!!!
|
||||
ereq.buf = (char*)s;
|
||||
ereq.buflen = len;
|
||||
return state->eeprom.handler(&ereq);
|
||||
return state->handler(&ereq);
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, void *userdata) {
|
||||
nn_EEPROM nn_defaultEEPROM = (nn_EEPROM) {
|
||||
.size = 4 * NN_KiB,
|
||||
.dataSize = 256,
|
||||
.readEnergyCost = 10,
|
||||
.writeEnergyCost = 100,
|
||||
.readDataEnergyCost = 10,
|
||||
.writeDataEnergyCost = 50,
|
||||
};
|
||||
|
||||
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata) {
|
||||
nn_Context ctx = universe->ctx;
|
||||
nn_EEPROM_state *state = nn_alloc(&ctx, sizeof(*state));
|
||||
if(state == NULL) return NULL;
|
||||
state->universe = universe;
|
||||
state->eeprom = *eeprom;
|
||||
state->userdata = userdata;
|
||||
state->handler = handler;
|
||||
const nn_Method methods[] = {
|
||||
{"getSize", "function(): number - Get the storage capacity of the EEPROM.", NN_DIRECT},
|
||||
{"getDataSize", "function(): number - Get the storage capacity of the EEPROM data.", NN_DIRECT},
|
||||
@ -2006,9 +2071,7 @@ nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eepro
|
||||
state->archlen = archlen;
|
||||
nn_memcpy(state->arch, vmem->arch, sizeof(char) * archlen);
|
||||
|
||||
nn_EEPROM neeprom = *eeprom;
|
||||
neeprom.handler = nn_veeprom_handler;
|
||||
nn_ComponentType *ty = nn_createEEPROM(universe, &neeprom, state);
|
||||
nn_ComponentType *ty = nn_createEEPROM(universe, eeprom, nn_veeprom_handler, state);
|
||||
if(ty == NULL) goto fail;
|
||||
return ty;
|
||||
fail:;
|
||||
@ -2018,3 +2081,395 @@ fail:;
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
typedef struct nn_Filesystem_state {
|
||||
nn_Universe *universe;
|
||||
void *userdata;
|
||||
nn_FilesystemHandler *handler;
|
||||
nn_Filesystem fs;
|
||||
} nn_Filesystem_state;
|
||||
|
||||
nn_Filesystem nn_defaultFilesystems[4] = {
|
||||
(nn_Filesystem) {
|
||||
.spaceTotal = 1 * NN_MiB,
|
||||
.readsPerTick = 4,
|
||||
.writesPerTick = 2,
|
||||
.dataEnergyCost = 256.0 / NN_MiB,
|
||||
},
|
||||
(nn_Filesystem) {
|
||||
.spaceTotal = 2 * NN_MiB,
|
||||
.readsPerTick = 4,
|
||||
.writesPerTick = 2,
|
||||
.dataEnergyCost = 512.0 / NN_MiB,
|
||||
},
|
||||
(nn_Filesystem) {
|
||||
.spaceTotal = 4 * NN_MiB,
|
||||
.readsPerTick = 7,
|
||||
.writesPerTick = 3,
|
||||
.dataEnergyCost = 1024.0 / NN_MiB,
|
||||
},
|
||||
(nn_Filesystem) {
|
||||
.spaceTotal = 8 * NN_MiB,
|
||||
.readsPerTick = 13,
|
||||
.writesPerTick = 5,
|
||||
.dataEnergyCost = 2048.0 / NN_MiB,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
nn_Filesystem nn_defaultFloppy = (nn_Filesystem) {
|
||||
.spaceTotal = 512 * NN_KiB,
|
||||
.readsPerTick = 1,
|
||||
.writesPerTick = 1,
|
||||
.dataEnergyCost = 8.0 / NN_MiB,
|
||||
};
|
||||
|
||||
nn_Exit nn_filesystem_handler(nn_ComponentRequest *req) {
|
||||
nn_Filesystem_state *state = req->typeUserdata;
|
||||
void *instance = req->compUserdata;
|
||||
// NULL for FREETYPE
|
||||
nn_Computer *computer = req->computer;
|
||||
nn_Context ctx = state->universe->ctx;
|
||||
|
||||
nn_FilesystemRequest fsreq;
|
||||
fsreq.userdata = state->userdata;
|
||||
fsreq.instance = instance;
|
||||
fsreq.computer = computer;
|
||||
fsreq.fsConf = &state->fs;
|
||||
|
||||
const char *method = req->methodCalled;
|
||||
nn_Exit err;
|
||||
|
||||
switch(req->action) {
|
||||
case NN_COMP_FREETYPE:
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
break;
|
||||
case NN_COMP_INIT:
|
||||
return NN_OK;
|
||||
case NN_COMP_DEINIT:
|
||||
fsreq.action = NN_FS_DROP;
|
||||
return state->handler(&fsreq);
|
||||
case NN_COMP_ENABLED:
|
||||
req->methodEnabled = true;
|
||||
return NN_OK;
|
||||
case NN_COMP_CALL:
|
||||
if(nn_strcmp(method, "spaceTotal") == 0) {
|
||||
req->returnCount = 1;
|
||||
return nn_pushnumber(computer, state->fs.spaceTotal);
|
||||
}
|
||||
if(nn_strcmp(method, "spaceUsed") == 0) {
|
||||
fsreq.action = NN_FS_SPACEUSED;
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
return nn_pushnumber(computer, fsreq.size);
|
||||
}
|
||||
if(nn_strcmp(method, "isReadOnly") == 0) {
|
||||
fsreq.action = NN_FS_ISREADONLY;
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
return nn_pushbool(computer, fsreq.size != 0);
|
||||
}
|
||||
if(nn_strcmp(method, "getLabel") == 0) {
|
||||
char buf[NN_MAX_LABEL];
|
||||
fsreq.action = NN_FS_GETLABEL;
|
||||
fsreq.strarg1 = buf;
|
||||
fsreq.strarg1len = NN_MAX_LABEL;
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
|
||||
return nn_pushlstring(computer, buf, fsreq.strarg1len);
|
||||
}
|
||||
if(nn_strcmp(method, "setLabel") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isstring(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fsreq.action = NN_FS_SETLABEL;
|
||||
// DO NOT MODIFY THE BUFFER!!!
|
||||
fsreq.strarg1 = (char *)nn_tolstring(computer, 0, &fsreq.strarg1len);
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
|
||||
return nn_pushlstring(computer, fsreq.strarg1, fsreq.strarg1len);
|
||||
}
|
||||
if(nn_strcmp(method, "open") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isstring(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(nn_getstacksize(computer) < 2) {
|
||||
err = nn_pushstring(computer, "r");
|
||||
if(err) return err;
|
||||
}
|
||||
if(!nn_isstring(computer, 1)) {
|
||||
nn_setError(computer, "bad argument #2 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
size_t pathlen;
|
||||
const char *path = nn_tolstring(computer, 0, &pathlen);
|
||||
if(pathlen >= NN_MAX_PATH) {
|
||||
nn_setError(computer, "path too long");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
char truepath[NN_MAX_PATH];
|
||||
nn_simplifyPath(path, truepath);
|
||||
size_t modelen;
|
||||
const char *mode = nn_tolstring(computer, 1, &modelen);
|
||||
fsreq.action = NN_FS_OPEN;
|
||||
fsreq.strarg1 = truepath;
|
||||
fsreq.strarg1len = nn_strlen(truepath);
|
||||
fsreq.strarg2 = (char *)mode;
|
||||
fsreq.strarg2len = modelen;
|
||||
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
return nn_pushnumber(computer, fsreq.fd);
|
||||
}
|
||||
if(nn_strcmp(method, "close") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isnumber(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fsreq.fd = nn_tonumber(computer, 0);
|
||||
fsreq.action = NN_FS_CLOSE;
|
||||
return state->handler(&fsreq);
|
||||
}
|
||||
if(nn_strcmp(method, "read") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isnumber(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(nn_getstacksize(computer) < 2) {
|
||||
err = nn_pushnumber(computer, NN_MAX_READ);
|
||||
if(err) return err;
|
||||
}
|
||||
if(!nn_isnumber(computer, 1)) {
|
||||
nn_setError(computer, "bad argument #2 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fsreq.action = NN_FS_READ;
|
||||
fsreq.fd = nn_tonumber(computer, 0);
|
||||
size_t requested = nn_tonumber(computer, 1);
|
||||
if(requested > NN_MAX_READ) requested = NN_MAX_READ;
|
||||
char buf[requested];
|
||||
fsreq.strarg1 = buf;
|
||||
fsreq.strarg1len = requested;
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
if(fsreq.strarg1 == NULL) return nn_pushnull(computer);
|
||||
return nn_pushlstring(computer, fsreq.strarg1, fsreq.strarg1len);
|
||||
}
|
||||
if(nn_strcmp(method, "write") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isnumber(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(nn_getstacksize(computer) < 2) {
|
||||
nn_setError(computer, "bad argument #2 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isnumber(computer, 1)) {
|
||||
nn_setError(computer, "bad argument #2 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fsreq.action = NN_FS_WRITE;
|
||||
fsreq.fd = nn_tonumber(computer, 0);
|
||||
fsreq.strarg1 = (char *)nn_tolstring(computer, 1, &fsreq.strarg1len);
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
return nn_pushbool(computer, true);
|
||||
}
|
||||
if(nn_strcmp(method, "seek") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isnumber(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (integer expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(nn_getstacksize(computer) < 2) {
|
||||
err = nn_pushstring(computer, "cur");
|
||||
if(err) return err;
|
||||
}
|
||||
if(!nn_isstring(computer, 1)) {
|
||||
nn_setError(computer, "bad argument #2 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(nn_getstacksize(computer) < 2) {
|
||||
err = nn_pushnumber(computer, 0);
|
||||
if(err) return err;
|
||||
}
|
||||
if(!nn_isnumber(computer, 2)) {
|
||||
nn_setError(computer, "bad argument #2 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
fsreq.action = NN_FS_SEEK;
|
||||
fsreq.fd = nn_tonumber(computer, 0);
|
||||
const char *whence = nn_tostring(computer, 1);
|
||||
fsreq.off = nn_tonumber(computer, 2);
|
||||
|
||||
if(nn_strcmp(whence, "set") == 0) {
|
||||
fsreq.whence = NN_SEEK_SET;
|
||||
} else if(nn_strcmp(whence, "cur") == 0) {
|
||||
fsreq.whence = NN_SEEK_CUR;
|
||||
} else if(nn_strcmp(whence, "end") == 0) {
|
||||
fsreq.whence = NN_SEEK_END;
|
||||
} else {
|
||||
nn_setError(computer, "bad seek whence");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
err = state->handler(&fsreq);
|
||||
return nn_pushnumber(computer, fsreq.off);
|
||||
}
|
||||
if(nn_strcmp(method, "list") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isstring(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
char truepath[NN_MAX_PATH];
|
||||
size_t pathlen;
|
||||
const char *path = nn_tolstring(computer, 0, &pathlen);
|
||||
if(pathlen >= NN_MAX_PATH) {
|
||||
nn_setError(computer, "path too long");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
nn_simplifyPath(path, truepath);
|
||||
int dirfd;
|
||||
fsreq.action = NN_FS_OPENDIR;
|
||||
fsreq.strarg1 = truepath;
|
||||
fsreq.strarg1len = nn_strlen(truepath);
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
dirfd = fsreq.fd;
|
||||
|
||||
// this sucks hard
|
||||
size_t entryCount = 0;
|
||||
while(1) {
|
||||
char entry[NN_MAX_PATH];
|
||||
fsreq.action = NN_FS_READDIR;
|
||||
fsreq.fd = dirfd;
|
||||
fsreq.strarg1 = entry;
|
||||
fsreq.strarg1len = NN_MAX_PATH;
|
||||
|
||||
err = state->handler(&fsreq);
|
||||
if(err) goto list_fail;
|
||||
if(fsreq.strarg1 == NULL) break;
|
||||
|
||||
if(fsreq.strarg1len == 1 && entry[0] == '.') continue;
|
||||
if(fsreq.strarg1len == 2 && entry[0] == '.' && entry[1] == '.') continue;
|
||||
|
||||
err = nn_pushlstring(computer, entry, fsreq.strarg1len);
|
||||
if(err) goto list_fail;
|
||||
entryCount++;
|
||||
}
|
||||
err = nn_pusharraytable(computer, entryCount);
|
||||
if(err) goto list_fail;
|
||||
req->returnCount = 1;
|
||||
fsreq.action = NN_FS_CLOSEDIR;
|
||||
fsreq.fd = dirfd;
|
||||
state->handler(&fsreq);
|
||||
return NN_OK;
|
||||
list_fail:
|
||||
fsreq.action = NN_FS_CLOSEDIR;
|
||||
fsreq.fd = dirfd;
|
||||
state->handler(&fsreq);
|
||||
return err;
|
||||
}
|
||||
if(nn_strcmp(method, "exists") == 0) {
|
||||
if(nn_getstacksize(computer) < 1) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
if(!nn_isstring(computer, 0)) {
|
||||
nn_setError(computer, "bad argument #1 (string expected)");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
char truepath[NN_MAX_PATH];
|
||||
size_t pathlen;
|
||||
const char *path = nn_tolstring(computer, 0, &pathlen);
|
||||
if(pathlen >= NN_MAX_PATH) {
|
||||
nn_setError(computer, "path too long");
|
||||
return NN_EBADCALL;
|
||||
}
|
||||
nn_simplifyPath(path, truepath);
|
||||
|
||||
fsreq.action = NN_FS_EXISTS;
|
||||
fsreq.strarg1 = truepath;
|
||||
fsreq.strarg1len = nn_strlen(truepath);
|
||||
err = state->handler(&fsreq);
|
||||
if(err) return err;
|
||||
req->returnCount = 1;
|
||||
return nn_pushbool(computer, fsreq.size != 0);
|
||||
}
|
||||
}
|
||||
return NN_OK;
|
||||
}
|
||||
|
||||
nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata) {
|
||||
nn_Context ctx = universe->ctx;
|
||||
nn_Filesystem_state *state = nn_alloc(&ctx, sizeof(*state));
|
||||
if(state == NULL) return NULL;
|
||||
state->universe = universe;
|
||||
state->userdata = userdata;
|
||||
state->handler = handler;
|
||||
state->fs = *filesystem;
|
||||
const nn_Method methods[] = {
|
||||
{"spaceTotal", "function(): number - Get the storage capacity of the filesystem.", NN_DIRECT},
|
||||
{"spaceUsed", "function(): number - Get the space used by the files on the drive.", NN_DIRECT},
|
||||
{"isReadOnly", "function(): boolean - Returns whether the drive is read-only.", NN_DIRECT},
|
||||
{"getLabel", "function(): string - Get the filesystem label.", NN_INDIRECT},
|
||||
{"setLabel", "function(label: string): string - Set the filesystem label and return what was actually set, which may be truncated.", NN_INDIRECT},
|
||||
{"open", "function(path: string, mode?: string = 'r'): number - Open a file. Valid modes are 'r' (read-only), 'w' (write-only) and 'a' (append-only).", NN_INDIRECT},
|
||||
{"close", "function(fd: number) - Closes a file.", NN_INDIRECT},
|
||||
{"read", "function(fd: number, count?: number): string? - Reads part of a file. If there is no more data, nothing is returned.", NN_INDIRECT},
|
||||
{"write", "function(fd: number, data: string): boolean - Writes the data to a file. Returns true on success.", NN_INDIRECT},
|
||||
{"seek", "function(fd: number, whence: string, offset: number): number - Seeks a file. Valid whences are 'set' (relative to start), 'cur' (relative to current), 'end' (relative to EoF, backwards). Returns the new position.", NN_INDIRECT},
|
||||
{"list", "function(path: string): string[] - Returns the names of the entries inside of the directory. Directories get a / appended to their names.", NN_INDIRECT},
|
||||
{"exists", "function(path: string): boolean - Checks if there exists an entry at the specified path.", NN_INDIRECT},
|
||||
{"size", "function(path: string) - Gets the size of the entry at the specified path.", NN_INDIRECT},
|
||||
{"remove", "function(path: string): boolean - Removes the entry at the specified path.", NN_INDIRECT},
|
||||
{"rename", "function(from: string, to: string): boolean - Renames or moves an entry to a new location.", NN_INDIRECT},
|
||||
{"isDirectory", "function(path: string): boolean - Checks if the entry at the specified path is a directory.", NN_INDIRECT},
|
||||
{"lastModified", "function(path: string): number - Returns the UNIX timestamp of the time the entry was last modified. This is stored in milliseconds, but is always a multiple of 1000.", NN_INDIRECT},
|
||||
{"makeDirectory", "function(path: string): boolean - Creates a directory. Creates parent directories if necessary.", NN_INDIRECT},
|
||||
{NULL, NULL, NN_INDIRECT},
|
||||
};
|
||||
nn_ComponentType *t = nn_createComponentType(universe, "filesystem", state, methods, nn_filesystem_handler);
|
||||
if(t == NULL) {
|
||||
nn_free(&ctx, state, sizeof(*state));
|
||||
return NULL;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@ -5,6 +5,27 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Platform checking support, to help out users.
|
||||
// Used internally as well.
|
||||
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||
//define something for Windows (32-bit and 64-bit, this part is common)
|
||||
#define NN_WINDOWS
|
||||
#elif __APPLE__
|
||||
#define NN_MACOS
|
||||
#elif __linux__
|
||||
#define NN_LINUX
|
||||
#endif
|
||||
|
||||
#if __unix__ // all unices not caught above
|
||||
// Unix
|
||||
#define NN_UNIX
|
||||
#define NN_POSIX
|
||||
#elif defined(_POSIX_VERSION)
|
||||
// POSIX
|
||||
#define NN_POSIX
|
||||
#endif
|
||||
|
||||
// every C standard header we depend on, conveniently put here
|
||||
#include <stddef.h> // for NULL,
|
||||
#include <stdint.h> // for intptr_t
|
||||
@ -25,7 +46,7 @@ extern "C" {
|
||||
#define NN_ALLOC_ALIGN 16
|
||||
// the maximum amount of items the callstack can have.
|
||||
#define NN_MAX_STACK 256
|
||||
// the maximum size a path is allowed to have
|
||||
// the maximum size a path is allowed to have, including the NULL terminator!
|
||||
#define NN_MAX_PATH 256
|
||||
// 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.
|
||||
@ -206,8 +227,6 @@ typedef enum nn_Exit {
|
||||
NN_EBADCALL,
|
||||
// bad state, the function was called at the wrong time
|
||||
NN_EBADSTATE,
|
||||
// resource busy. If the result of nn_call, you should call it again later.
|
||||
NN_EBUSY,
|
||||
} nn_Exit;
|
||||
|
||||
// This stores necessary data between computers
|
||||
@ -369,6 +388,7 @@ void nn_setComputerState(nn_Computer *computer, nn_ComputerState state);
|
||||
nn_ComputerState nn_getComputerState(nn_Computer *computer);
|
||||
|
||||
// runs a tick of the computer. Make sure to check the state as well!
|
||||
// This automatically resets the component budgets and call budget.
|
||||
nn_Exit nn_tick(nn_Computer *computer);
|
||||
|
||||
typedef struct nn_DeviceInfoEntry {
|
||||
@ -493,10 +513,6 @@ void nn_setCallBudget(nn_Computer *computer, size_t budget);
|
||||
// gets the total call budget
|
||||
size_t nn_getCallBudget(nn_Computer *computer);
|
||||
|
||||
// subtracts from the call budget.
|
||||
// This cannot underflow, it's clamped to 0.
|
||||
void nn_callCost(nn_Computer *computer, size_t callIntensity);
|
||||
|
||||
// returns the remaining call budget
|
||||
size_t nn_callBudgetRemaining(nn_Computer *computer);
|
||||
|
||||
@ -507,6 +523,19 @@ void nn_resetCallBudget(nn_Computer *computer);
|
||||
// At this point, the architecture should exit with a yield.
|
||||
bool nn_componentsOverused(nn_Computer *computer);
|
||||
|
||||
void nn_resetComponentBudgets(nn_Computer *computer);
|
||||
|
||||
// Uses 1/perTick to the component budget.
|
||||
// Upon a full component budget being used for that component, it returns true.
|
||||
// nn_componentsOverused() will also return true.
|
||||
// This indicates the architecture should yield, to throttle the computer for overuse.
|
||||
bool nn_costComponent(nn_Computer *computer, const char *address, double perTick);
|
||||
// Uses amount/perTick to the component budget.
|
||||
// Upon a full component budget being used for that component, it returns true.
|
||||
// nn_componentsOverused() will also return true.
|
||||
// This indicates the architecture should yield, to throttle the computer for overuse.
|
||||
bool nn_costComponentN(nn_Computer *computer, const char *address, double amount, double perTick);
|
||||
|
||||
// call stack operations.
|
||||
// The type system and API are inspired by Lua, as Lua remains the most popular architecture for OpenComputers.
|
||||
// This does support other languages, however it may make some APIs clunky due to the usage of tables and 1-based indexing.
|
||||
@ -664,30 +693,24 @@ typedef struct nn_EEPROMRequest {
|
||||
char *buf;
|
||||
} nn_EEPROMRequest;
|
||||
|
||||
// reads and writes are always 1/1
|
||||
typedef struct nn_EEPROM {
|
||||
// the maximum capacity of the EEPROM
|
||||
size_t size;
|
||||
// the maximum capacity of the EEPROM's associated data
|
||||
size_t dataSize;
|
||||
// the call cost of reading an EEPROM
|
||||
size_t readCallCost;
|
||||
// the energy cost of reading an EEPROM
|
||||
double readEnergyCost;
|
||||
// the call cost of reading an EEPROM's associated data
|
||||
size_t readDataCallCost;
|
||||
// the energy cost of reading an EEPROM's associated data
|
||||
double readDataEnergyCost;
|
||||
// the call cost of writing to an EEPROM
|
||||
size_t writeCallCost;
|
||||
// the energy cost of writing to an EEPROM
|
||||
double writeEnergyCost;
|
||||
// the call cost of writing to an EEPROM's associated data
|
||||
size_t writeDataCallCost;
|
||||
// the energy cost of writing to an EEPROM's associated data
|
||||
double writeDataEnergyCost;
|
||||
nn_Exit (*handler)(nn_EEPROMRequest *request);
|
||||
} nn_EEPROM;
|
||||
|
||||
extern nn_EEPROM nn_defaultEEPROM;
|
||||
|
||||
typedef struct nn_VEEPROM {
|
||||
const char *code;
|
||||
size_t codelen;
|
||||
@ -699,9 +722,11 @@ typedef struct nn_VEEPROM {
|
||||
bool isReadonly;
|
||||
} nn_VEEPROM;
|
||||
|
||||
typedef nn_Exit nn_EEPROMHandler(nn_EEPROMRequest *request);
|
||||
|
||||
// the userdata passed to the component is the userdata
|
||||
// in the handler
|
||||
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, void *userdata);
|
||||
nn_ComponentType *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata);
|
||||
nn_ComponentType *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem);
|
||||
|
||||
// Note on paths:
|
||||
@ -751,13 +776,14 @@ typedef enum nn_FilesystemAction {
|
||||
// The entry should be stored in strarg2, and strarg2len is the capacity of the buffer.
|
||||
// If the buffer is too short, truncate the result.
|
||||
// Set strarg2len to the length of the entry.
|
||||
// If there are no more entries, set strarg2 to NULL.
|
||||
// Do note that directories should have / appended at the end of their entries.
|
||||
// Directory file descriptors are not exposed to Lua,
|
||||
// Directory file descriptors are not exposed to the architecture,
|
||||
// thus they can only come from NN_FS_OPENDIR.
|
||||
// This means you may not need to validate these file descriptors.
|
||||
NN_FS_READDIR,
|
||||
// close a directory file descriptor, stored in fd.
|
||||
// Directory file descriptors are not exposed to Lua,
|
||||
// Directory file descriptors are not exposed to the architecture,
|
||||
// thus they can only come from NN_FS_OPENDIR.
|
||||
// This means you may not need to validate these file descriptors.
|
||||
NN_FS_CLOSEDIR,
|
||||
@ -767,11 +793,12 @@ typedef enum nn_FilesystemAction {
|
||||
// as needed.
|
||||
NN_FS_MKDIR,
|
||||
// Return the lastmodified timestamp.
|
||||
// This number is stored in milliseconds, but aligned to seconds.
|
||||
// DO NOT RETURN A NUMBER NOT DIVISIBLE BY 1000, OpenOS WILL BREAK
|
||||
// DUE TO BAD CODE.
|
||||
// This number is stored in seconds.
|
||||
// The timestamp should be stored in size, it may not make
|
||||
// sense but it is a field and it is there.
|
||||
// Do note that the lastModified() method returns it in milliseconds,
|
||||
// however it must be a multiple of 1000 due to OpenOS depending
|
||||
// on that behavior.
|
||||
NN_FS_LASTMODIFIED,
|
||||
// Checks if a path, stored in strarg1, is a directory.
|
||||
// If it is, size should be set to 1.
|
||||
@ -781,8 +808,6 @@ typedef enum nn_FilesystemAction {
|
||||
// If it is, size should be set to 1.
|
||||
// If it is not, size should be set to 0.
|
||||
NN_FS_ISREADONLY,
|
||||
// Makes a file-system read-only.
|
||||
NN_FS_MAKEREADONLY,
|
||||
// Checks if a path, stored in strarg1, exists on the filesystem.
|
||||
// If it is, size should be set to 1.
|
||||
// If it is not, size should be set to 0.
|
||||
@ -824,6 +849,7 @@ typedef struct nn_FilesystemRequest {
|
||||
void *userdata;
|
||||
void *instance;
|
||||
nn_Computer *computer;
|
||||
struct nn_Filesystem *fsConf;
|
||||
nn_FilesystemAction action;
|
||||
int fd;
|
||||
nn_FilesystemWhence whence;
|
||||
@ -835,6 +861,33 @@ typedef struct nn_FilesystemRequest {
|
||||
size_t size;
|
||||
} nn_FilesystemRequest;
|
||||
|
||||
typedef struct nn_Filesystem {
|
||||
// the maximum capacity of the filesystem
|
||||
size_t spaceTotal;
|
||||
// how many read calls can be done per tick
|
||||
// list, exists, isDirectory, seek also count as reads.
|
||||
double readsPerTick;
|
||||
// how many write calls can be done per tick
|
||||
// makeDirectory, open, remove and rename also count as writes.
|
||||
double writesPerTick;
|
||||
// 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.
|
||||
double dataEnergyCost;
|
||||
} nn_Filesystem;
|
||||
|
||||
// 4 Tiers.
|
||||
// 0 - Tier 1 equivalent
|
||||
// 1 - Tier 2 equivalent
|
||||
// 2 - Tier 3 equivalent
|
||||
// 3 - Tier 4, a better version of Tier 3.
|
||||
extern nn_Filesystem nn_defaultFilesystems[4];
|
||||
// a basic floppy
|
||||
extern nn_Filesystem nn_defaultFloppy;
|
||||
|
||||
typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request);
|
||||
|
||||
nn_ComponentType *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata);
|
||||
|
||||
typedef enum nn_ScreenAction {
|
||||
// instance dropped
|
||||
NN_SCR_DROP,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user