progress on modem and data card

This commit is contained in:
2026-05-05 19:08:31 +03:00
parent d5a7869d12
commit edca903416
3 changed files with 427 additions and 8 deletions

View File

@@ -48,6 +48,100 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
return NN_OK;
}
static nn_Exit ne_dataBullshit(nn_DataCardRequest *req) {
nn_Computer *C = req->computer;
nn_DataCardAction action = req->action;
nn_Exit e;
if(action == NN_DATA_DROP) {
return NN_OK;
}
if(action == NN_DATA_ENCODE64) {
int outSize = 0;
char *out = EncodeDataBase64((const unsigned char *)req->data, req->datalen, &outSize);
if(out == NULL) return NN_ENOMEM;
// -1 because raylib includes the NUL terminator??
e = nn_pushlstring(C, out, outSize-1);
MemFree(out);
return e;
}
if(action == NN_DATA_DECODE64) {
int outSize = 0;
char *out = (char *)DecodeDataBase64(req->data, &outSize);
if(out == NULL) return NN_ENOMEM;
e = nn_pushlstring(C, out, outSize);
MemFree(out);
return e;
}
if(action == NN_DATA_DEFLATE) {
int outSize = 0;
char *out = (char *)CompressData((const unsigned char *)req->data, req->datalen, &outSize);
if(out == NULL) return NN_ENOMEM;
e = nn_pushlstring(C, out, outSize);
MemFree(out);
return e;
}
if(action == NN_DATA_INFLATE) {
int outSize = 0;
char *out = (char *)DecompressData((unsigned char *)req->data, req->datalen, &outSize);
if(out == NULL) return NN_ENOMEM;
e = nn_pushlstring(C, out, outSize);
MemFree(out);
return e;
}
if(action == NN_DATA_CRC32) {
unsigned int check = nn_computeCRC32(req->crc32.data, req->crc32.datalen);
req->crc32.checksum[0] = (check >> 0) & 0xFF;
req->crc32.checksum[1] = (check >> 8) & 0xFF;
req->crc32.checksum[2] = (check >> 16) & 0xFF;
req->crc32.checksum[3] = (check >> 24) & 0xFF;
return NN_OK;
}
if(action == NN_DATA_MD5) {
unsigned int *out = ComputeMD5((unsigned char *)req->md5.data, req->md5.datalen);
if(out == NULL) return NN_ENOMEM;
memcpy(req->md5.checksum, out, 16);
return NN_OK;
}
if(action == NN_DATA_SHA256) {
// does not match OC, dunno why
unsigned int *out = ComputeSHA256((unsigned char *)req->sha256.data, req->sha256.datalen);
if(out == NULL) return NN_ENOMEM;
memcpy(req->sha256.checksum, out, 32);
return NN_OK;
}
if(action == NN_DATA_RANDOM) {
for(size_t i = 0; i < req->randbuf.buflen; i++) {
req->randbuf.buf[i] = rand();
}
return NN_OK;
}
if(action == NN_DATA_ENCRYPT) {
return nn_pushlstring(C, req->data, req->datalen);
}
if(action == NN_DATA_DECRYPT) {
return nn_pushlstring(C, req->data, req->datalen);
}
if(C) nn_setError(C, "ne: data method not implemented");
return NN_EBADCALL;
}
static nn_Exit ne_modemBullshit(nn_ModemRequest *req) {
nn_Computer *C = req->computer;
if(req->action == NN_MODEM_DROP) {
return NN_OK;
}
if(req->action == NN_MODEM_SEND) {
printf("Transmission from %s to %s (port %zu) of %zu bytes (%zu values)\n", req->localAddress, req->send.address == NULL ? "*" : req->send.address, req->send.port, req->send.contents->buflen, req->send.contents->valueCount);
return nn_pushModemMessage(C, req->localAddress, nn_getComputerAddress(C), req->send.port, 0, req->send.contents);
}
if(C) nn_setError(C, "ne: modem method not implemented");
return NN_EBADCALL;
}
static unsigned char ne_processColorPart(unsigned char channel, double brightness) {
double n = (double)channel / 255;
n *= brightness;
@@ -392,6 +486,9 @@ int main(int argc, char **argv) {
nn_setComponentMethods(ocelotCard, sandboxMethods);
nn_setComponentHandler(ocelotCard, sandbox_handler);
nn_Component *dataCard = nn_createDataCard(u, NULL, &nn_defaultDataCards[2], NULL, ne_dataBullshit);
nn_Component *modem = nn_createModem(u, NULL, &nn_defaultWiredModem, NULL, ne_modemBullshit);
char *eepromCode = (char *)minBIOS;
size_t eepromSize = strlen(minBIOS);
const char *eepromPath = getenv("NN_EEPROM");
@@ -492,8 +589,8 @@ int main(int argc, char **argv) {
double nextSecond = 0;
double wattage = 0;
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[3]);
nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[3]);
nn_Component *screen = ncl_createScreen(u, NULL, &nn_defaultScreens[2]);
nn_Component *gpuCard = ncl_createGPU(u, NULL, &nn_defaultGPUs[2]);
nn_Component *keyboard = nn_createComponent(
u, "mainKB", "keyboard");
@@ -536,6 +633,8 @@ int main(int argc, char **argv) {
nn_mountComponent(c, testingfs, 3, false);
nn_mountComponent(c, testDrive, 4, false);
nn_mountComponent(c, testFlash, 5, false);
nn_mountComponent(c, dataCard, 6, false);
nn_mountComponent(c, modem, 7, false);
int ltx = 0, lty = 0;
double scrollBuf = 0;
SetTargetFPS(60);
@@ -759,6 +858,8 @@ cleanup:;
nn_dropComponent(screen);
nn_dropComponent(gpuCard);
nn_dropComponent(keyboard);
nn_dropComponent(dataCard);
nn_dropComponent(modem);
// rip the universe
nn_destroyUniverse(u);
ncl_destroyGlyphCache(gc);

View File

@@ -1752,7 +1752,11 @@ nn_Exit nn_tick(nn_Computer *computer) {
err = nn_startComputer(computer);
if(err) return err;
} else if(computer->state != NN_RUNNING) {
if(computer->state != NN_CRASHED) nn_setErrorFromExit(computer, NN_EBADSTATE);
if(computer->state == NN_BLACKOUT) {
nn_setError(computer, "out of energy");
} else if(computer->state != NN_CRASHED) {
nn_setErrorFromExit(computer, NN_EBADSTATE);
}
return NN_EBADSTATE;
}
computer->state = NN_RUNNING;
@@ -2187,6 +2191,11 @@ nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const
}
computer->stackSize = req.returnCount;
if(nn_getEnergy(computer) <= 0) {
nn_setError(computer, "out of energy");
return NN_EBADCALL;
}
return NN_OK;
}
@@ -6055,6 +6064,17 @@ nn_DataCard nn_defaultDataCards[3] = {
NN_INIT(nn_DataCard) {
.limit = NN_MiB,
.maxRandom = NN_KiB,
.base64PerTick = 32,
.deflatingPerTick = 4,
.crc32PerTick = 32,
.md5PerTick = 8,
.sha256PerTick = 4,
.encryptPerTick = 4,
.genPerTick = 1,
.deserializePerTick = 8,
.ecdhPerTick = 1,
.ecdsaPerTick = 1,
.randomPerTick = 4,
.canHash = true,
.canEncrypt = false,
.canECDH = false,
@@ -6070,6 +6090,17 @@ nn_DataCard nn_defaultDataCards[3] = {
NN_INIT(nn_DataCard) {
.limit = NN_MiB,
.maxRandom = NN_KiB,
.base64PerTick = 32,
.deflatingPerTick = 4,
.crc32PerTick = 32,
.md5PerTick = 8,
.sha256PerTick = 4,
.encryptPerTick = 4,
.genPerTick = 1,
.deserializePerTick = 8,
.ecdhPerTick = 1,
.ecdsaPerTick = 1,
.randomPerTick = 4,
.canHash = true,
.canEncrypt = true,
.canECDH = false,
@@ -6085,6 +6116,17 @@ nn_DataCard nn_defaultDataCards[3] = {
NN_INIT(nn_DataCard) {
.limit = NN_MiB,
.maxRandom = NN_KiB,
.base64PerTick = 32,
.deflatingPerTick = 4,
.crc32PerTick = 32,
.md5PerTick = 8,
.sha256PerTick = 4,
.encryptPerTick = 4,
.genPerTick = 1,
.deserializePerTick = 8,
.ecdhPerTick = 1,
.ecdsaPerTick = 1,
.randomPerTick = 4,
.canHash = true,
.canEncrypt = true,
.canECDH = true,
@@ -6111,10 +6153,10 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
if(method == NN_DATANUM_SHA256 || method == NN_DATANUM_MD5) {
req->methodEnabled = dataCard.canHash;
}
if(method == NN_DATANUM_DEFLATE || method == NN_DATANUM_INFLATE || method == NN_DATANUM_RANDOM || method == NN_DATANUM_MAXRANDOM) {
if(method == NN_DATANUM_DEFLATE || method == NN_DATANUM_INFLATE) {
req->methodEnabled = dataCard.canCompress;
}
if(method == NN_DATANUM_ENCRYPT || method == NN_DATANUM_DECRYPT) {
if(method == NN_DATANUM_ENCRYPT || method == NN_DATANUM_DECRYPT || method == NN_DATANUM_RANDOM || method == NN_DATANUM_MAXRANDOM) {
req->methodEnabled = dataCard.canEncrypt;
}
if(method == NN_DATANUM_GENKEYPAIR || method == NN_DATANUM_ECDH || method == NN_DATANUM_ECDSA || method == NN_DATANUM_DESERIALIZEKEY) {
@@ -6147,6 +6189,153 @@ static nn_Exit nn_dataHandler(nn_ComponentRequest *req) {
}
// TODO: the cool methods
if(method == NN_DATANUM_ENCODE64) {
nn_costComponent(C, req->compAddress, dataCard.base64PerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_ENCODE64;
dreq.data = nn_tolstring(C, 0, &dreq.datalen);
if(dreq.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return NN_OK;
}
if(method == NN_DATANUM_DECODE64) {
nn_costComponent(C, req->compAddress, dataCard.base64PerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_DECODE64;
dreq.data = nn_tolstring(C, 0, &dreq.datalen);
if(dreq.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return NN_OK;
}
if(method == NN_DATANUM_DEFLATE) {
nn_costComponent(C, req->compAddress, dataCard.deflatingPerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_DEFLATE;
dreq.data = nn_tolstring(C, 0, &dreq.datalen);
if(dreq.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return NN_OK;
}
if(method == NN_DATANUM_INFLATE) {
nn_costComponent(C, req->compAddress, dataCard.deflatingPerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_INFLATE;
dreq.data = nn_tolstring(C, 0, &dreq.datalen);
if(dreq.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return NN_OK;
}
if(method == NN_DATANUM_CRC32) {
nn_costComponent(C, req->compAddress, dataCard.crc32PerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_CRC32;
dreq.crc32.data = nn_tolstring(C, 0, &dreq.crc32.datalen);
if(dreq.crc32.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(C, dreq.crc32.checksum, 4);
}
if(method == NN_DATANUM_MD5) {
nn_costComponent(C, req->compAddress, dataCard.md5PerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_MD5;
dreq.md5.data = nn_tolstring(C, 0, &dreq.md5.datalen);
if(dreq.md5.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(C, dreq.md5.checksum, 16);
}
if(method == NN_DATANUM_SHA256) {
nn_costComponent(C, req->compAddress, dataCard.sha256PerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_SHA256;
dreq.sha256.data = nn_tolstring(C, 0, &dreq.sha256.datalen);
if(dreq.sha256.datalen > dataCard.limit) return NN_ELIMIT;
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return nn_pushlstring(C, dreq.sha256.checksum, 32);
}
if(method == NN_DATANUM_RANDOM) {
nn_costComponent(C, req->compAddress, dataCard.randomPerTick);
if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL;
intptr_t n = nn_tointeger(C, 0);
if(n <= 0) {
n = 1;
}
if(n > dataCard.maxRandom) return NN_ELIMIT;
char *buf = nn_alloc(ctx, n);
dreq.action = NN_DATA_RANDOM;
dreq.randbuf.buf = buf;
dreq.randbuf.buflen = n;
e = state->handler(&dreq);
if(e) {
nn_free(ctx, buf, n);
return e;
}
req->returnCount = 1;
e = nn_pushlstring(C, buf, n);
nn_free(ctx, buf, n);
return e;
}
if(method == NN_DATANUM_ENCRYPT) {
nn_costComponent(C, req->compAddress, dataCard.encryptPerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 1, "bad argument #2 (string expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 2, "bad argument #3 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_ENCRYPT;
dreq.encrypt.data = nn_tolstring(C, 0, &dreq.encrypt.datalen);
if(dreq.encrypt.datalen > dataCard.limit) return NN_ELIMIT;
size_t len;
dreq.encrypt.key = nn_tolstring(C, 1, &len);
if(len != 16) {
nn_setError(C, "invalid key");
return NN_EBADCALL;
}
dreq.encrypt.iv = nn_tolstring(C, 2, &len);
if(len != 16) {
nn_setError(C, "invalid IV");
return NN_EBADCALL;
}
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return NN_OK;
}
if(method == NN_DATANUM_DECRYPT) {
nn_costComponent(C, req->compAddress, dataCard.encryptPerTick);
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 1, "bad argument #2 (string expected)")) return NN_EBADCALL;
if(nn_checkstring(C, 2, "bad argument #3 (string expected)")) return NN_EBADCALL;
dreq.action = NN_DATA_ENCRYPT;
dreq.encrypt.data = nn_tolstring(C, 0, &dreq.encrypt.datalen);
if(dreq.encrypt.datalen > dataCard.limit) return NN_ELIMIT;
size_t len;
dreq.encrypt.key = nn_tolstring(C, 1, &len);
if(len != 16) {
nn_setError(C, "invalid key");
return NN_EBADCALL;
}
dreq.encrypt.iv = nn_tolstring(C, 2, &len);
if(len != 16) {
nn_setError(C, "invalid IV");
return NN_EBADCALL;
}
e = state->handler(&dreq);
if(e) return e;
req->returnCount = 1;
return NN_OK;
}
if(C) nn_setError(C, "data: not implemented yet");
return NN_EBADCALL;
@@ -6165,7 +6354,7 @@ nn_Component *nn_createDataCard(nn_Universe *universe, const char *address, cons
[NN_DATANUM_MD5] = {"md5", "function(data: string): string - Computes the MD5 hash of the data", NN_DIRECT},
[NN_DATANUM_SHA256] = {"sha256", "function(data: string): string - Computes the SHA256 hash of the data", NN_DIRECT},
[NN_DATANUM_DEFLATE] = {"deflate", "function(data: string): string - Compresses the data", NN_DIRECT},
[NN_DATANUM_INFLATE] = {"deflate", "function(data: string): string - Decompresses the compressed data", NN_DIRECT},
[NN_DATANUM_INFLATE] = {"inflate", "function(data: string): string - Decompresses the compressed data", NN_DIRECT},
[NN_DATANUM_ENCRYPT] = {"encrypt", "function(data: string, key: string, iv: string): string - Encrypts the data", NN_DIRECT},
[NN_DATANUM_DECRYPT] = {"decrypt", "function(data: string, key: string, iv: string): string - Decrypts the data", NN_DIRECT},
[NN_DATANUM_RANDOM] = {"random", "function(size: integer): string - Generates an amount of secure random bytes", NN_DIRECT},
@@ -6264,15 +6453,19 @@ static nn_Exit nn_modemHandler(nn_ComponentRequest *req) {
}
if(method == NN_MODEMNUM_ISWIRED) {
req->returnCount = 1;
return nn_pushbool(C, isWired);
}
if(method == NN_MODEMNUM_ISWIRELESS) {
req->returnCount = 1;
return nn_pushbool(C, isWireless);
}
if(method == NN_MODEMNUM_MAXPACKETSIZE) {
req->returnCount = 1;
return nn_pushinteger(C, state->modem.maxPacketSize);
}
if(method == NN_MODEMNUM_MAXVALUES) {
req->returnCount = 1;
return nn_pushinteger(C, state->modem.maxValues);
}
if(method == NN_MODEMNUM_GETSTRENGTH) {
@@ -6283,9 +6476,73 @@ static nn_Exit nn_modemHandler(nn_ComponentRequest *req) {
return nn_pushinteger(C, mreq.strength);
}
if(method == NN_MODEMNUM_MAXSTRENGTH) {
req->returnCount = 1;
return nn_pushinteger(C, state->modem.maxRange);
}
if(method == NN_MODEMNUM_BROADCAST) {
if(nn_checkinteger(C, 0, "bad argument #1 (integer expected)")) return NN_EBADCALL;
intptr_t port = nn_tointeger(C, 0);
if(port < 1 || port > NN_MAX_PORT) {
nn_setError(C, "invalid port");
return NN_EBADCALL;
}
size_t valcount = nn_getstacksize(C) - 1;
if(valcount > state->modem.maxValues) return NN_EBADCALL;
int cost = nn_countValueCost(C, valcount);
if(cost < 0) {
nn_setError(C, "invalid contents");
return NN_EBADCALL;
}
if(cost > state->modem.maxPacketSize) return NN_ELIMIT;
nn_EncodedNetworkContents data;
e = nn_encodeNetworkContents(C, &data, valcount);
if(e) return e;
mreq.action = NN_MODEM_SEND;
mreq.send.address = NULL;
mreq.send.port = port;
mreq.send.contents = &data;
e = state->handler(&mreq);
nn_dropNetworkContents(&data);
if(!e) {
req->returnCount = 1;
e = nn_pushbool(C, true);
}
return e;
}
if(method == NN_MODEMNUM_SEND) {
if(nn_checkstring(C, 0, "bad argument #1 (string expected)")) return NN_EBADCALL;
if(nn_checkinteger(C, 1, "bad argument #2 (integer expected)")) return NN_EBADCALL;
const char *addr = nn_tostring(C, 0);
intptr_t port = nn_tointeger(C, 1);
if(port < 1 || port > NN_MAX_PORT) {
nn_setError(C, "invalid port");
return NN_EBADCALL;
}
size_t valcount = nn_getstacksize(C) - 1;
if(valcount > state->modem.maxValues) return NN_EBADCALL;
int cost = nn_countValueCost(C, valcount);
if(cost < 0) {
nn_setError(C, "invalid contents");
return NN_EBADCALL;
}
if(cost > state->modem.maxPacketSize) return NN_ELIMIT;
nn_EncodedNetworkContents data;
e = nn_encodeNetworkContents(C, &data, valcount);
if(e) return e;
mreq.action = NN_MODEM_SEND;
mreq.send.address = addr;
mreq.send.port = port;
mreq.send.contents = &data;
e = state->handler(&mreq);
nn_dropNetworkContents(&data);
if(!e) {
req->returnCount = 1;
e = nn_pushbool(C, true);
}
return e;
}
if(C) nn_setError(C, "modem: not implemented yet");
return NN_EBADCALL;
}

View File

@@ -1657,10 +1657,15 @@ nn_Component *nn_createScreen(
nn_ScreenHandler *handler
);
// Computes a CRC32 checksum
unsigned int nn_computeCRC32(const char *data, size_t datalen);
typedef struct nn_DataCard {
// The buffer size of the data card, limit in both input and output.
// This buffer is allocated on the heap before the call, thus setting it to be very large will lead to huge *spikes* in memory usage.
// In OC, this value is 1MiB regardless of tier.
// As there is no out buffer and you are expected to push strings,
// the buffer is not pre-allocated. This is intentional, as memory
// would be wasted and it could be a potential attack vector otherwise.
size_t limit;
// The maximum amount of secure random bytes that can be generated.
@@ -1669,6 +1674,29 @@ typedef struct nn_DataCard {
// as the amount of bytes needed is known perfectly.
size_t maxRandom;
// for encoding/decoding. OC defaulted to 32
size_t base64PerTick;
// for deflate/inflate. OC defaulted to 4
size_t deflatingPerTick;
// OC defaulted to 32
size_t crc32PerTick;
// OC defaulted to 8
size_t md5PerTick;
// OC defaulted to 4
size_t sha256PerTick;
// for encrypt/decrypt. OC defaulted to 4
size_t encryptPerTick;
// for generateKeyPair. OC defaulted to 1
size_t genPerTick;
// for deserializeKey. OC defaulted to 8
size_t deserializePerTick;
// OC defaulted to 1
size_t ecdhPerTick;
// OC defaulted to 1
size_t ecdsaPerTick;
// OC defaults to 4
size_t randomPerTick;
// Capabilities
bool canHash;
bool canEncrypt;
@@ -1718,7 +1746,7 @@ typedef enum nn_DataCardAction {
NN_DATA_DEFLATE,
// Inflate. Should support the ZLIB format, as thats what OC uses, and GZIP support is optional.
NN_DATA_INFALTE,
NN_DATA_INFLATE,
// Encrypt data with AES-128. The full algorithm is AES/CBC/PKCS5, as is use PKCS5 for padding, CBC for block sequences, and AES-128 for encrypting blocks.
// It does also receive a 128-bit AES Initialization Vector, for better security.
@@ -1752,6 +1780,39 @@ typedef struct nn_DataCardRequest {
const nn_DataCard *dataCard;
nn_DataCardAction action;
// TODO: the fields
union {
struct {
const char *data;
size_t datalen;
char checksum[4];
} crc32;
struct {
const char *data;
size_t datalen;
char checksum[16];
} md5;
struct {
const char *data;
size_t datalen;
char checksum[32];
} sha256;
// for encrypt/decrypt
struct {
const char *data;
size_t datalen;
const char *key;
const char *iv;
} encrypt;
struct {
char *buf;
size_t buflen;
} randbuf;
// for deflate, inflate, encode64 and decode64
struct {
const char *data;
size_t datalen;
};
};
} nn_DataCardRequest;
typedef nn_Exit (nn_DataCardHandler)(nn_DataCardRequest *req);