From 59f7dd1492eac6bbf7a678aefa8dc52dc31fbcf4 Mon Sep 17 00:00:00 2001 From: IonutParau Date: Sun, 15 Mar 2026 18:22:01 +0100 Subject: [PATCH] erase the old version --- TODO.md | 78 +- rewrite/TODO.md | 65 - rewrite/neonucleus.h | 1681 ----------------- src/component.c | 150 -- src/component.h | 35 - src/components/diskDrive.c | 112 -- src/components/drive.c | 257 --- src/components/eeprom.c | 390 ---- src/components/externalComputer.c | 194 -- src/components/filesystem.c | 687 ------- src/components/gpu.c | 960 ---------- src/components/hologram.c | 13 - src/components/hologram.h | 38 - src/components/keyboard.c | 11 - src/components/loopbackModem.c | 144 -- src/components/loopbackTunnel.c | 59 - src/components/modem.c | 351 ---- src/components/screen.c | 512 ----- src/components/screen.h | 32 - src/components/tunnel.c | 158 -- src/components/volatileDrive.c | 74 - src/components/volatileEeprom.c | 98 - src/components/volatileFilesystem.c | 639 ------- src/computer.c | 674 ------- src/computer.h | 60 - src/data.zig | 20 - src/deviceInfo.c | 130 -- src/emulator.c | 1016 ---------- src/emulator/.gitkeep | 0 src/lock.c | 86 - {rewrite => src}/luaarch.c | 0 {rewrite => src}/machine.lua | 0 {rewrite => src}/main.c | 3 +- {rewrite => src}/minBIOS.lua | 0 {rewrite => src}/neonucleus.c | 41 +- src/neonucleus.h | 2680 ++++++++++++++++----------- src/resource.c | 29 - src/resource.h | 21 - src/sandbox.lua | 455 ----- src/testLuaArch.c | 789 -------- src/testLuaArch.h | 7 - src/tinycthread.c | 934 ---------- src/tinycthread.h | 479 ----- src/unicode.c | 435 ----- src/universe.c | 75 - src/universe.h | 17 - src/utils.c | 462 ----- src/value.c | 257 --- 48 files changed, 1700 insertions(+), 13708 deletions(-) delete mode 100644 rewrite/TODO.md delete mode 100644 rewrite/neonucleus.h delete mode 100644 src/component.c delete mode 100644 src/component.h delete mode 100644 src/components/diskDrive.c delete mode 100644 src/components/drive.c delete mode 100644 src/components/eeprom.c delete mode 100644 src/components/externalComputer.c delete mode 100644 src/components/filesystem.c delete mode 100644 src/components/gpu.c delete mode 100644 src/components/hologram.c delete mode 100644 src/components/hologram.h delete mode 100644 src/components/keyboard.c delete mode 100644 src/components/loopbackModem.c delete mode 100644 src/components/loopbackTunnel.c delete mode 100644 src/components/modem.c delete mode 100644 src/components/screen.c delete mode 100644 src/components/screen.h delete mode 100644 src/components/tunnel.c delete mode 100644 src/components/volatileDrive.c delete mode 100644 src/components/volatileEeprom.c delete mode 100644 src/components/volatileFilesystem.c delete mode 100644 src/computer.c delete mode 100644 src/computer.h delete mode 100644 src/data.zig delete mode 100644 src/deviceInfo.c delete mode 100644 src/emulator.c delete mode 100644 src/emulator/.gitkeep delete mode 100644 src/lock.c rename {rewrite => src}/luaarch.c (100%) rename {rewrite => src}/machine.lua (100%) rename {rewrite => src}/main.c (99%) rename {rewrite => src}/minBIOS.lua (100%) rename {rewrite => src}/neonucleus.c (99%) delete mode 100644 src/resource.c delete mode 100644 src/resource.h delete mode 100644 src/sandbox.lua delete mode 100644 src/testLuaArch.c delete mode 100644 src/testLuaArch.h delete mode 100644 src/tinycthread.c delete mode 100644 src/tinycthread.h delete mode 100644 src/unicode.c delete mode 100644 src/universe.c delete mode 100644 src/universe.h delete mode 100644 src/utils.c delete mode 100644 src/value.c diff --git a/TODO.md b/TODO.md index 7d81c7f..5059a99 100644 --- a/TODO.md +++ b/TODO.md @@ -1,51 +1,65 @@ -# Code quality stuff +# For MVP functionality -- Literally rewrite the whole thing. -- Use the same namespacing style everywhere, that being -`nn__` for functions related to "classes", -`nn_` for static functions, -`nn__t` for types. -- Get rid of `nn_address` -- Make a lot more stuff const -- Rework to API to be much more future-proof to reduce potential breaking changes. +- `drive` component +- volatile filesystem +- volatile drive +- device info -# Parity with Vanilla OC (only the stuff that makes sense for an emulator) +# To re-evaluate -- `hologram` component +- More stack manipulation functions to allow libraries to have better APIs. +- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them +to be made more portable. +- Exposing more context wrappers than just the memory allocation functions. +- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code. + +# Vanilla components needed + +Not everything OC has (as a few of them are really MC-centered) but most of it. + +- `data` component (note: maybe add hamming code support?) +- `modem` component +- `tunnel` component +- `relay` component (note: OCDoc still refers to it as `access_point`) - `computer` component -- `data` component (with error correction codes and maybe synthesizing audio) +- `geolyzer` component +- `net_splitter` component - `redstone` component -- `internet` component +- `motion_sensor` component +- `printer3d` component (note: maybe add signal for when printer completes?) +- `sign` component +- `microcontroller` component +- `hologram` component +- `disk_drive` component +- `note_block` component -# Bugfixes - -- Do a huge audit at some point -- `nn_unicode_charWidth` appears to be bugged, look into that. -- validate against moving `a/b` into `a` or moving `a` into `a/b` in the filesystem component. - -# The extra components +# Custom components needed - `oled` component (OLED screen, a store of draw commands and resolution from NN's perspective) - `ipu` component, an Image Processing Unit. Can bind with `oled`s, and issues said draw commands -- `vt`, a virtual terminal with ANSI-like escapes. (and a function to get its resolution) +- `vt`, a combination of a GPU, a screen and a keyboard with limited functionality. Offers direct operations on the video memory - (maybe) `qpu` component, a Quantum Processing Unit for quantum computing - `radio_controller` and `radio_tower` components, for radio telecommunications - (maybe) `clock` component for arbitrary precision time-keeping - `led` component for LED matrixes and/or LED lights - `speaker` component, allows playing audio by asking for binary samples and pushing a signal when it needs more - `microphone` component, allows reading audio from nearby sources -- `tape_drive` component, compatible with Computronics, except maybe with proper seek times and support for multiple tapes +- `tape_drive` component, compatible with Computronics - `cd_reader` and `cd_writer` components, to work with CDs - `serial` component, for serial communications with other devices (USB?) +- `iron_noteblock` component +- `colorful_lamp` component -# Internal changes +# To make it good -- use more arenas!!!!!!! -- make sure OOMs are recoverable -- rework some interfaces to use pre-allocated or stack-allocated memory more -- use dynamic arrays for signals (and maybe components), but still keep the maximums to prevent memory hogging -- setup an extensive testing system to find bugs easier -- optimize the codebase by using globals instead of universe userdata -- use compiler hints to let the optimizer make the code even faster -- (maybe) rework a bunch of internal structures to use tagged unions over vtables (such as components). This may make certain APIs unnecessary or slightly -different. This can improve performance at the cost of making the codebase more complex +- make more stuff const if it can be. Gotta help out the optimizer +- write a bunch of unit tests to ensure the public API works correctly +- ensure OOMs are recoverable +- do a hude audit for bugs at some point + +# To make it fast + +NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs. + +- make signals use a circular buffer instead of a simple array +- use a hashmap for components (and device info), this may require reworking how iterating over them is handled diff --git a/rewrite/TODO.md b/rewrite/TODO.md deleted file mode 100644 index da9fc97..0000000 --- a/rewrite/TODO.md +++ /dev/null @@ -1,65 +0,0 @@ -# For MVP functionality - -- `drive` component -- volatile filesystem -- volatile drive -- device info - -# To re-evaluate - -- More stack manipulation functions to allow libraries to have more APIs. -- Having a copy of the context stored directly in requests instead of having to use getComputerContext, as it simplifies the APIs and allows them -to be made more portable. -- Exposing more context wrappers than just the memory allocation functions. -- Exposing more internal functions that may be useful to the user to prevent pointless rewriting and duplicate machine code. - -# Vanilla components needed - -Not everything OC has (as a few of them are really MC-centered) but most of it. - -- `data` component (note: maybe add hamming code support?) -- `modem` component -- `tunnel` component -- `relay` component (note: OCDoc still refers to it as `access_point`) -- `computer` component -- `geolyzer` component -- `net_splitter` component -- `redstone` component -- `motion_sensor` component -- `printer3d` component (note: maybe add signal for when printer completes?) -- `sign` component -- `microcontroller` component -- `hologram` component -- `disk_drive` component -- `note_block` component - -# Custom components needed - -- `oled` component (OLED screen, a store of draw commands and resolution from NN's perspective) -- `ipu` component, an Image Processing Unit. Can bind with `oled`s, and issues said draw commands -- `vt`, a combination of a GPU, a screen and a keyboard with limited functionality. Offers direct operations on the video memory -- (maybe) `qpu` component, a Quantum Processing Unit for quantum computing -- `radio_controller` and `radio_tower` components, for radio telecommunications -- (maybe) `clock` component for arbitrary precision time-keeping -- `led` component for LED matrixes and/or LED lights -- `speaker` component, allows playing audio by asking for binary samples and pushing a signal when it needs more -- `microphone` component, allows reading audio from nearby sources -- `tape_drive` component, compatible with Computronics -- `cd_reader` and `cd_writer` components, to work with CDs -- `serial` component, for serial communications with other devices (USB?) -- `iron_noteblock` component -- `colorful_lamp` component - -# To make it good - -- make more stuff const if it can be. Gotta help out the optimizer -- write a bunch of unit tests to ensure the public API works correctly -- ensure OOMs are recoverable -- do a hude audit for bugs at some point - -# To make it fast - -NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs. - -- make signals use a circular buffer instead of a simple array -- use a hashmap for components (and device info), this may require reworking how iterating over them is handled diff --git a/rewrite/neonucleus.h b/rewrite/neonucleus.h deleted file mode 100644 index 716fe81..0000000 --- a/rewrite/neonucleus.h +++ /dev/null @@ -1,1681 +0,0 @@ -#ifndef NEONUCLEUS_H -#define NEONUCLEUS_H - -#ifdef __cplusplus -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 // for NULL, -#include // for intptr_t -#include // for true, false and bool - -// Internally we need stdatomic.h and, if NN_BAREMETAL is not defined, stdlib.h and time.h - -// The entire NeoNucleus API, in one header file - -// Internal limits or constants - -#define NN_KiB (1024) -#define NN_MiB (1024 * NN_KiB) -#define NN_GiB (1024 * NN_MiB) -#define NN_TiB (1024 * NN_TiB) - -// the alignment an allocation should have -#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, 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. -#define NN_MAX_READ 65536 -// the maximum size of a label -#define NN_MAX_LABEL 256 -// maximum size of a wakeup message -#define NN_MAX_WAKEUPMSG 2048 -// the maximum amount of file descriptors that can be open simultaneously -#define NN_MAX_OPENFILES 128 -// the maximum amount of userdata that can be sent simultaneously. -#define NN_MAX_USERDATA 64 -// maximum size of a signal, computed the same as modem packet costs. -#define NN_MAX_SIGNALSIZE 8192 -// maximum amount of signals. -#define NN_MAX_SIGNALS 128 -// the maximum value of a port. Ports start at 1. -#define NN_MAX_PORT 65535 -// the magic port number to close all ports -#define NN_CLOSEPORTS 0 -// maximum amount of architectures one machine can support. -#define NN_MAX_ARCHITECTURES 32 -// maximum size of the architecture name EEPROMs can store -#define NN_MAX_ARCHNAME 64 -// maximum size of an address. -// This only matters in places where an address is returned through a component, as it is the amount of space to allocate for the response. -// Past this there would be a truncation which would invalidate the address. -// However, 256 is unrealistically long, as UUIDv4 only needs 36. -// Please, do not go above this. -#define NN_MAX_ADDRESS 256 -// the port used by tunnel cards. This port is invalid for modems. -#define NN_TUNNEL_PORT 0 -// maximum amount of users a computer can have -#define NN_MAX_USERS 64 -// maximum length of a username -#define NN_MAX_USERNAME 128 - -// the maximum size of a UTF-8 character -#define NN_MAX_UNICODE_BUFFER 4 - -// the maximum size of a component error message. If the error is bigger than this, -// it is truncated. -#define NN_MAX_ERROR_SIZE 1024 - -// unicode (UTF-8) support library - -typedef unsigned int nn_codepoint; - -bool nn_unicode_validate(const char *s, size_t len); -// validates only the *first* codepoint in the NULL-terminated string. -// This returns the length in bytes of the codepoint, with 0 meaning -// invalid. -size_t nn_unicode_validateFirstChar(const char *s, size_t len); - -// returns the amount of unicode codepoints in the UTF-8 string. -// Undefined behavior for invalid UTF-8, make sure to validate it if needed. -size_t nn_unicode_len(const char *s, size_t len); -// returns the amount of unicode codepoints in the UTF-8 string. -// If s is invalid UTF-8, all invalid bytes are considered a 1-byte codepoint. -size_t nn_unicode_lenPermissive(const char *s, size_t len); - -// Writes the codepoints of s into codepoints. -// Undefined behavior for invalid UTF-8, make sure to validate it if needed. -// The codepoints buffer must be big enough to store the string, use nn_unicode_len() -// to get the required buffer length. -void nn_unicode_codepoints(const char *s, size_t len, nn_codepoint *codepoints); -// Writes the codepoints of s into codepoints. -// If s is invalid UTF-8, all invalid bytes are considered a 1-byte codepoint. -// The codepoints buffer must be big enough to store the string, use nn_unicode_lenPermissive() -// to get the required buffer length. -void nn_unicode_codepointsPermissive(const char *s, size_t len, nn_codepoint *codepoints); - -// Returns the first codepoint from a UTF-8 string. -// If s is invalid UTF-8, the behavior is undefined. -nn_codepoint nn_unicode_firstCodepoint(const char *s); -// Returns the size, in bytes, required by UTF-8 for a codepoint. -size_t nn_unicode_codepointSize(nn_codepoint codepoint); -// Writes the UTF-8 bytes for a given codepoint into buffer. -// It does NOT write a NULL terminator, but it does return the length. -size_t nn_unicode_codepointToChar(char buffer[NN_MAX_UNICODE_BUFFER], nn_codepoint codepoint); -// the width, on a screen, for a codepoint. -// This matters for emojies. -size_t nn_unicode_charWidth(nn_codepoint codepoint); -// The width, on a screen, for an entire string. -// The behavior is undefined for -size_t nn_unicode_wlen(const char *s, size_t len); -size_t nn_unicode_wlenPermissive(const char *s, size_t len); - -// Returns the amount of bytes needed to store the UTF-8 encoded text. -// The behavior on invalid codepoints is undefined. -size_t nn_unicode_countBytes(nn_codepoint *codepoints, size_t len); -// Writes the UTF-8 encoded text. -// DOES NOT WRITE A NULL TERMINATOR. -// s must be big enough to store the string, use nn_unicode_bytelen() -// to allocate the correct amount of space. -// The behavior on invalid codepoints is undefined. -void nn_unicode_writeBytes(char *s, nn_codepoint *codepoints, size_t len); - -// Returns the uppercase version of the codepoint -nn_codepoint nn_unicode_upper(nn_codepoint codepoint); -// Returns the lowercase version of the codepoint -nn_codepoint nn_unicode_lower(nn_codepoint codepoint); - -// The type of a the function used as the allocator. -// The expected behavior is as follows: -// alloc(state, NULL, 0, newSize) -> malloc(newSize) -// alloc(state, memory, oldSize, 0) -> free(memory) -// alloc(state, memory, oldSize, newSize) -> realloc(memory, newSize) -// -// NeoNucleus will ensure oldSize is what the application requested on the last allocation. -// This is useful for allocators which may not do extensive bookkeeping. -// In the case of Out Of Memory, you are expected to return NULL. -typedef void *nn_AllocProc(void *state, void *memory, size_t oldSize, size_t newSize); - -// Meant to return the time, in seconds, since some epoch. -typedef double nn_TimeProc(void *state); - -typedef size_t nn_RngProc(void *state); - -typedef enum nn_LockAction { - // create the mutex - NN_LOCK_CREATE, - // destroy the mutex - NN_LOCK_DESTROY, - // lock the mutex - NN_LOCK_LOCK, - // unlock the mutex - NN_LOCK_UNLOCK, -} nn_LockAction; - -typedef struct nn_LockRequest { - // mutate it for NN_LOCK_INIT - void *lock; - nn_LockAction action; -} nn_LockRequest; - -// Intended for a plain mutex. -// This is used for synchronization. OpenComputers achieves synchronization -// between the worker threads by sending them as requests to a central thread (indirect methods). -// In NeoNucleus, the function pointer is invoked on the calling thead. This technically makes all methods direct, -// however methods which are meant to be slow may become indirect, as indirect methods consume the entire call budget. -// Do note that locks are only used in "full" component implementations, such as the volatile storage devices. -// The interfaces do not do any automatic synchronization via locks, all synchronization is assumed -// to be handled in the implementer of the interface, because only you know how to best synchronize -// it with the outside world. -typedef void nn_LockProc(void *state, nn_LockRequest *req); - -// The *context* NeoNucleus is operating in. -// This determines: -// - How memory is allocated -// - How random numbers are generated and what the range is -// - What the current time is -// - How locks work -typedef struct nn_Context { - void *state; - nn_AllocProc *alloc; - nn_TimeProc *time; - // the maximum value the RNG can produce. - // The RNG is assumed to generate [0, rngMaximum]. INCLUSIVE. - // When a [0, 1) range is needed, the value is divided by (rngMaximum+1), - // so rngMaximum+1 MUST NOT OVERFLOW. - size_t rngMaximum; - nn_RngProc *rng; - nn_LockProc *lock; -} nn_Context; - -// if NN_BAREMETAL is defined when NeoNucleus is compiled, -// this function will fill in a bogus context that does nothing. -// Otherwise, it fills in a context which uses the C standard library. -// This function is only meant to be called once, to get the initial context. -// It does seed the RNG when using the C standard library, so do not -// call it in loops. -void nn_initContext(nn_Context *ctx); - -// Memory allocation!!! - -void *nn_alloc(nn_Context *ctx, 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); - -typedef enum nn_Exit { - // no error - NN_OK = 0, - // out of memory. - NN_ENOMEM, - // over the limit. For example, adding too many architectures to a machine. - NN_ELIMIT, - // internal stack underflow when managing the stack. - NN_EBELOWSTACK, - // internal stack overflow when carrying values. - NN_ENOSTACK, - // bad invocation, error message stored in computer state - NN_EBADCALL, - // bad state, the function was called at the wrong time - NN_EBADSTATE, -} nn_Exit; - -// This stores necessary data between computers -typedef struct nn_Universe nn_Universe; - -nn_Universe *nn_createUniverse(nn_Context *ctx); -void nn_destroyUniverse(nn_Universe *universe); - -// The actual computer -typedef struct nn_Computer nn_Computer; - -typedef enum nn_ComputerState { - // the machine is running just fine - NN_RUNNING = 0, - // machine is initializing. This is the state after createComputer but before the first nn_tick. - // It is a required state of various initialization functions. - NN_BOOTUP, - // the machine has powered off. - NN_POWEROFF, - // the machine demands being restarted. - NN_RESTART, - // the machine has crashed. RIP - NN_CRASHED, - // the machine ran out of energy. - NN_BLACKOUT, - // change architecture. - NN_CHARCH, -} nn_ComputerState; - -typedef enum nn_ArchitectureAction { - // create the local state - NN_ARCH_INIT, - // destroy the local state - NN_ARCH_DEINIT, - // run 1 tick - NN_ARCH_TICK, - // get the free memory - NN_ARCH_FREEMEM, -} nn_ArchitectureAction; - -typedef struct nn_ArchitectureRequest { - // the state pointer passed through - void *globalState; - // the computer which made the request - nn_Computer *computer; - // the local state bound to this computer. - // In NN_ARCH_INIT, this is NULL, and must be set to the new state, or an appropriate exit code returned. - void *localState; - // the action requested - nn_ArchitectureAction action; - union { - // in the case of NN_ARCH_FREEMEM, the free memory - size_t freeMemory; - }; -} nn_ArchitectureRequest; - -typedef nn_Exit nn_ArchitectureHandler(nn_ArchitectureRequest *req); - -typedef struct nn_Architecture { - const char *name; - void *state; - nn_ArchitectureHandler *handler; -} nn_Architecture; - -// Standard RAM sizes. -// Standard OC goes from tier 1 to tier 6, -// NN adds 2 more tiers. -extern size_t nn_ramSizes[8]; - -// The state of a *RUNNING* computer. -// Powered off computers shall not have a state, and as far as NeoNucleus is aware, -// not exist. -// The computer API *is not thread-safe*, so it is recommended that you use an external lock to manage it if you are running -// it in a multi-threaded environment. -// The userdata pointer is meant to store external data required by the environment. -// totalMemory is a hint to the architecture as to how much memory to allow the runner to use. It is in bytes. -// maxComponents and maxDevices determine how many components can be connected simultaneously to this computer, and how much device info can be -// registered on this computer. -nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices); -// Destroys the state, effectively shutting down the computer. -void nn_destroyComputer(nn_Computer *computer); -// get the userdata pointer -void *nn_getComputerUserdata(nn_Computer *computer); -const char *nn_getComputerAddress(nn_Computer *computer); -nn_Universe *nn_getComputerUniverse(nn_Computer *computer); -nn_Context *nn_getUniverseContext(nn_Universe *universe); -nn_Context *nn_getComputerContext(nn_Computer *computer); -// Sets the memory scale, which defaults to 1. -// For context, OC will set the memory scale to 1.8 on 64-bit systems by default. -// This scale affects how much real-world memory an amount of VM actually takes up. -// This means if the total memory is 4 MiB, and the scale is set to 2, the computer can take up to 8MiB of actual RAM. -// However, nn_getTotalMemory() will still return 4 MiB. -// The architecture is meant to ensure that the reported free memory is also scaled. As in, the real-world free memory -// is divided by this scale to ensure it is within the correct range. -// It is undefined behavior to change the memory scale *after* the first call to nn_tick(). -// Some architectures may ignore this, if they are very low-level and thus -// do not have any implicit changes of sizes between 32-bit and 64-bit. -void nn_setMemoryScale(nn_Computer *computer, double scale); -double nn_getMemoryScale(nn_Computer *computer); - -// Returns the memory usage limit of the computer. -size_t nn_getTotalMemory(nn_Computer *computer); -// Gets the total amount of free memory the computer has available. The total memory - this is the amount of memory used. -size_t nn_getFreeMemory(nn_Computer *computer); -// Gets the total amount of used memory the computer has allocated. -// This is just the total minus the free, and does not take into -// account the overhead of storing the computer instance. -size_t nn_getUsedMemory(nn_Computer *computer); -// gets the current uptime of a computer. When the computer is not running, this value can be anything and loses all meaning. -double nn_getUptime(nn_Computer *computer); - -// address is copied. -// It can be NULL if you wish to have no tmp address. -// It can fail due to out-of-memory errors. -nn_Exit nn_setTmpAddress(nn_Computer *computer, const char *address); -// can return NULL if none was set -const char *nn_getTmpAddress(nn_Computer *computer); - -// Registers a user to the computer. -nn_Exit nn_addUser(nn_Computer *computer, const char *user); -// Unregisters a user from the computer. -// If they were never there, nothing is removed and all is fine. -// It returns if the user was originally there. -bool nn_removeUser(nn_Computer *computer, const char *user); -// NULL for out-of-bound users -// Can be used to iterate all users. -const char *nn_getUser(nn_Computer *computer, size_t idx); -// Helper function. -// Always returns true if 0 users are registered. -// If users are registered, it will only return true if the specified -// user is registered. -// This can be used for checking signals. -bool nn_hasUser(nn_Computer *computer, const char *user); - -// Sets the computer's architecture. -// The architecture determines everything from how the computer runs, to how it turns off. -// Everything is limited by the architecture. -// The architecture is copied, it can be freed after this is called. -void nn_setArchitecture(nn_Computer *computer, const nn_Architecture *arch); -// Gets the current architecture. -nn_Architecture nn_getArchitecture(nn_Computer *computer); -// Sets the computer's desired architecture. -// The desired architecture indicates, when the computer state is CHARCH, what the new architecture should be. -// This is set even if it is not in the supported architecture list, *you must check if it is in that list first.* -// The architecture is copied, it can be freed after this is called. -void nn_setDesiredArchitecture(nn_Computer *computer, const nn_Architecture *arch); -// Gets the desired architecture. This is the architecture the computer should use after changing architectures. -nn_Architecture nn_getDesiredArchitecture(nn_Computer *computer); -// Adds a new supported architecture, which indicates to the code running on this computer that it is possible to switch to that architecture. -// The architecture is copied, it can be freed after this is called. -nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture *arch); -// Returns the array of supported architectures, as well as the length. -const nn_Architecture *nn_getSupportedArchitectures(nn_Computer *computer, size_t *len); -// Helper function for searching for an architecture using a computer which supports it and the architecture name. -// If the architecture is not found, it returns one with a NULL name. -nn_Architecture nn_findSupportedArchitecture(nn_Computer *computer, const char *name); - -// sets the energy capacity of the computer. -void nn_setTotalEnergy(nn_Computer *computer, double maxEnergy); -// gets the energy capacity of the computer -double nn_getTotalEnergy(nn_Computer *computer); -// gets the current amount of energy -double nn_getEnergy(nn_Computer *computer); -// Returns true if there is no more energy left, and a blackout has occured. -bool nn_removeEnergy(nn_Computer *computer, double energy); - -// the handler of energy costs. -// The default handler just returns the total energy. -// Computers do not keep track of their current energy, they just call this function. -// getEnergy() calls this with amountToRemove set to 0. It is recommended to handle this as a fastpath. -// This should return the new amount of energy after the removal. -// A negative amount can be returned, it will be clamped to 0. -// If an error occurs, it is recommended to just return 0 and trigger a blackout. -// TODO: evaluate if API should be reworked to handle errors in energy handler. -typedef double nn_EnergyHandler(void *energyState, nn_Computer *computer, double amountToRemove); - -void nn_setEnergyHandler(nn_Computer *computer, void *energyState, nn_EnergyHandler *handler); - -// copies the string into the local error buffer. The error is NULL terminated, but also capped by NN_MAX_ERROR_SIZE -void nn_setError(nn_Computer *computer, const char *s); -// set a default error message from an exit. -// Does nothing for EBADCALL. -void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit); -// copies the string into the local error buffer. The error is capped by NN_MAX_ERROR_SIZE-1. The -1 is there because the NULL terminator is still inserted at the end. -// Do note that nn_getError() still returns a NULL-terminated string, thus NULL terminators in this error will lead to a shortened error. -void nn_setLError(nn_Computer *computer, const char *s, size_t len); -// Gets a pointer to the local error buffer. This is only meaningful when NN_EBADCALL is returned. -const char *nn_getError(nn_Computer *computer); -// clears the computer's error buffer, making nn_getError return a simple empty string. -void nn_clearError(nn_Computer *computer); - -// sets the computer state to the desired state. This is meant for architectures to report things like reboots or shutdowns, DO NOT ABUSE THIS. -void nn_setComputerState(nn_Computer *computer, nn_ComputerState state); -// gets the current computer state -nn_ComputerState nn_getComputerState(nn_Computer *computer); - -// Checks if the uptime is below the idle timestamp. -bool nn_isComputerIdle(nn_Computer *computer); -// Shifts over the idle timestamp. -void nn_addIdleTime(nn_Computer *computer, double time); -// runs a tick of the computer. Make sure to check the state as well! -// This automatically resets the component budgets and call budget. -// It also sets the idle timestamp to the current uptime. -nn_Exit nn_tick(nn_Computer *computer); - -typedef struct nn_DeviceInfoEntry { - const char *name; - const char *value; -} nn_DeviceInfoEntry; - -typedef struct nn_DeviceInfo { - const char *address; - const nn_DeviceInfoEntry *entries; -} nn_DeviceInfo; - -// adds some device information to the computer. This can also be removed. -// Entries is terminated by a NULL name, and preferrably also NULL value. -// It is perfectly fine to free entries after the call, it is copied. -nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *address, const nn_DeviceInfoEntry entries[]); -// Removes info assicated with a device -void nn_removeDeviceInfo(nn_Computer *computer, const char *address); -// gets the device info array. -const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len); - -typedef enum nn_MethodFlags { - // calling will consume the entire call budget - NN_INDIRECT = 0, - // calling will only consume 1 call from the call budget - NN_DIRECT = (1<<0), - // this indicates this method wraps a *field* - // getter means calling it with no arguments will return the current value, - NN_GETTER = (1<<1), - // this indicates this method wraps a *field* - // setter means calling it with 1 argument will try to set the value. - NN_SETTER = (1<<2), -} nn_MethodFlags; - -#define NN_FIELD_MASK (NN_GETTER | NN_SETTER) - -typedef struct nn_Method { - const char *name; - const char *docString; - nn_MethodFlags flags; -} nn_Method; - -typedef struct nn_ComponentState nn_ComponentState; - -typedef enum nn_ComponentAction { - // create the local state - NN_COMP_INIT, - // delete the local state - NN_COMP_DEINIT, - // perform a method call - NN_COMP_CALL, - // check if a method is enabled - NN_COMP_ENABLED, - // delete the type userdata - NN_COMP_FREETYPE, -} nn_ComponentAction; - -typedef struct nn_ComponentRequest { - // the userdata of the component type. This may be an associated VM, for example. - void *typeUserdata; - // the userdata of the component, passed in addComponent. This may be an associated resource, for example. - void *compUserdata; - // the local state of the component. NN_COMP_INIT should initialize this pointer. - void *state; - nn_Computer *computer; - // address of the component - const char *compAddress; - // the action requested - nn_ComponentAction action; - // for NN_COMP_CALL, it is the method called. - // for NN_COMP_ENABLED, it is the method being checked. - const char *methodCalled; - union { - // for NN_COMP_CALL, it is the amount of return values. - size_t returnCount; - // for NN_COMP_ENABLED, it is whether the method is enabled. - bool methodEnabled; - }; -} nn_ComponentRequest; - -typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req); - -// Creates a new component type. It is safe to free name and methods afterwards. -nn_ComponentState *nn_createComponentState(nn_Universe *universe, const char *name, void *userdata, const nn_Method methods[], nn_ComponentHandler *handler); -// NOTE: do not destroy this before destroying any components using it, or any computers with components using it. -// The component type is still used one last time for the destructor of the components. -void nn_destroyComponentState(nn_ComponentState *cstate); - -// adds a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component added. -// You MUST NOT destroy the component type while a component using that type still exists. -// You can free the address after the call just fine. -nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentState *cstate, const char *address, int slot, void *userdata); -// Checks if a component of that address exists. -bool nn_hasComponent(nn_Computer *computer, const char *address); -// Checks if the component has that method. -// This not only checks if the method exists in the component type, -// but also checks if the method is enabled for the component instance. -bool nn_hasMethod(nn_Computer *computer, const char *address, const char *method); -// removes a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component removed. -nn_Exit nn_removeComponent(nn_Computer *computer, const char *address); -// Gets the name of a type of a component. -const char *nn_getComponentType(nn_Computer *computer, const char *address); -// Gets the slot of a component. -int nn_getComponentSlot(nn_Computer *computer, const char *address); -// Returns the array of component methods. This can be used for doc strings or just listing methods. -const nn_Method *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len); -// get the address at a certain index. -// It'll return NULL for out of bounds indexes. -// This can be used to iterate over all components. -const char *nn_getComponentAddress(nn_Computer *computer, size_t idx); -// Returns the doc-string associated with a method. -const char *nn_getComponentDoc(nn_Computer *computer, const char *address, const char *method); -void *nn_getComponentUserdata(nn_Computer *computer, const char *address); - -// this uses the call stack. -// Component calls must not call other components, it just doesn't work. -// The lack of an argument count is because the entire call stack is assumed to be the arguments. -// In the case of NN_EBUSY, you should call it again with the same arguments later. -nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method); - -// Sets the call budget. -// The default is 1,000. -void nn_setCallBudget(nn_Computer *computer, size_t budget); - -// gets the total call budget -size_t nn_getCallBudget(nn_Computer *computer); - -// returns the remaining call budget -size_t nn_callBudgetRemaining(nn_Computer *computer); - -// automatically called by nn_tick() -void nn_resetCallBudget(nn_Computer *computer); - -// returns whether there is no more call budget left. -// 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. -// Internally, reference counting is used to manage the memory automatically. The API is designed such that strong reference cycles -// cannot occur. - -// returns if there is enough space for [amount] values -bool nn_checkstack(nn_Computer *computer, size_t amount); - -// pushes a null on the call stack -nn_Exit nn_pushnull(nn_Computer *computer); -// pushes a boolean on the call stack -nn_Exit nn_pushbool(nn_Computer *computer, bool truthy); -// pushes a number on the call stack -nn_Exit nn_pushnumber(nn_Computer *computer, double num); -// casts [num] to a double and pushes it on the call stack -nn_Exit nn_pushinteger(nn_Computer *computer, intptr_t num); -// pushes a NULL-terminated string on the call stack. The string is copied, so you can free it afterwards without worry. -nn_Exit nn_pushstring(nn_Computer *computer, const char *str); -// pushes a string on the call stack. The string is copied, so you can free it afterwards without worry. The copy will have a NULL terminator inserted -// at the end for APIs which need it, but the length is also stored. -nn_Exit nn_pushlstring(nn_Computer *computer, const char *str, size_t len); -// pushes a computer userdata to the stack. This is indicative of a resource, such as an HTTP request. -nn_Exit nn_pushuserdata(nn_Computer *computer, size_t userdataIdx); -// pushes a table meant to be an array. [len] is the length of the array. The keys are numbers and 1-indexed, just like in Lua. -// The values are popped, then the array is pushed. -nn_Exit nn_pusharraytable(nn_Computer *computer, size_t len); -// pushes a table. [len] is the amount of pairs. Keys should not be duplicated, as they are not de-duplicated. -// The stack should have a sequence of K1,V1,K2,V2,etc., len pairs long. -// The pairs are popped, then the array is pushed. -nn_Exit nn_pushtable(nn_Computer *computer, size_t len); - -// stack management - -// pops the top value off the stack -nn_Exit nn_pop(nn_Computer *computer); -// pops the top N values off the stack -nn_Exit nn_popn(nn_Computer *computer, size_t n); -// pushes the top value onto the stack, effectively duplicating the top value. -nn_Exit nn_dupe(nn_Computer *computer); -// pushes the top N values onto the stack, effectively duplicating the top N values. -nn_Exit nn_dupen(nn_Computer *computer, size_t n); - -// pushes the value at idx. -nn_Exit nn_dupeat(nn_Computer *computer, size_t idx); - -// get the current amount of values on the call stack. -// For component calls, calling this at the start effectively gives you the argument count. -size_t nn_getstacksize(nn_Computer *computer); -// Removes all values from the stack. -// It is recommended to do this when initiating a component call or -// after returning from errors, as the call stack may have -// random junk on it. -void nn_clearstack(nn_Computer *computer); - -// type check! The API may misbehave if types do not match, so always type-check! - -// Returns whether the value at [idx] is a null. -// [idx] starts at 0. -bool nn_isnull(nn_Computer *computer, size_t idx); -// Returns whether the value at [idx] is a boolean. -// [idx] starts at 0. -bool nn_isboolean(nn_Computer *computer, size_t idx); -// Returns whether the value at [idx] is a number. -// [idx] starts at 0. -bool nn_isnumber(nn_Computer *computer, size_t idx); -// Returns whether the value at [idx] is a number AND -// the number can safely be cast to an intptr_t. -bool nn_isinteger(nn_Computer *computer, size_t idx); -// Returns whether the value at [idx] is a string. -// [idx] starts at 0. -bool nn_isstring(nn_Computer *computer, size_t idx); -// Returns whether the value at [idx] is a userdata. -// [idx] starts at 0. -bool nn_isuserdata(nn_Computer *computer, size_t idx); -// Returns whether the value at [idx] is a table. -// [idx] starts at 0. -bool nn_istable(nn_Computer *computer, size_t idx); -// Returns the name of the type of the value at that index. -// For out of bounds indexes, "none" is returned. -const char *nn_typenameof(nn_Computer *computer, size_t idx); - -// Argument helpers - -// Returns true if the argument at that index is not null. -bool nn_checknull(nn_Computer *computer, size_t idx, const char *errMsg); -// Returns true if the argument at that index is not a boolean. -bool nn_checkboolean(nn_Computer *computer, size_t idx, const char *errMsg); -// Returns true if the argument at that index is not a number. -bool nn_checknumber(nn_Computer *computer, size_t idx, const char *errMsg); -// Returns true if the argument at that index is not an integer. -bool nn_checkinteger(nn_Computer *computer, size_t idx, const char *errMsg); -// Returns true if the argument at that index is not a string. -bool nn_checkstring(nn_Computer *computer, size_t idx, const char *errMsg); -// Returns true if the argument at that index is not userdata. -bool nn_checkuserdata(nn_Computer *computer, size_t idx, const char *errMsg); -// Returns true if the argument at that index is a table. -bool nn_checktable(nn_Computer *computer, size_t idx, const char *errMsg); - -// Checks if idx is equal to the stack size. -// If it is, it will push a null. -nn_Exit nn_defaultnull(nn_Computer *computer, size_t idx); -// Checks if idx is equal to the stack size. -// If it is, it will push a boolean [value]. -nn_Exit nn_defaultboolean(nn_Computer *computer, size_t idx, bool value); -// Checks if idx is equal to the stack size. -// If it is, it will push a number [num]. -nn_Exit nn_defaultnumber(nn_Computer *computer, size_t idx, double num); -// Checks if idx is equal to the stack size. -// If it is, it will push an integer [num]. -nn_Exit nn_defaultinteger(nn_Computer *computer, size_t idx, intptr_t num); -// Checks if idx is equal to the stack size. -// If it is, it will push a string [str]. -nn_Exit nn_defaultstring(nn_Computer *computer, size_t idx, const char *str); -// Checks if idx is equal to the stack size. -// If it is, it will push a string [str]. -nn_Exit nn_defaultlstring(nn_Computer *computer, size_t idx, const char *str, size_t len); -// Checks if idx is equal to the stack size. -// If it is, it will push the userdata [userdataIdx]. -nn_Exit nn_defaultuserdata(nn_Computer *computer, size_t idx, size_t userdataIdx); -// Checks if idx is equal to the stack size. -// If it is, it will push an empty table. -nn_Exit nn_defaulttable(nn_Computer *computer, size_t idx); - -// NOTE: behavior of the nn_to*() functions and nn_dumptable() when the values have the wrong types or at out of bounds indexes is undefined. - -// Returns the boolean value at [idx]. -bool nn_toboolean(nn_Computer *computer, size_t idx); -// Returns the number value at [idx]. -double nn_tonumber(nn_Computer *computer, size_t idx); -// Returns the number value at [idx] cast to an intptr_t. -// NOTE: for numbers where nn_isinteger() returns false, -// the cast is undefined. -// This includes values such as infinity and NaN, where -// the behavior is platform, ABI and compiler-specific. -intptr_t nn_tointeger(nn_Computer *computer, size_t idx); -// Returns the string value at [idx]. -const char *nn_tostring(nn_Computer *computer, size_t idx); -// Returns the string value and its length at [idx]. -const char *nn_tolstring(nn_Computer *computer, size_t idx, size_t *len); -// Returns the userdata index at [idx]. -size_t nn_touserdata(nn_Computer *computer, size_t idx); -// Takes a table value and pushes onto the stack the key-value pairs, as well as writes how many there were in [len]. -// It pushes them as K1,V1,K2,V2,K3,V3,etc., just like went in to pushtable. And yes, the keys are not de-duplicated. -nn_Exit nn_dumptable(nn_Computer *computer, size_t idx, size_t *len); - -// computes the cost of the top [values] values using the same algorithm as -// the modem. -// It will return -1 if the values are invalid. -// The algorithm is as mentioned in https://ocdoc.cil.li/component:modem -// and is as follows: -// - Every value adds a 2 byte overhead -// - Numbers add another 8 bytes, true/false/null another 4 bytes, strings as -// many bytes as they contain, except empty strings count as 1 byte. -int nn_countValueCost(nn_Computer *computer, size_t values); - -// computes the signal cost. -// This is a slightly modified version of value cost, except it allows -// tables and userdata. -// All values are always valid. -// For userdata and tables: -// - Userdata adds another 8 bytes overhead like numbers do. -// - Tables add yet another 2 byte overhead for their terminator, and the sum of all of the size of the keys and values they contain as per this algorithm. -size_t nn_countSignalCost(nn_Computer *computer, size_t values); - -// Returns the amount of signals stored -size_t nn_countSignals(nn_Computer *computer); -// Pops [valueCount] values from the call stack and pushes them as a signal. -nn_Exit nn_pushSignal(nn_Computer *computer, size_t valueCount); -// Removes the first signal and pushes the values onto the call stack, while setting valueCount to the amount of values in the signal. -// If there is no signal, it returns EBADSTATE -nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount); - -// The high-level API of the built-in components. -// These components still make no assumptions about the OS, and still require handlers to connect them to the outside work. - -// TODO: screen, gpu, filesystem, eeprom and the rest of the universe - -typedef enum nn_EEPROMAction { - // the eeprom instance has been dropped - NN_EEPROM_DROP, - // the eeprom state has been dropped - NN_EEPROM_FREE, - NN_EEPROM_GET, - NN_EEPROM_SET, - NN_EEPROM_GETDATA, - NN_EEPROM_SETDATA, - NN_EEPROM_GETLABEL, - NN_EEPROM_SETLABEL, - NN_EEPROM_GETARCH, - NN_EEPROM_SETARCH, - NN_EEPROM_ISREADONLY, - NN_EEPROM_MAKEREADONLY, -} nn_EEPROMAction; - -typedef struct nn_EEPROMRequest { - // associated userdata - void *userdata; - // associated component userdata - void *instance; - // the computer making the request - nn_Computer *computer; - const struct nn_EEPROM *eepromConf; - nn_EEPROMAction action; - // all the get* options should set this to the length, - // and its initial value is the capacity of [buf]. - // For ISREADONLY, this should be set to 0 if false and 1 if true. - unsigned int buflen; - // this may be the buffer length - 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 energy cost of reading an EEPROM - double readEnergyCost; - // the energy cost of reading an EEPROM's associated data - double readDataEnergyCost; - // the energy cost of writing to an EEPROM - double writeEnergyCost; - // the energy cost of writing to an EEPROM's associated data - double writeDataEnergyCost; - // idle time added when writing code - double writeDelay; - // idle time added when writing data - double writeDataDelay; -} nn_EEPROM; - -// Tier 1 - The normal EEPROM equivalent -// Tier 2 - A better EEPROM -// Tier 3 - An even better EEPROM -// Tier 4- The best EEPROM -extern nn_EEPROM nn_defaultEEPROMs[4]; - -typedef struct nn_VEEPROM { - const char *code; - size_t codelen; - const char *data; - size_t datalen; - const char *label; - size_t labellen; - const char *arch; - 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_ComponentState *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata); -nn_ComponentState *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem); - -// Note on paths: -// - Paths given always have their length stored, but also have a NULL terminator. -// - Paths are validated. They check for illegal characters as per OC's definition. -// - Logical paradoxes such as rename("a", "a/b") are automatically checked and handled. -// - \ are automatically replaced with / -// - .. and leading / is handled automatically. This also improves sandboxing, as ../a.txt would become just a.txt -// - For rename, it automatically checks if the destination exists and if so, errors out. -typedef enum nn_FilesystemAction { - // the filesystem instance has been dropped. - // This is just for computer-local state, make sure to free it. - NN_FS_DROP, - // the filesystem state has been dropped. - // Make sure to close all file descriptors which are still open. - NN_FS_FREE, - // open a file. strarg1 stores the path, and strarg2 stores the mode. - // strarg1len and strarg2len are their respective lengths. - // The output should be in fd. - NN_FS_OPEN, - // read a file. - // The file descriptor is stored in fd, - // make sure to ensure it is valid. - // strarg1len is the capacity of strarg1. - // Write the result of reading into strarg1. - // Update strarg1len to reflect the amount of data read. - // Set strarg1 to NULL to indicate EOF. - NN_FS_READ, - // write to a file. - // The file descriptor is stored in fd, - // make sure to ensure it is valid. - // strarg1len is the amount of data to write. - // strarg1 is the contents of the buffer to write. - NN_FS_WRITE, - // seek a file. - // The file descriptor is stored in fd, - // make sure to ensure it is valid. - // The offset is stored in off. - // The seek mode is stored in whence. - // It should set off to the new position. - NN_FS_SEEK, - // close a file. - // The file descriptor is stored in fd, - // make sure to ensure it is valid. - NN_FS_CLOSE, - // open a directory file descriptor. - // The result should be in fd. - NN_FS_OPENDIR, - // read a directory file descriptor, stored in fd. - // 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 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 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, - // Create a directory at a given path stored in strarg1. - // strarg1len is the length of the path. - // It is meant to also create parent directories recursively - // as needed. - NN_FS_MKDIR, - // Return the lastmodified timestamp. - // 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. - // If it is not, size should be set to 0. - NN_FS_ISDIRECTORY, - // Checks if the filesystem is read-only. - // If it is, size should be set to 1. - // If it is not, size should be set to 0. - NN_FS_ISREADONLY, - // 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. - NN_FS_EXISTS, - // Returns the label. - // The label should be written into strarg1, with strarg1len as the capacity. - // Set strarg1len to the label length. - NN_FS_GETLABEL, - // Sets the label. - // The label is stored in strarg1, with strarg1len as the length. - NN_FS_SETLABEL, - // Gets the space used, it should be stored in size. - NN_FS_SPACEUSED, - // Gets 2 paths, strarg1 and strarg2, with their lengths. - // It should try to rename strarg1 to strarg2, as in, - // it should move strarg1 to be at strarg2, potentially - // using recursive directory copies. - NN_FS_RENAME, - // Removes the path stored in strarg1. - NN_FS_REMOVE, - // Returns the size of the entry at strarg1. - // The size of a directory is typically 0. - // The size of a file is typically the amount of bytes in its contents. - // Using other measures of size will rarely break code, - // but may confuse users. - NN_FS_SIZE, -} nn_FilesystemAction; - -typedef enum nn_FilesystemWhence { - // relative to start - NN_SEEK_SET, - // relative to the current position - NN_SEEK_CUR, - // relative to the EOF position. - NN_SEEK_END, -} nn_FilesystemWhence; - -typedef struct nn_FilesystemRequest { - void *userdata; - void *instance; - nn_Computer *computer; - struct nn_Filesystem *fsConf; - nn_FilesystemAction action; - int fd; - nn_FilesystemWhence whence; - int off; - char *strarg1; - size_t strarg1len; - char *strarg2; - size_t strarg2len; - 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, size, lastModified, 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; -// a generic tmpfs -extern nn_Filesystem nn_defaultTmpFS; - -typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request); - -typedef struct nn_VFileNode { - // the name of the node. - // This is the raw name, do not append / to directories. - const char *name; - // if NULL, the node is a directory. - const char *data; - union { - // for files, how much of data to read. - size_t dataLen; - // for directories, the amount of entries encoded afterwards. - // Do note that entry encoding is recursive, so for example - // a(1) b(2) c("hi") d("there"), means directory a/ has a directory b/ which has 2 files, c and d, - // even though a's entry count is 1. - size_t entryCount; - }; -} nn_VFileNode; - -typedef struct nn_VFilesystem { - const char *label; - size_t labellen; - bool isReadOnly; - // The maximum amount of directory entries. This is used to pre-allocate an array. - // It also helps against memory hogging attacks. - size_t maxDirEntries; - // the maximum amount of nodes the filesystem can have. This is also used to pre-allocate an array. - size_t maxNodeCount; - // used to compute lastModified. This, together with the context's time procedure, is used to compute the timestamp. - // It must be a UNIX timestamp, else you'll get weird results. - size_t creationTime; - size_t rootNodeCount; - // the flat array of the filesystem. See nn_VFileNode for details. - nn_VFileNode *image; -} nn_VFilesystem; - -nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata); -nn_ComponentState *nn_createVFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, const nn_VFilesystem *vfs); - -typedef enum nn_DriveAction { - // instance dropped - NN_DRIVE_DROP, - // free screen state - NN_DRIVE_FREE, - // Gets the current label. - // [index] is set to the capacity of [buf]. - // You must write the label into [buf], then set [index] to the length of the label. - // Empty label means no label. - NN_DRIVE_GETLABEL, - // Sets the current label. - // [index] is set to the length of [buf]. - // Empty label means no label. - // Set [index] to the new length of the label, if it has been truncated. - NN_DRIVE_SETLABEL, - // gets the current read head, or more accurately, the last sector used - // in order to compute seeking penalties. - // You must output the current read head in [index]. - NN_DRIVE_GETCURSECTOR, - // Reads a sector. - // The sector index is in [index], and the contents are in [buf]. - NN_DRIVE_READSECTOR, - // Writes a sector. - // The sector index is in [index]. - // Output the contents of that sector in [buf]. - NN_DRIVE_WRITESECTOR, - // Reads a byte - // The byte index is in [index]. - // You must output the byte in [byte]. - NN_DRIVE_READBYTE, - // Writes a byte. - // The byte index is in [index], the byte is in [byte]. - NN_DRIVE_WRITEBYTE, -} nn_DriveAction; - -// Note that sectors and bytes are 1-indexed. -// Bounds checking is done automatically by the interface. -typedef struct nn_DriveRequest { - void *userdata; - void *instance; - nn_Computer *computer; - struct nn_Drive *driveConf; - nn_DriveAction action; - size_t index; - union { - char *buf; - // OC explicitly uses *signed* chars. - // Helper methods for reading unsigned bytes cast it to an unsigned byte first. - // Just, do not ask. - signed char byte; - }; -} nn_DriveRequest; - -typedef nn_Exit nn_DriveHandler(nn_DriveRequest *req); - -typedef struct nn_Drive { - // The capacity of the drive. - // It is in bytes, but it MUST be a multiple of the sector size. - // The total amount of sectors, as in capacity / sectorSize, must also be divisible by the platter count. - // If it is not, it is UB. - size_t capacity; - // the sector size, typically 512 - size_t sectorSize; - // the amount of platters the drive has. This contributes to how many "rotations" are needed. - // A drive with 8 sectors but 1 platter, when seeking from sector 1 to 8, would mean 7 rotations. - // However, if it has 2 platters, it'd be seen as 1 to 4 being at the same angle as 5 to 8, which - // would mean only 3 rotations. - size_t platterCount; - // how many reads can be issued per tick. - // Reading either a sector or a byte counts as 1 read. - size_t readsPerTick; - // how many writes can be issued per tick. - // Writing a sector counts as 1 write. - // Writing a byte counts as 1 read and 1 write, - // you can imagine it as reading the sector, editing the byte, - // then writing the sector back. - size_t writesPerTick; - // Set to 0 for *infinite*, effectively an SSD. - // This would mean there is 0 penalty for seeking (technically unreliastic even for an SSD). - // This is simply used to compute idle time. It is in literal full rotations per minute. - size_t rpm; - // If false, it behaves like a normal OC drive, where the drive can spin backwards to seek. - // However, this is unrealistic, as doing so may crack the sensitive platter and make the - // reader lose lift. - // For fans of physics, this option only allows the seeks to go forwards. - // This is super punishing at a slow RPM, so it is recommended to bump up - // the RPM to something like 7200 RPM. - bool onlySpinForwards; - // 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_Drive; - -typedef struct nn_VDrive { - // initial label - const char *label; - size_t labellen; - // initial data - const char *data; - size_t datalen; -} nn_VDrive; - -extern nn_Drive nn_defaultDrives[4]; - -nn_ComponentState *nn_createDrive(nn_Universe *universe, const nn_Drive *drive, nn_DriveHandler *handler, void *userdata); -nn_ComponentState *nn_createVDrive(nn_Universe *universe, const nn_Drive *drive, const nn_VDrive *vdrive); - -typedef enum nn_ScreenAction { - // instance dropped - NN_SCR_DROP, - // free screen state - NN_SCR_FREE, - - // set w to 1 if it is on, or 0 if it is off. - NN_SCR_ISON, - // attempt to turn the screen on. - // set w to 1 if it was on, or 0 if it was off. - // set h to 1 if it is now on, or 0 if it is now off. - NN_SCR_TURNON, - // attempt to turn the screen off. - // set w to 1 if it was on, or 0 if it was off. - // set h to 1 if it is now on, or 0 if it is now off. - NN_SCR_TURNOFF, - // get a keyboard. The index requested is stored in h. - // If the index is out of bounds, set keyboard to NULL. - // Else, write the keyboard address into the buffer in keyboard. - // The capacity of the buffer is stored in w. - NN_SCR_GETKEYBOARD, - // change the screen to/from precise mode. - // Precise mode means mouse events will have real-number coordinates, as opposed to integer-based ones. - // NeoNucleus does not automatically round this, you are meant to round it. - // The new precision value is stored in w, where it is a 1 to enable it and 0 to disable it. - // Set w to 1 if precise mode is now enabled, or 0 if it isn't. - NN_SCR_SETPRECISE, - // Set w to 1 if precise mode is enabled, or 0 if it isn't. - NN_SCR_ISPRECISE, - // change the screen to/from inverted touch mode. - // Inverted touch mode normally provides an alternative way to interact with the touchscreen. - // For example, in OC, it makes the GUI only open with shift+rightclick, and normal rightclick - // triggers a touch event instead. It is best to give it an equivalent meaning to OC's to prevent - // unexpected program behavior. - // The new inverted touch mode state is stored in w, where it is a 1 to enable it and 0 to disable it. - // Set w to 1 if inverted touch mode is now enabled, or 0 if it isn't. - NN_SCR_SETTOUCHINVERTED, - // Set w to 1 if inverted touch mode is enabled, or 0 if it isn't. - NN_SCR_ISTOUCHINVERTED, - // Gets the aspect ratio (amount of screen blocks joined together). - // Outside of MC, this may not make much sense, in which case you can just set it to 1x1. - // Store the width in w and the height in h. - NN_SCR_GETASPECTRATIO, -} nn_ScreenAction; - -typedef struct nn_ScreenRequest { - void *userdata; - void *instance; - nn_Computer *computer; - nn_ScreenAction action; - int w; - int h; - char *keyboard; -} nn_ScreenRequest; - -typedef enum nn_ScreenFeatures { - NN_SCRF_NONE = 0, - // whether it supports mouse input. - // If it doesn't, it should not emit - // touch, drag or other mouse events. - // Walk events should also not be emitted. - NN_SCRF_MOUSE = 1<<0, - // Whether precise mode is supported. - NN_SCRF_PRECISE = 1<<1, - // Whether touch inverted is supported. - NN_SCRF_TOUCHINVERTED = 1<<2, - // it indicates that the palette can be edited. - NN_SCRF_EDITABLECOLORS = 1<<3, -} nn_ScreenFeatures; - -// A struct for the reference screen configurations -// This does not influence the interface at all, -// however it exists as a runtime reference of what -// the conventional screen tiers are. -typedef struct nn_ScreenConfig { - int maxWidth; - int maxHeight; - nn_ScreenFeatures features; - int paletteColors; - char maxDepth; -} nn_ScreenConfig; - -// OC has 3 tiers, NN adds a 4th one as well. -extern nn_ScreenConfig nn_defaultScreens[4]; - -typedef nn_Exit nn_ScreenHandler(nn_ScreenRequest *req); - -nn_ComponentState *nn_createScreen(nn_Universe *universe, nn_ScreenHandler *handler, void *userdata); -// a useless component which does nothing -nn_ComponentState *nn_createKeyboard(nn_Universe *universe); - -// Remember: -// - Colors are in 0xRRGGBB format. -// - Screen coordinates and palettes are 1-indexed. -// - If NN_GPU_SETRESOLUTION returns NN_OK, a screen_resized signal is queued automatically. -// - VRAM is always fast -typedef enum nn_GPUAction { - // instance dropped - NN_GPU_DROP, - // component state dropped - NN_GPU_FREE, - - // Conventional GPU functions - - // requests to bind to a screen connected to the computer. - // The address is stored in text, with the length in width. - // The interface does check that the computer does have the screen, but do look out - // for time-of-check/time-of-use issues which may occur in multi-threaded environments. - // If x is set to 1, the reset flag is enabled. This means the GPU should "reset" the state - // of the screen. - NN_GPU_BIND, - // requests to unbind the GPU from its screen. - // If there is no screen, it just does nothing. - NN_GPU_UNBIND, - // Ask for the screen the GPU is currently bound to. - // If it is not bound to any, text should be set to NULL. - // If it is, you must write to text the address of the screen. - // width stores the capacity of text, so if needed, truncate it to that many bytes. - // The length of this address must be stored in width. - NN_GPU_GETSCREEN, - // Gets the current background. - // x should store either the color in 0xRRGGBB format or the palette index. - // y should be 1 if x is a palette index and 0 if it is a color. - NN_GPU_GETBACKGROUND, - // Sets the current background. - // x should store either the color in 0xRRGGBB format or the palette index. - // y should be 1 if x is a palette index and 0 if it is a color. - // The values x and y should be updated to reflect the old state. - NN_GPU_SETBACKGROUND, - // Gets the current foreground. - // x should store either the color in 0xRRGGBB format or the palette index. - // y should be 1 if x is a palette index and 0 if it is a color. - NN_GPU_GETFOREGROUND, - // Sets the current foreground. - // x should store either the color in 0xRRGGBB format or the palette index. - // y should be 1 if x is a palette index and 0 if it is a color. - // The values x and y should be updated to reflect the old state. - NN_GPU_SETFOREGROUND, - // Gets the palette color. - // x is the index. - // y should be set to the color. - NN_GPU_GETPALETTECOLOR, - // Gets the palette color. - // x is the index. - // y is the color. - NN_GPU_SETPALETTECOLOR, - // Gets the maximum depth supported by the GPU and screen. - // Valid depth values in OC are 1, 4 and 8, however NN also recognizes 2, 3, 16 and 24. - // The result should be stored in x. - NN_GPU_MAXDEPTH, - // Gets the current depth the screen is displaying at. - // The result should be stored in x. - NN_GPU_GETDEPTH, - // Sets the current depth the screen is displaying at. - // The new depth is in x. - // This should not change the stored color values of neither the palette nor the characters, - // but simply change what their color is translated to graphically. - // The old depth should be stored in x. - NN_GPU_SETDEPTH, - // Gets the maximum resolution supported by the GPU and screen. - // Result should be in width and height. - NN_GPU_MAXRESOLUTION, - // Gets the resolution of the screen. - // Result should be in width and height. - NN_GPU_GETRESOLUTION, - // Sets the resolution of the screen. - // The new resolution should be stored in width and height. - // If successful, a screen_resized event is implicitly queued. - NN_GPU_SETRESOLUTION, - // Gets the current screen viewport. - // The result should be in width and height. - NN_GPU_GETVIEWPORT, - // Sets the screen viewport. - // The new viewport dimensions are stored in width and height. - NN_GPU_SETVIEWPORT, - // Gets a character. - // The position requested is given in x and y. - // The codepoint of the character should be set in [codepoint]. - // The foreground and background color should be set in [width] and [height]. - // The palette indexes of the foreground and background should be set - // in [dest] and [src] respectively. If the pixel color was not from - // the palette, the imaginary -1 palette index can be used. - NN_GPU_GET, - // Sets a horizontal line of text at a given x, y. - // The position is stored in x, y, and is the position of the first character. - // The text goes left-to-right on the horizontal line. Anything off-screen is discared. - // There is no wrapping. - // The text is stored in text, with the size of the text, in bytes, being stored in width. - NN_GPU_SET, - // like NN_GPU_SET, but the text is set vertically. - // This means instead of going from left-to-right on the screen on a horizontal line, - // it is up-to-down on a vertical line. - NN_GPU_SETVERTICAL, - // Copies a portion of the screen to another location. - // The rectangle being copied is width x height, and has the top-left corner at x, y. - // The destination rectangle is also width x height, but has the top-left corner at x + tx, y + ty. - // The copy happens as if it is using an intermediary buffer, thus even if the source and destination - // intersect, the order in which characters are copied must not change the result. - NN_GPU_COPY, - // Fills a rectangle - // The rectangle's top-left corner is at x, y, and its dimensions are width x height. - // The character it should be filled with has its unicode codepoint stored in codepoint. - NN_GPU_FILL, - - // VRAM buffers (always blazing fast) - - // Should return the current active buffer. - // 0 for the screen, or if there is no screen. - // The result should be stored in x. - NN_GPU_GETACTIVEBUFFER, - // Switches the active buffer to a new one, stored in x. - NN_GPU_SETACTIVEBUFFER, - // Gets a buffer by index in an imaginary list containing all of them. - // The index is in x, the buffer is output in y. - // If y is 0, the sequence is assumed to end. - NN_GPU_BUFFERS, - // Allocates a buffer. - // The buffer sizes are in width and height, with 0 x 0 meaning max resolution (default). - // This consumes exactly width * height VRAM. - // The new buffer should be put in x. - // If there was not enough VRAM for this, x can be set to 0. - NN_GPU_ALLOCBUFFER, - // Frees a buffer. - // The buffer is stored in x. - // This releases the same VRAM that the buffer consumed when allocated. - NN_GPU_FREEBUFFER, - // Frees all buffers. The free VRAM should be equal to the total VRAM after this. - NN_GPU_FREEBUFFERS, - // Gets memory info about the GPU. - // x should be set to the amount of free VRAM available. - NN_GPU_FREEMEM, - // Gets the size of a buffer, stored in x. - // The size should be stored in width and height. - NN_GPU_GETBUFFERSIZE, - // Copy a region between buffers or between the screen and buffers. - // The destination buffer is stored in dest. If 0, it refers to the screen. - // The source buffer is stored in src. If 0, it refers to the screen. - // x, y, width and height define the source rectangle, in the same way as in fill, to copy from the source buffer. - // tx, ty refer to the top-left corner for the destination rectangle, in the destination buffer. It has the same width - // and height as the source rectangle. - // Screen-to-screen copies are illegal and checked, no need to worry about handling them. - NN_GPU_BITBLT, -} nn_GPUAction; - -typedef struct nn_GPURequest { - void *userdata; - void *instance; - nn_Computer *computer; - struct nn_GPU *gpuConf; - nn_GPUAction action; - int x; - int y; - int width; - int height; - union { - struct { - int tx; - int ty; - }; - nn_codepoint codepoint; - char *text; - }; - int dest; - int src; -} nn_GPURequest; - -typedef struct nn_GPU { - // the minimum between these and the screen's - // are the maximum width/height/depth supported. - int maxWidth; - int maxHeight; - char maxDepth; - // this is in pixels. - size_t totalVRAM; - // amount of times copy can be called before running out of budget. - int copyPerTick; - // amount of times fill can be called before running out of budget. - int fillPerTick; - // amount of times set can be called before running out of budget. - int setPerTick; - // amount of times setForeground can be called before running out of budget. - int setForegroundPerTick; - // amount of times setBackground can be called before running out of budget. - int setBackgroundPerTick; - // energy per non-space set. - double energyPerWrite; - // energy per space set. - double energyPerClear; -} nn_GPU; - -typedef nn_Exit nn_GPUHandler(nn_GPURequest *req); - -// 1 GPU tier for every screen. -extern nn_GPU nn_defaultGPUs[4]; - -nn_ComponentState *nn_createGPU(nn_Universe *universe, const nn_GPU *gpu, nn_GPUHandler *handler, void *userdata); - -// Colors and palettes. -// Do note that the - -// The NeoNucleus 2-bit palette -extern int nn_palette2[4]; - -// The NeoNucleus 3-bit palette -extern int nn_palette3[8]; - -// The OC 4-bit palette. -extern int nn_ocpalette4[16]; - -// The Minecraft 4-bit palette, using dye colors. -extern int nn_mcpalette4[16]; - -// The OC 8-bit palette. -extern int nn_ocpalette8[256]; - -// initializes the contents of the palettes. -void nn_initPalettes(); - -// Expensive. -// Maps a color to the closest match in a palette. -int nn_mapColor(int color, int *palette, size_t len); -// Expensive. -// Maps a color within a given depth. -// ocCompatible only matters for 4-bit, and determines whether to use the OC palette or the MC palette. -// Invalid depths behave identically to 24-bit, in which case the color is left unchanged. -int nn_mapDepth(int color, int depth, bool ocCompatible); - -// the name of a depth, if valid. -// If invalid, NULL is returned, thus this can be used to check -// if a depth is valid as well. -// Valid depths are 1, 2, 3, 4, 8, 16 and 24. -const char *nn_depthName(int depth); - -// Signal helpers - -// common mouse buttons, not an exhaustive list -#define NN_BUTTON_LEFT 0 -#define NN_BUTTON_RIGHT 1 -#define NN_BUTTON_MIDDLE 2 - -// OC keycodes -// taken from https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua -#define NN_KEY_UNKNOWN 0 -#define NN_KEY_1 0x02 -#define NN_KEY_2 0x03 -#define NN_KEY_3 0x04 -#define NN_KEY_4 0x05 -#define NN_KEY_5 0x06 -#define NN_KEY_6 0x07 -#define NN_KEY_7 0x08 -#define NN_KEY_8 0x09 -#define NN_KEY_9 0x0A -#define NN_KEY_0 0x0B -#define NN_KEY_A 0x1E -#define NN_KEY_B 0x30 -#define NN_KEY_C 0x2E -#define NN_KEY_D 0x20 -#define NN_KEY_E 0x12 -#define NN_KEY_F 0x21 -#define NN_KEY_G 0x22 -#define NN_KEY_H 0x23 -#define NN_KEY_I 0x17 -#define NN_KEY_J 0x24 -#define NN_KEY_K 0x25 -#define NN_KEY_L 0x26 -#define NN_KEY_M 0x32 -#define NN_KEY_N 0x31 -#define NN_KEY_O 0x18 -#define NN_KEY_P 0x19 -#define NN_KEY_Q 0x10 -#define NN_KEY_R 0x13 -#define NN_KEY_S 0x1F -#define NN_KEY_T 0x14 -#define NN_KEY_U 0x16 -#define NN_KEY_V 0x2F -#define NN_KEY_W 0x11 -#define NN_KEY_X 0x2D -#define NN_KEY_Y 0x15 -#define NN_KEY_Z 0x2C - -#define NN_KEY_APOSTROPHE 0x28 -#define NN_KEY_AT 0x91 -#define NN_KEY_BACK 0x0E -#define NN_KEY_BACKSLASH 0x2B -// caps-lock -#define NN_KEY_CAPITAL 0x3A -#define NN_KEY_COLON 0x92 -#define NN_KEY_COMMA 0x33 -#define NN_KEY_ENTER 0x1C -#define NN_KEY_EQUALS 0x0D -// accent grave -#define NN_KEY_GRAVE 0x29 -#define NN_KEY_LBRACKET 0x1A -#define NN_KEY_LCONTROL 0x1D -// left alt -#define NN_KEY_LMENU 0x38 -#define NN_KEY_LSHIFT 0x2A -#define NN_KEY_MINUS 0x0C -#define NN_KEY_NUMLOCK 0x45 -#define NN_KEY_PAUSE 0xC5 -#define NN_KEY_PERIOD 0x34 -#define NN_KEY_RBRACKET 0x1B -#define NN_KEY_RCONTROL 0x9D -// right alt -#define NN_KEY_RMENU 0xB8 -#define NN_KEY_RSHIFT 0x36 -// scroll lock -#define NN_KEY_SCROLL 0x46 -#define NN_KEY_SEMICOLON 0x27 -#define NN_KEY_SLASH 0x35 -#define NN_KEY_SPACE 0x39 -#define NN_KEY_STOP 0x95 -#define NN_KEY_TAB 0x0F -#define NN_KEY_UNDERLINE 0x93 - -#define NN_KEY_UP 0xC8 -#define NN_KEY_DOWN 0xD0 -#define NN_KEY_LEFT 0xCB -#define NN_KEY_RIGHT 0xCD -#define NN_KEY_HOME 0xC7 -#define NN_KEY_END 0xCF -#define NN_KEY_PAGEUP 0xC9 -#define NN_KEY_PAGEDOWN 0xD1 -#define NN_KEY_INSERT 0xD2 -#define NN_KEY_DELETE 0xD3 - -#define NN_KEY_F1 0x3B -#define NN_KEY_F2 0x3C -#define NN_KEY_F3 0x3D -#define NN_KEY_F4 0x3E -#define NN_KEY_F5 0x3F -#define NN_KEY_F6 0x40 -#define NN_KEY_F7 0x41 -#define NN_KEY_F8 0x42 -#define NN_KEY_F9 0x43 -#define NN_KEY_F10 0x44 -#define NN_KEY_F11 0x57 -#define NN_KEY_F12 0x58 -#define NN_KEY_F13 0x64 -#define NN_KEY_F14 0x65 -#define NN_KEY_F15 0x66 -#define NN_KEY_F16 0x67 -#define NN_KEY_F17 0x68 -#define NN_KEY_F18 0x69 -#define NN_KEY_F19 0x71 - -#define NN_KEY_KANA 0x70 -#define NN_KEY_KANJI 0x94 -#define NN_KEY_CONVERT 0x79 -#define NN_KEY_NOCONVERT 0x7B -#define NN_KEY_YEN 0x7D -#define NN_KEY_CIRCUMFLEX 0x90 -#define NN_KEY_AX 0x96 - -#define NN_KEY_NUMPAD0 0x52 -#define NN_KEY_NUMPAD1 0x4F -#define NN_KEY_NUMPAD2 0x50 -#define NN_KEY_NUMPAD3 0x51 -#define NN_KEY_NUMPAD4 0x4B -#define NN_KEY_NUMPAD5 0x4C -#define NN_KEY_NUMPAD6 0x4D -#define NN_KEY_NUMPAD7 0x47 -#define NN_KEY_NUMPAD8 0x48 -#define NN_KEY_NUMPAD9 0x49 -#define NN_KEY_NUMPADMUL 0x37 -#define NN_KEY_NUMPADDIV 0xB5 -#define NN_KEY_NUMPADSUB 0x4A -#define NN_KEY_NUMPADADD 0x4E -#define NN_KEY_NUMPADDECIMAL 0x53 -#define NN_KEY_NUMPADCOMMA 0xB3 -#define NN_KEY_NUMPADENTER 0x9C -#define NN_KEY_NUMPADEQUALS 0x8D - -// pushes a screen_resized signal -nn_Exit nn_pushScreenResized(nn_Computer *computer, const char *screenAddress, int newWidth, int newHeight); -// pushes a touch signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushTouch(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); -// pushes a drag signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushDrag(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); -// pushes a drop signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); -// pushes a scroll signal -// A positive direction usually means up, a negative one usually means down. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player); -// pushes a walk signal -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player); - -// pushes a key_down event -// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. -// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. -// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushKeyDown(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); -// pushes a key_up event -// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. -// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. -// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushKeyUp(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); -// pushes a clipboard event -// clipboard should be a NULL-terminated string. -// NN does no truncation of the contents, but it is best to limit it. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, const char *player); -// pushes a clipboard event -// len is the length of the clipboard. -// NN does no truncation of the contents, but it is best to limit it. -// The signal is checked, as in, the player must be a user of the computer if users are defined. -nn_Exit nn_pushLClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, size_t len, const char *player); - -// TODO: the remaining vanilla ones in https://ocdoc.cil.li/component:signals - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/component.c b/src/component.c deleted file mode 100644 index e27c08a..0000000 --- a/src/component.c +++ /dev/null @@ -1,150 +0,0 @@ -#include "neonucleus.h" -#include "component.h" - -nn_componentTable *nn_newComponentTable(nn_Alloc *alloc, const char *typeName, void *userdata, nn_componentConstructor *constructor, nn_componentDestructor *destructor) { - nn_componentTable *table = nn_alloc(alloc, sizeof(nn_componentTable)); - if(table == NULL) { - return NULL; - } - table->name = nn_strdup(alloc, typeName); - if(table->name == NULL) { - nn_dealloc(alloc, table, sizeof(nn_componentTable)); - return NULL; - } - table->userdata = userdata; - table->constructor = constructor; - table->destructor = destructor; - table->methodCount = 0; - table->alloc = *alloc; - return table; -} - -void nn_destroyComponentTable(nn_componentTable *table) { - nn_Alloc alloc = table->alloc; - nn_deallocStr(&alloc, table->name); - for(nn_size_t i = 0; i < table->methodCount; i++) { - nn_method_t method = table->methods[i]; - nn_deallocStr(&alloc, method.name); - nn_deallocStr(&alloc, method.doc); - } - nn_dealloc(&alloc, table, sizeof(nn_componentTable)); -} - -nn_method_t *nn_defineMethod(nn_componentTable *table, const char *methodName, nn_componentMethod *methodFunc, const char *methodDoc) { - if(table->methodCount == NN_MAX_METHODS) return NULL; - nn_method_t method; - method.method = methodFunc; - method.name = nn_strdup(&table->alloc, methodName); - if(method.name == NULL) return NULL; - method.direct = true; - method.doc = nn_strdup(&table->alloc, methodDoc); - if(method.doc == NULL) { - nn_deallocStr(&table->alloc, method.name); - return NULL; - } - method.userdata = NULL; - method.condition = NULL; - table->methods[table->methodCount] = method; - nn_method_t *ptr = table->methods + table->methodCount; - table->methodCount++; - return ptr; -} - -void nn_method_setDirect(nn_method_t *method, nn_bool_t direct) { - method->direct = direct; -} - -void nn_method_setUserdata(nn_method_t *method, void *userdata) { - method->userdata = userdata; -} - -void nn_method_setCondition(nn_method_t *method, nn_componentMethodCondition_t *condition) { - method->condition = condition; -} - -const char *nn_getTableMethod(nn_componentTable *table, nn_size_t idx, nn_bool_t *outDirect) { - if(idx >= table->methodCount) return NULL; - nn_method_t method = table->methods[idx]; - if(outDirect != NULL) *outDirect = method.direct; - return method.name; -} - -const char *nn_methodDoc(nn_componentTable *table, const char *methodName) { - for(nn_size_t i = 0; i < table->methodCount; i++) { - if(nn_strcmp(table->methods[i].name, methodName) == 0) { - return table->methods[i].doc; - } - } - return NULL; -} - -static nn_bool_t nni_checkMethodEnabled(nn_method_t method, void *statePtr) { - if(method.condition == NULL) return true; - return method.condition(statePtr, method.userdata); -} - -nn_bool_t nn_isMethodEnabled(nn_component *component, const char *methodName) { - nn_componentTable *table = component->table; - for(nn_size_t i = 0; i < table->methodCount; i++) { - if(nn_strcmp(table->methods[i].name, methodName) == 0) { - nn_method_t method = table->methods[i]; - return nni_checkMethodEnabled(method, component->statePtr); - } - } - return false; -} - -nn_computer *nn_getComputerOfComponent(nn_component *component) { - return component->computer; -} - -nn_address nn_getComponentAddress(nn_component *component) { - return component->address; -} - -int nn_getComponentSlot(nn_component *component) { - return component->slot; -} - -nn_componentTable *nn_getComponentTable(nn_component *component) { - return component->table; -} - -const char *nn_getComponentType(nn_componentTable *table) { - return table->name; -} - -void *nn_getComponentUserdata(nn_component *component) { - return component->statePtr; -} - -nn_bool_t nn_invokeComponentMethod(nn_component *component, const char *name) { - nn_componentTable *table = component->table; - for(nn_size_t i = 0; i < table->methodCount; i++) { - nn_method_t method = table->methods[i]; - if(nn_strcmp(method.name, name) == 0) { - nn_callCost(component->computer, NN_CALL_COST); - if(!method.direct) { - nn_triggerIndirect(component->computer); - } - if(!nni_checkMethodEnabled(method, component->statePtr)) { - return false; // pretend it's gone - } - method.method(component->statePtr, method.userdata, component, component->computer); - return true; - } - } - - // no such method - return false; -} - -void nn_simulateBufferedIndirect(nn_component *component, double amount, double amountPerTick) { - double maximum = 100.0; - double x = amount * maximum / amountPerTick; - component->indirectBufferProgress += x; - if(component->indirectBufferProgress >= maximum) { - component->indirectBufferProgress = 0; - nn_triggerIndirect(component->computer); - } -} diff --git a/src/component.h b/src/component.h deleted file mode 100644 index 797b477..0000000 --- a/src/component.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef NEONUCLEUS_COMPONENT_H -#define NEONUCLEUS_COMPONENT_H - -#include "neonucleus.h" -#include "computer.h" - -typedef struct nn_method_t { - char *name; - nn_componentMethod *method; - void *userdata; - char *doc; - nn_bool_t direct; - nn_componentMethodCondition_t *condition; -} nn_method_t; - -typedef struct nn_componentTable { - char *name; - nn_Alloc alloc; - void *userdata; - nn_componentConstructor *constructor; - nn_componentDestructor *destructor; - nn_method_t methods[NN_MAX_METHODS]; - nn_size_t methodCount; -} nn_componentTable; - -typedef struct nn_component { - nn_address address; - int slot; - float indirectBufferProgress; - nn_componentTable *table; - void *statePtr; - nn_computer *computer; -} nn_component; - -#endif diff --git a/src/components/diskDrive.c b/src/components/diskDrive.c deleted file mode 100644 index e99a895..0000000 --- a/src/components/diskDrive.c +++ /dev/null @@ -1,112 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_diskDrive { - nn_Context ctx; - nn_guard *lock; - nn_refc refc; - nn_diskDriveTable table; -} nn_diskDrive; - -nn_diskDrive *nn_newDiskDrive(nn_Context *context, nn_diskDriveTable table) { - nn_diskDrive *drive = nn_alloc(&context->allocator, sizeof(nn_diskDrive)); - if(drive == NULL) return NULL; - drive->lock = nn_newGuard(context); - if(drive->lock == NULL) { - nn_dealloc(&context->allocator, drive, sizeof(nn_diskDrive)); - } - drive->refc = 1; - drive->table = table; - drive->ctx = *context; - return drive; -} - -nn_guard *nn_getDiskDriveLock(nn_diskDrive *diskDrive) { - return diskDrive->lock; -} - -void nn_retainDiskDrive(nn_diskDrive *diskDrive) { - nn_incRef(&diskDrive->refc); -} - -nn_bool_t nn_destroyDiskDrive(nn_diskDrive *diskDrive) { - if(!nn_decRef(&diskDrive->refc)) return false; - if(diskDrive->table.deinit != NULL) { - diskDrive->table.deinit(diskDrive->table.userdata); - } - nn_Context ctx = diskDrive->ctx; - nn_Alloc a = ctx.allocator; - - nn_deleteGuard(&ctx, diskDrive->lock); - nn_dealloc(&a, diskDrive, sizeof(nn_diskDrive)); - return true; -} - -void nn_diskDrive_destroy(void *_, nn_component *component, nn_diskDrive *diskDrive) { - nn_destroyDiskDrive(diskDrive); -} - -void nn_diskDrive_eject(nn_diskDrive *diskDrive, void *_, nn_component *component, nn_computer *computer) { - double velocity = nn_toNumberOr(nn_getArgument(computer, 0), 0); - - nn_errorbuf_t err = ""; - nn_lock(&diskDrive->ctx, diskDrive->lock); - if(diskDrive->table.isEmpty(diskDrive->table.userdata)) { - nn_unlock(&diskDrive->ctx, diskDrive->lock); - nn_return_boolean(computer, false); - return; - } - diskDrive->table.eject(diskDrive->table.userdata, velocity, err); - nn_unlock(&diskDrive->ctx, diskDrive->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, true); -} - -void nn_diskDrive_isEmpty(nn_diskDrive *diskDrive, void *_, nn_component *component, nn_computer *computer) { - nn_lock(&diskDrive->ctx, diskDrive->lock); - nn_bool_t empty = diskDrive->table.isEmpty(diskDrive->table.userdata); - nn_unlock(&diskDrive->ctx, diskDrive->lock); - - nn_return_boolean(computer, empty); -} - -void nn_diskDrive_media(nn_diskDrive *diskDrive, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - nn_Alloc *a = &diskDrive->ctx.allocator; - nn_lock(&diskDrive->ctx, diskDrive->lock); - if(diskDrive->table.isEmpty(diskDrive->table.userdata)) { - nn_unlock(&diskDrive->ctx, diskDrive->lock); - nn_setCError(computer, "drive is empty"); - return; - } - nn_address s = diskDrive->table.media(diskDrive->table.userdata, a, err); - nn_unlock(&diskDrive->ctx, diskDrive->lock); - - if(!nn_error_isEmpty(err)) { - nn_deallocStr(a, s); - nn_setError(computer, err); - return; - } - - nn_return_string(computer, s, nn_strlen(s)); - nn_deallocStr(a, s); -} - -void nn_loadDiskDriveTable(nn_universe *universe) { - nn_componentTable *diskDriveTable = nn_newComponentTable(nn_getAllocator(universe), "disk_drive", NULL, NULL, (void *)nn_diskDrive_destroy); - nn_storeUserdata(universe, "NN:DISK_DRIVE", diskDriveTable); - - nn_defineMethod(diskDriveTable, "eject", (nn_componentMethod *)nn_diskDrive_eject, "eject([velocity: number]): boolean - Ejects the floopy, if present. Returns whether it was present."); - nn_defineMethod(diskDriveTable, "isEmpty", (nn_componentMethod *)nn_diskDrive_isEmpty, "isEmpty(): boolean - Returns whether the drive is empty."); - nn_defineMethod(diskDriveTable, "media", (nn_componentMethod *)nn_diskDrive_media, "media(): string - Returns the address of the inner floppy disk."); -} - -nn_component *nn_addDiskDrive(nn_computer *computer, nn_address address, int slot, nn_diskDrive *diskDrive) { - nn_componentTable *diskDriveTable = nn_queryUserdata(nn_getUniverse(computer), "NN:DISK_DRIVE"); - - return nn_newComponent(computer, address, slot, diskDriveTable, diskDrive); -} diff --git a/src/components/drive.c b/src/components/drive.c deleted file mode 100644 index 787883c..0000000 --- a/src/components/drive.c +++ /dev/null @@ -1,257 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_drive { - nn_refc refc; - nn_guard *lock; - nn_Context ctx; - nn_size_t currentSector; - nn_driveTable table; - nn_driveControl ctrl; -} nn_drive; - -nn_drive *nn_newDrive(nn_Context *context, nn_driveTable table, nn_driveControl control) { - nn_drive *d = nn_alloc(&context->allocator, sizeof(nn_drive)); - if(d == NULL) return NULL; - d->lock = nn_newGuard(context); - if(d->lock == NULL) { - nn_dealloc(&context->allocator, d, sizeof(nn_drive)); - return NULL; - } - d->refc = 1; - d->table = table; - d->ctrl = control; - d->currentSector = 0; - d->ctx = *context; - return d; -} - -nn_guard *nn_getDriveLock(nn_drive *drive) { - return drive->lock; -} - -void nn_retainDrive(nn_drive *drive) { - nn_incRef(&drive->refc); -} - -nn_bool_t nn_destroyDrive(nn_drive *drive) { - if(!nn_decRef(&drive->refc)) return false; - - if(drive->table.deinit != NULL) { - drive->table.deinit(drive->table.userdata); - } - - nn_Context ctx = drive->ctx; - - nn_deleteGuard(&ctx, drive->lock); - nn_dealloc(&ctx.allocator, drive, sizeof(nn_drive)); - return true; -} - -void nn_drive_destroy(void *_, nn_component *component, nn_drive *drive) { - nn_destroyDrive(drive); -} - -void nni_drive_readCost(nn_component *component, nn_drive *drive) { - nn_driveControl ctrl = drive->ctrl; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_simulateBufferedIndirect(component, 1, ctrl.readSectorsPerTick); - nn_addHeat(computer, ctrl.readHeatPerSector); - nn_removeEnergy(computer, ctrl.readEnergyPerSector); -} - -void nni_drive_writeCost(nn_component *component, nn_drive *drive) { - nn_driveControl ctrl = drive->ctrl; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_simulateBufferedIndirect(component, 1, ctrl.writeSectorsPerTick); - nn_addHeat(computer, ctrl.writeHeatPerSector); - nn_removeEnergy(computer, ctrl.writeEnergyPerSector); -} - -void nni_drive_seekTo(nn_component *component, nn_drive *drive, nn_size_t sector) { - sector = sector - 1; // switch to 0 to N-1 sector addressing, - // which is much nicer to do math with - // and Lua made a big oopsie. - nn_size_t old = drive->currentSector; - nn_driveControl ctrl = drive->ctrl; - if(ctrl.seekSectorsPerTick == 0) return; // seek latency disabled - nn_computer *computer = nn_getComputerOfComponent(component); - - // Compute important constants - nn_size_t sectorSize = drive->table.sectorSize; - nn_size_t platterCount = drive->table.platterCount; - nn_size_t capacity = drive->table.capacity; - - nn_size_t sectorsPerPlatter = (capacity / sectorSize) / platterCount; - - sector %= sectorsPerPlatter; - - if(old == sector) return; - drive->currentSector = sector; - - nn_size_t moved = 0; - if(sector > old) { - moved = sector - old; // moved forwards - } else if(sector < old) { - // moved back, depends on some options - if(ctrl.reversable) { - // horribly fucking unrealistic, as HDDs - // spin at ~7200 RPM, and if it decides - // to spontaneously instantly reverse direction, - // the force would crack the disk - // However, real life is a myth. - moved = old - sector; - } else { - // full turn - moved = sectorsPerPlatter - old; - } - } - - nn_simulateBufferedIndirect(component, moved, ctrl.seekSectorsPerTick); - nn_addHeat(computer, ctrl.motorHeatPerSector * moved); - nn_removeEnergy(computer, ctrl.motorEnergyPerSector * moved); -} - -void nn_drive_getLabel(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - char buf[NN_LABEL_SIZE]; - nn_size_t l = NN_LABEL_SIZE; - nn_lock(&drive->ctx, drive->lock); - drive->table.getLabel(drive->table.userdata, buf, &l); - nn_unlock(&drive->ctx, drive->lock); - if(l == 0) { - nn_return(computer, nn_values_nil()); - } else { - nn_return_string(computer, buf, l); - } -} -void nn_drive_setLabel(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_size_t l = 0; - nn_value label = nn_getArgument(computer, 0); - const char *buf = nn_toString(label, &l); - if(buf == NULL) { - nn_setCError(computer, "bad label (string expected)"); - return; - } - nn_lock(&drive->ctx, drive->lock); - l = drive->table.setLabel(drive->table.userdata, buf, l); - nn_unlock(&drive->ctx, drive->lock); - nn_return_string(computer, buf, l); -} -void nn_drive_getSectorSize(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_size_t sector_size = drive->table.sectorSize; - nn_return(computer, nn_values_integer(sector_size)); -} -void nn_drive_getPlatterCount(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_size_t platter_count = drive->table.platterCount; - nn_return(computer, nn_values_integer(platter_count)); -} -void nn_drive_getCapacity(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_size_t capacity = drive->table.capacity; - nn_return(computer, nn_values_integer(capacity)); -} -void nn_drive_readSector(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_value sectorValue = nn_getArgument(computer, 0); - int sector = nn_toInt(sectorValue); - nn_size_t sector_size = drive->table.sectorSize; - // we leave the +1 intentionally to compare the end of the real sector - if (sector < 1 || (sector * sector_size > drive->table.capacity)) { - nn_setCError(computer, "bad argument #1 (sector out of range)"); - return; - } - char buf[sector_size]; - nn_lock(&drive->ctx, drive->lock); - drive->table.readSector(drive->table.userdata, sector, buf); - nn_unlock(&drive->ctx, drive->lock); - nn_return_string(computer, buf, sector_size); -} -void nn_drive_writeSector(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_value sectorValue = nn_getArgument(computer, 0); - int sector = nn_toInt(sectorValue); - nn_size_t sector_size = drive->table.sectorSize; - nn_value bufValue = nn_getArgument(computer, 1); - - nn_size_t buf_size = 0; - const char *buf = nn_toString(bufValue, &buf_size); - if (buf_size != sector_size) { - nn_setCError(computer, "bad argument #2 (expected buffer of length `sectorSize`)"); - return; - } - // we leave the +1 intentionally to compare the end of the real sector - if (sector < 1 || (sector * sector_size > drive->table.capacity)) { - nn_setCError(computer, "bad argument #1 (sector out of range)"); - return; - } - nn_lock(&drive->ctx, drive->lock); - drive->table.writeSector(drive->table.userdata, sector, buf); - nn_unlock(&drive->ctx, drive->lock); - - nn_return_boolean(computer, true); -} -void nn_drive_readByte(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_value offsetValue = nn_getArgument(computer, 0); - nn_size_t disk_offset = nn_toInt(offsetValue) - 1; - nn_size_t sector_size = drive->table.sectorSize; - int sector = (disk_offset / sector_size) + 1; - nn_size_t sector_offset = disk_offset % sector_size; - - if (disk_offset >= drive->table.capacity) { - nn_setCError(computer, "bad argument #1 (index out of range)"); - return; - } - char buf[sector_size]; - nn_lock(&drive->ctx, drive->lock); - drive->table.readSector(drive->table.userdata, sector, buf); - nn_unlock(&drive->ctx, drive->lock); - - nn_return(computer, nn_values_integer(buf[sector_offset])); -} -void nn_drive_writeByte(nn_drive *drive, void *_, nn_component *component, nn_computer *computer) { - nn_value offsetValue = nn_getArgument(computer, 0); - nn_value writeValue = nn_getArgument(computer, 1); - nn_size_t disk_offset = nn_toInt(offsetValue) - 1; - nn_integer_t write = nn_toInt(writeValue); - nn_size_t sector_size = drive->table.sectorSize; - int sector = (disk_offset / sector_size) + 1; - nn_size_t sector_offset = disk_offset % sector_size; - - if (write < -128 || write > 255) { - nn_setCError(computer, "bad argument #2 (byte out of range)"); - return; - } - if (disk_offset >= drive->table.capacity) { - nn_setCError(computer, "bad argument #1 (index out of range)"); - return; - } - nn_lock(&drive->ctx, drive->lock); - char buf[sector_size]; - drive->table.readSector(drive->table.userdata, sector, buf); - - buf[sector_offset] = write; - - drive->table.writeSector(drive->table.userdata, sector, buf); - nn_unlock(&drive->ctx, drive->lock); - - nn_return_boolean(computer, true); -} - -void nn_loadDriveTable(nn_universe *universe) { - nn_componentTable *driveTable = nn_newComponentTable(nn_getAllocator(universe), "drive", NULL, NULL, (nn_componentDestructor *)nn_drive_destroy); - nn_storeUserdata(universe, "NN:DRIVE", driveTable); - - nn_defineMethod(driveTable, "getLabel", (nn_componentMethod *)nn_drive_getLabel, "getLabel():string - Get the current label of the drive."); - nn_defineMethod(driveTable, "setLabel", (nn_componentMethod *)nn_drive_setLabel, "setLabel(value:string):string - Sets the label of the drive. Returns the new value, which may be truncated."); - nn_defineMethod(driveTable, "getSectorSize", (nn_componentMethod *)nn_drive_getSectorSize, "getSectorSize():number - Returns the size of a single sector on the drive, in bytes."); - nn_defineMethod(driveTable, "getPlatterCount", (nn_componentMethod *)nn_drive_getPlatterCount, "getPlatterCount():number - Returns the number of platters in the drive."); - nn_defineMethod(driveTable, "getCapacity", (nn_componentMethod *)nn_drive_getCapacity, "getCapacity():number - Returns the total capacity of the drive, in bytes."); - nn_defineMethod(driveTable, "readSector", (nn_componentMethod *)nn_drive_readSector, "readSector(sector:number):string - Read the current contents of the specified sector."); - nn_defineMethod(driveTable, "writeSector", (nn_componentMethod *)nn_drive_writeSector, "writeSector(sector:number, value:string) - Write the specified contents to the specified sector."); - nn_defineMethod(driveTable, "readByte", (nn_componentMethod *)nn_drive_readByte, "readByte(offset:number):number - Read a single byte at the specified offset."); - nn_defineMethod(driveTable, "writeByte", (nn_componentMethod *)nn_drive_writeByte, "writeByte(offset:number, value:number) - Write a single byte to the specified offset."); -} - -nn_component *nn_addDrive(nn_computer *computer, nn_address address, int slot, nn_drive *drive) { - nn_componentTable *driveTable = nn_queryUserdata(nn_getUniverse(computer), "NN:DRIVE"); - return nn_newComponent(computer, address, slot, driveTable, drive); -} - diff --git a/src/components/eeprom.c b/src/components/eeprom.c deleted file mode 100644 index 0bd92ed..0000000 --- a/src/components/eeprom.c +++ /dev/null @@ -1,390 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_eeprom { - nn_Context ctx; - nn_refc refc; - nn_guard *lock; - nn_eepromTable table; - nn_eepromControl control; -} nn_eeprom; - -nn_eeprom *nn_newEEPROM(nn_Context *context, nn_eepromTable table, nn_eepromControl control) { - nn_eeprom *e = nn_alloc(&context->allocator, sizeof(nn_eeprom)); - if(e == NULL) return NULL; - e->lock = nn_newGuard(context); - if(e->lock == NULL) { - nn_dealloc(&context->allocator, e, sizeof(nn_eeprom)); - return NULL; - } - e->ctx = *context; - e->refc = 1; - e->control = control; - e->table = table; - return e; -} - -nn_guard *nn_getEEPROMLock(nn_eeprom *eeprom) { - return eeprom->lock; -} -void nn_retainEEPROM(nn_eeprom *eeprom) { - nn_incRef(&eeprom->refc); -} - -nn_bool_t nn_destroyEEPROM(nn_eeprom *eeprom) { - if(!nn_decRef(&eeprom->refc)) return false; - // no need to lock, we are the only one with a reference - - if(eeprom->table.deinit != NULL) { - eeprom->table.deinit(eeprom->table.userdata); - } - - nn_Context ctx = eeprom->ctx; - - nn_deleteGuard(&ctx, eeprom->lock); - nn_dealloc(&ctx.allocator, eeprom, sizeof(nn_eeprom)); - - return true; -} - -static void nn_eeprom_readCost(nn_component *component, nn_size_t bytesRead) { - nn_eepromControl control = ((nn_eeprom *)nn_getComponentUserdata(component))->control; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_removeEnergy(computer, control.readEnergyCostPerByte * bytesRead); - nn_addHeat(computer, control.readHeatPerByte * bytesRead); - nn_simulateBufferedIndirect(component, bytesRead, control.bytesReadPerTick); -} - -static void nn_eeprom_writeCost(nn_component *component, nn_size_t bytesWritten) { - nn_eepromControl control = ((nn_eeprom *)nn_getComponentUserdata(component))->control; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_removeEnergy(computer, control.writeEnergyCostPerByte * bytesWritten); - nn_addHeat(computer, control.writeHeatPerByte * bytesWritten); - nn_simulateBufferedIndirect(component, bytesWritten, control.bytesWrittenPerTick); -} - -void nn_eeprom_destroy(void *_, nn_component *component, nn_eeprom *eeprom) { - nn_destroyEEPROM(eeprom); -} - -void nn_eeprom_getSize(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_return(computer, nn_values_integer(eeprom->table.size)); -} - -void nn_eeprom_getDataSize(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_return(computer, nn_values_integer(eeprom->table.dataSize)); -} - -void nn_eeprom_getLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - char buf[NN_LABEL_SIZE]; - nn_size_t l = NN_LABEL_SIZE; - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - eeprom->table.getLabel(eeprom->table.userdata, buf, &l, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - if(l == 0) { - nn_return(computer, nn_values_nil()); - } else { - nn_return_string(computer, buf, l); - } - - // Latency, energy costs and stuff - nn_eeprom_readCost(component, l); -} - -void nn_eeprom_setLabel(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_size_t l = 0; - nn_value label = nn_getArgument(computer, 0); - const char *buf = nn_toString(label, &l); - if(buf == NULL) { - nn_setCError(computer, "bad label (string expected)"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - l = eeprom->table.setLabel(eeprom->table.userdata, buf, l, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_string(computer, buf, l); - - // Latency, energy costs and stuff - nn_eeprom_writeCost(component, l); -} - -void nn_eeprom_get(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_size_t cap = eeprom->table.size; - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - char *buf = nn_alloc(alloc, cap); - if(buf == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - nn_size_t len = eeprom->table.get(eeprom->table.userdata, buf, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_string(computer, buf, len); - nn_dealloc(alloc, buf, cap); - - nn_eeprom_readCost(component, len); -} - -void nn_eeprom_set(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_value data = nn_getArgument(computer, 0); - nn_size_t len; - const char *buf = nn_toString(data, &len); - if(len > eeprom->table.size) { - nn_setCError(computer, "out of space"); - return; - } - if(buf == NULL) { - if(data.tag == NN_VALUE_NIL) { - buf = ""; - len = 0; - } else { - nn_setCError(computer, "bad data (string expected)"); - return; - } - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - if(eeprom->table.isReadonly(eeprom->table.userdata, err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_setCError(computer, "readonly"); - return; - } - if(!nn_error_isEmpty(err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_setError(computer, err); - return; - } - eeprom->table.set(eeprom->table.userdata, buf, len, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_eeprom_writeCost(component, len); -} - -void nn_eeprom_getData(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_size_t cap = eeprom->table.dataSize; - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - char *buf = nn_alloc(alloc, cap); - if(buf == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - nn_size_t len = eeprom->table.getData(eeprom->table.userdata, buf, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_string(computer, buf, len); - nn_dealloc(alloc, buf, cap); - - nn_eeprom_readCost(component, len); -} - -void nn_eeprom_setData(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_value data = nn_getArgument(computer, 0); - nn_size_t len = 0; - const char *buf = nn_toString(data, &len); - if(buf == NULL) { - if(data.tag == NN_VALUE_NIL) { - buf = ""; - len = 0; - } else { - nn_setCError(computer, "bad data (string expected)"); - return; - } - } - if(len > eeprom->table.dataSize) { - nn_setCError(computer, "out of space"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - if(eeprom->table.isReadonly(eeprom->table.userdata, err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_setCError(computer, "readonly"); - return; - } - if(!nn_error_isEmpty(err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_setError(computer, err); - return; - } - eeprom->table.setData(eeprom->table.userdata, buf, len, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_eeprom_writeCost(component, len); -} - -void nn_eeprom_getArchitecture(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - char *s = eeprom->table.getArchitecture(alloc, eeprom->table.userdata, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - if(s == NULL) { - nn_return_nil(computer); - return; - } - - nn_size_t l = nn_strlen(s); - - nn_return_string(computer, s, nn_strlen(s)); - - nn_deallocStr(alloc, s); - - nn_eeprom_readCost(component, l); -} - -void nn_eeprom_setArchitecture(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_value data = nn_getArgument(computer, 0); - const char *buf = nn_toCString(data); - if(buf == NULL) { - nn_setCError(computer, "bad data (string expected)"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - if(eeprom->table.isReadonly(eeprom->table.userdata, err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_setCError(computer, "readonly"); - return; - } - if(!nn_error_isEmpty(err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_setError(computer, err); - return; - } - eeprom->table.setArchitecture(eeprom->table.userdata, buf, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_eeprom_writeCost(component, nn_strlen(buf)); -} - -void nn_eeprom_isReadOnly(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - nn_return(computer, nn_values_boolean(eeprom->table.isReadonly(eeprom->table.userdata, err))); - nn_unlock(&eeprom->ctx, eeprom->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } -} - -void nn_eeprom_makeReadonly(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - nn_bool_t done =eeprom->table.makeReadonly(eeprom->table.userdata, err); - nn_unlock(&eeprom->ctx, eeprom->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_boolean(computer, done); -} - -void nn_eeprom_getChecksum(nn_eeprom *eeprom, void *_, nn_component *component, nn_computer *computer) { - nn_size_t dataCap = eeprom->table.dataSize; - nn_size_t codeCap = eeprom->table.size; - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - char *buf = nn_alloc(alloc, dataCap + codeCap); - if(buf == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&eeprom->ctx, eeprom->lock); - nn_size_t dataLen = eeprom->table.getData(eeprom->table.userdata, buf, err); - if(!nn_error_isEmpty(err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_dealloc(alloc, buf, dataCap + codeCap); - nn_setError(computer, err); - return; - } - int codeLen = eeprom->table.get(eeprom->table.userdata, buf + dataLen, err); - if(!nn_error_isEmpty(err)) { - nn_unlock(&eeprom->ctx, eeprom->lock); - nn_dealloc(alloc, buf, dataCap + codeCap); - nn_setError(computer, err); - return; - } - nn_unlock(&eeprom->ctx, eeprom->lock); - char hash[4]; - nn_data_crc32(buf, dataLen + codeLen, hash); - nn_dealloc(alloc, buf, dataCap + codeCap); - - char encoded[8]; - - const char *hex = "0123456789abcdef"; - for(int i = 0; i < 4; i++) { - unsigned char b = hash[i]; - encoded[i*2] = hex[b >> 4]; - encoded[i*2+1] = hex[b & 0xF]; - } - - nn_return_string(computer, encoded, sizeof(encoded)); - - nn_eeprom_readCost(component, dataLen + codeLen); -} - -void nn_loadEepromTable(nn_universe *universe) { - nn_componentTable *eepromTable = nn_newComponentTable(nn_getAllocator(universe), "eeprom", NULL, NULL, (void *)nn_eeprom_destroy); - nn_storeUserdata(universe, "NN:EEPROM", eepromTable); - - nn_defineMethod(eepromTable, "getSize", (void *)nn_eeprom_getSize, "getSize(): integer - Returns the maximum code capacity of the EEPROM."); - nn_defineMethod(eepromTable, "getDataSize", (void *)nn_eeprom_getDataSize, "getDataSize(): integer - Returns the maximum data capacity of the EEPROM."); - nn_defineMethod(eepromTable, "getLabel", (void *)nn_eeprom_getLabel, "getLabel(): string - Returns the current label."); - nn_defineMethod(eepromTable, "setLabel", (void *)nn_eeprom_setLabel, "setLabel(label: string): string - Sets the new label. Returns the actual label set to, which may be truncated."); - nn_defineMethod(eepromTable, "get", (void *)nn_eeprom_get, "get(): string - Reads the current code contents."); - nn_defineMethod(eepromTable, "set", (void *)nn_eeprom_set, "set(data: string) - Sets the current code contents."); - nn_defineMethod(eepromTable, "getData", (void *)nn_eeprom_getData, "getData(): string - Reads the current data contents."); - nn_defineMethod(eepromTable, "setData", (void *)nn_eeprom_setData, "setData(data: string) - Sets the current data contents."); - nn_defineMethod(eepromTable, "getArchitecture", (void *)nn_eeprom_getArchitecture, "getArchitecture(): string - Gets the intended architecture."); - nn_defineMethod(eepromTable, "setArchitecture", (void *)nn_eeprom_setArchitecture, "setArchitecture(data: string) - Sets the intended architecture."); - nn_defineMethod(eepromTable, "isReadOnly", (void *)nn_eeprom_isReadOnly, "isReadOnly(): boolean - Returns whether this EEPROM is read-only."); - nn_defineMethod(eepromTable, "makeReadOnly", (void *)nn_eeprom_makeReadonly, "makeReadOnly() - Makes the current EEPROM read-only. Normally, this cannot be undone."); - nn_defineMethod(eepromTable, "makeReadonly", (void *)nn_eeprom_makeReadonly, "makeReadonly() - Legacy alias to makeReadOnly()"); - nn_defineMethod(eepromTable, "getChecksum", (void *)nn_eeprom_getChecksum, "getChecksum(): string - Returns a checksum of the data on the EEPROM."); -} - -nn_component *nn_addEEPROM(nn_computer *computer, nn_address address, int slot, nn_eeprom *eeprom) { - nn_componentTable *eepromTable = nn_queryUserdata(nn_getUniverse(computer), "NN:EEPROM"); - - return nn_newComponent(computer, address, slot, eepromTable, eeprom); -} diff --git a/src/components/externalComputer.c b/src/components/externalComputer.c deleted file mode 100644 index 029e5bc..0000000 --- a/src/components/externalComputer.c +++ /dev/null @@ -1,194 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_externalComputer_t { - nn_Context ctx; - nn_refc refc; - nn_guard *lock; - nn_externalComputerTable_t table; -} nn_externalComputer_t; - -nn_externalComputer_t *nn_newExternalComputer(nn_Context *ctx, nn_externalComputerTable_t table) { - nn_externalComputer_t *external = nn_alloc(&ctx->allocator, sizeof(nn_externalComputer_t)); - if(external == NULL) return NULL; - external->lock = nn_newGuard(ctx); - if(external->lock == NULL) { - nn_dealloc(&ctx->allocator, external, sizeof(nn_externalComputer_t)); - return NULL; - } - external->refc = 1; - external->table = table; - return external; -} - -nn_guard *nn_externalComputer_getLock(nn_externalComputer_t *external) { - return external->lock; -} - -void nn_externalComputer_retain(nn_externalComputer_t *external) { - nn_incRef(&external->refc); -} - -nn_bool_t nn_externalComputer_destroy(nn_externalComputer_t *external) { - if(!nn_decRef(&external->refc)) return false; - - nn_Context ctx = external->ctx; - - nn_deleteGuard(&ctx, external->lock); - nn_dealloc(&ctx.allocator, external, sizeof(nn_externalComputer_t)); - - return true; -} - -void nni_externalComputer_componentDestroy(void *_, nn_component *component, nn_externalComputer_t *external) { - nn_externalComputer_destroy(external); -} - -static void nni_externalComputer_start(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - - nn_lock(&external->ctx, external->lock); - nn_bool_t worked = external->table.start(external->table.userdata, computer, err); - nn_unlock(&external->ctx, external->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, worked); -} - -static void nni_externalComputer_stop(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - - nn_lock(&external->ctx, external->lock); - nn_bool_t worked = external->table.stop(external->table.userdata, computer, err); - nn_unlock(&external->ctx, external->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, worked); -} - -static void nni_externalComputer_isRunning(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - - nn_lock(&external->ctx, external->lock); - nn_bool_t truthy = external->table.isRunning(external->table.userdata, computer, err); - nn_unlock(&external->ctx, external->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, truthy); -} - -static void nni_externalComputer_isRobot(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - - nn_lock(&external->ctx, external->lock); - nn_bool_t truthy = external->table.isRobot(external->table.userdata, computer, err); - nn_unlock(&external->ctx, external->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, truthy); -} - -static void nni_externalComputer_getArchitecture(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - - nn_lock(&external->ctx, external->lock); - nn_architecture *arch = external->table.getArchitecture(external->table.userdata, computer, err); - nn_unlock(&external->ctx, external->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_cstring(computer, arch->archName); -} - -static void nni_externalComputer_getDeviceInfo(nn_externalComputer_t *external, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - nn_deviceInfoList_t *info = nn_newDeviceInfoList(&external->ctx, 16); - if(info == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - - nn_lock(&external->ctx, external->lock); - external->table.getDeviceInfo(external->table.userdata, info, computer, err); - nn_unlock(&external->ctx, external->lock); - - if(!nn_error_isEmpty(err)) { - nn_deleteDeviceInfoList(info); - nn_setError(computer, err); - return; - } - - nn_size_t deviceCount = nn_getDeviceCount(info); - nn_value devicesSerialized = nn_return_table(computer, deviceCount); - - for(nn_size_t i = 0; i < deviceCount; i++) { - nn_deviceInfo_t *device = nn_getDeviceInfoAt(info, i); - nn_size_t deviceInfoSize = nn_getDeviceKeyCount(device); - - nn_value deviceTable = nn_values_table(&external->ctx.allocator, deviceInfoSize); - - for(nn_size_t j = 0; j < deviceInfoSize; j++) { - const char *value = NULL; - const char *key = nn_iterateDeviceInfoKeys(device, j, &value); - - nn_values_setPair( - deviceTable, - j, - nn_values_string(&external->ctx.allocator, key, nn_strlen(key)), - nn_values_string(&external->ctx.allocator, value, nn_strlen(value)) - ); - } - - const char *addr = nn_getDeviceInfoAddress(device); - nn_values_setPair( - devicesSerialized, - i, - nn_values_string(&external->ctx.allocator, addr, nn_strlen(addr)), - deviceTable - ); - } - - nn_deleteDeviceInfoList(info); -} - -void nn_loadExternalComputerTable(nn_universe *universe) { - nn_componentTable *computerTable = nn_newComponentTable(nn_getAllocator(universe), "modem", NULL, NULL, (nn_componentDestructor *)nni_externalComputer_componentDestroy); - nn_storeUserdata(universe, "NN:COMPUTER", computerTable); - - nn_method_t *method; - method = nn_defineMethod(computerTable, "start", (nn_componentMethod *)nni_externalComputer_start, "start(): boolean - Starts the computer. Returns whether it was successful"); - nn_method_setDirect(method, false); - method = nn_defineMethod(computerTable, "stop", (nn_componentMethod *)nni_externalComputer_stop, "stop(): boolean - Stops the computer. Returns whether it was successful"); - nn_method_setDirect(method, false); - method = nn_defineMethod(computerTable, "isRunning", (nn_componentMethod *)nni_externalComputer_isRunning, "isRunning(): boolean - Returns whether the computer was running"); - nn_method_setDirect(method, false); - method = nn_defineMethod(computerTable, "isRobot", (nn_componentMethod *)nni_externalComputer_isRobot, "isRobot(): boolean - Returns whether the computer was running"); - nn_method_setDirect(method, false); - method = nn_defineMethod(computerTable, "getArchitecture", (nn_componentMethod *)nni_externalComputer_getArchitecture, "getArchitecture(): string - Returns the name of the architecture of the computer"); - nn_method_setDirect(method, false); - method = nn_defineMethod(computerTable, "getDeviceInfo", (nn_componentMethod *)nni_externalComputer_getDeviceInfo, "getDeviceList(): {[string]: {[string]: string}} - Returns information about the devices connected to the computer"); - nn_method_setDirect(method, false); -} - -nn_component *nn_externalComputer_addTo(nn_computer *computer, nn_address address, int slot, nn_externalComputer_t *external) { - nn_componentTable *computerTable = nn_queryUserdata(nn_getUniverse(computer), "NN:COMPUTER"); - return nn_newComponent(computer, address, slot, computerTable, external); -} diff --git a/src/components/filesystem.c b/src/components/filesystem.c deleted file mode 100644 index 1d35724..0000000 --- a/src/components/filesystem.c +++ /dev/null @@ -1,687 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_filesystem { - nn_refc refc; - nn_guard *lock; - nn_Context ctx; - nn_filesystemTable table; - nn_filesystemControl control; - nn_size_t spaceUsedCache; - - // last due to cache concerns (this struck is massive) - void *files[NN_MAX_OPEN_FILES]; -} nn_filesystem; - -void nn_fs_destroy(nn_componentMethod *_, nn_component *component, nn_filesystem *fs) { - nn_destroyFilesystem(fs); -} - -nn_filesystem *nn_newFilesystem(nn_Context *context, nn_filesystemTable table, nn_filesystemControl control) { - nn_filesystem *fs = nn_alloc(&context->allocator, sizeof(nn_filesystem)); - if(fs == NULL) return NULL; - fs->refc = 1; - fs->ctx = *context; - fs->table = table; - fs->control = control; - fs->spaceUsedCache = 0; - fs->lock = nn_newGuard(context); - if(fs->lock == NULL) { - nn_dealloc(&context->allocator, fs, sizeof(nn_filesystem)); - return NULL; - } - - for(nn_size_t i = 0; i < NN_MAX_OPEN_FILES; i++) { - fs->files[i] = NULL; - } - return fs; -} - -nn_guard *nn_getFilesystemLock(nn_filesystem *fs) { - return fs->lock; -} - -void nn_retainFilesystem(nn_filesystem *fs) { - nn_incRef(&fs->refc); -} - -nn_bool_t nn_destroyFilesystem(nn_filesystem *fs) { - if(!nn_decRef(&fs->refc)) return false; - - nn_errorbuf_t err = ""; // ignored - - // close all files - for(nn_size_t i = 0; i < NN_MAX_OPEN_FILES; i++) { - void *f = fs->files[i]; - if(f != NULL) fs->table.close(fs->table.userdata, f, err); - } - - if(fs->table.deinit != NULL) { - fs->table.deinit(fs->table.userdata); - } - - nn_Context ctx = fs->ctx; - nn_deleteGuard(&ctx, fs->lock); - nn_dealloc(&ctx.allocator, fs, sizeof(nn_filesystem)); - return true; -} - -nn_size_t nn_fs_getSpaceUsed(nn_filesystem *fs) { - nn_lock(&fs->ctx, fs->lock); - if(fs->spaceUsedCache != 0) { - nn_unlock(&fs->ctx, fs->lock); - return fs->spaceUsedCache; - } - nn_size_t spaceUsed = fs->table.spaceUsed(fs->table.userdata); - fs->spaceUsedCache = spaceUsed; - nn_unlock(&fs->ctx, fs->lock); - return spaceUsed; -} - -void nn_fs_invalidateSpaceUsed(nn_filesystem *fs) { - nn_lock(&fs->ctx, fs->lock); - fs->spaceUsedCache = 0; - nn_unlock(&fs->ctx, fs->lock); -} - -nn_size_t nn_fs_getSpaceRemaining(nn_filesystem *fs) { - nn_lock(&fs->ctx, fs->lock); - nn_size_t used = nn_fs_getSpaceUsed(fs); - nn_size_t total = fs->table.spaceTotal; - nn_unlock(&fs->ctx, fs->lock); - return total - used; -} - -void *nn_fs_unwrapFD(nn_filesystem *fs, nn_size_t fd) { - if(fd >= NN_MAX_OPEN_FILES) { - return NULL; - } - nn_lock(&fs->ctx, fs->lock); - void *file = fs->files[fd]; - nn_unlock(&fs->ctx, fs->lock); - if(file == NULL) { - return NULL; - } - return file; -} - -void nn_fs_readCost(nn_filesystem *fs, nn_size_t bytes, nn_component *component) { - nn_filesystemControl control = fs->control; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_simulateBufferedIndirect(component, bytes, control.readBytesPerTick); - nn_removeEnergy(computer, control.readEnergyPerByte * bytes); - nn_addHeat(computer, control.readHeatPerByte * bytes); -} - -void nn_fs_writeCost(nn_filesystem *fs, nn_size_t bytes, nn_component *component) { - nn_filesystemControl control = fs->control; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_simulateBufferedIndirect(component, bytes, control.writeBytesPerTick); - nn_removeEnergy(computer, control.writeEnergyPerByte * bytes); - nn_addHeat(computer, control.writeHeatPerByte * bytes); -} - -void nn_fs_removeCost(nn_filesystem *fs, nn_size_t count, nn_component *component) { - nn_filesystemControl control = fs->control; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_simulateBufferedIndirect(component, count, control.removeFilesPerTick); - nn_removeEnergy(computer, control.removeEnergy * count); - nn_addHeat(computer, control.removeHeat * count); -} - -void nn_fs_createCost(nn_filesystem *fs, nn_size_t count, nn_component *component) { - nn_filesystemControl control = fs->control; - nn_computer *computer = nn_getComputerOfComponent(component); - - nn_simulateBufferedIndirect(component, count, control.createFilesPerTick); - nn_removeEnergy(computer, control.createEnergy * count); - nn_addHeat(computer, control.createHeat * count); -} - -void nn_fs_getLabel(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { - char buf[NN_LABEL_SIZE]; - nn_size_t l = NN_LABEL_SIZE; - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - fs->table.getLabel(fs->table.userdata, buf, &l, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - if(l == 0) { - nn_return(computer, nn_values_nil()); - } else { - nn_return_string(computer, buf, l); - } - - nn_fs_readCost(fs, l, component); -} - -void nn_fs_setLabel(nn_filesystem *fs, void *_, nn_component *component, nn_computer *computer) { - nn_size_t l = 0; - nn_value label = nn_getArgument(computer, 0); - const char *buf = nn_toString(label, &l); - if(buf == NULL) { - nn_setCError(computer, "bad label (string expected)"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - l = fs->table.setLabel(fs->table.userdata, buf, l, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_string(computer, buf, l); - - nn_fs_writeCost(fs, l, component); -} - -void nn_fs_spaceUsed(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_size_t space = nn_fs_getSpaceUsed(fs); - nn_return(computer, nn_values_integer(space)); -} - -void nn_fs_spaceTotal(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_return(computer, nn_values_integer(fs->table.spaceTotal)); -} - -void nn_fs_isReadOnly(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_return_boolean(computer, fs->table.isReadOnly(fs->table.userdata, err)); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } -} - -void nn_fs_size(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_size_t byteSize = fs->table.size(fs->table.userdata, canonical, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return(computer, nn_values_integer(byteSize)); -} - -void nn_fs_remove(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_size_t removed = fs->table.remove(fs->table.userdata, canonical, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_boolean(computer, removed > 0); - - nn_fs_removeCost(fs, removed, component); -} - -void nn_fs_lastModified(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_timestamp_t t = fs->table.lastModified(fs->table.userdata, canonical, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - // OpenOS does BULLSHIT with this thing, dividing it by 1000 and expecting it to be - // fucking usable as a date, meaning it needs to be an int. - // Because of that, we ensure it is divisible by 1000 - t -= t % 1000; - - nn_return(computer, nn_values_integer(t)); -} - -void nn_fs_rename(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value fromValue = nn_getArgument(computer, 0); - const char *from = nn_toCString(fromValue); - if(from == NULL) { - nn_setCError(computer, "bad path #1 (string expected)"); - return; - } - char canonicalFrom[NN_MAX_PATH]; - if(nn_path_canonical(from, canonicalFrom)) { - nn_setCError(computer, "bad path #1 (illegal path)"); - return; - } - - nn_value toValue = nn_getArgument(computer, 0); - const char *to = nn_toCString(toValue); - if(to == NULL) { - nn_setCError(computer, "bad path #2 (string expected)"); - return; - } - char canonicalTo[NN_MAX_PATH]; - if(nn_path_canonical(to, canonicalTo)) { - nn_setCError(computer, "bad path #2 (illegal path)"); - return; - } - - // TODO: validate against cases such as a/b -> a or a -> a/b - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - if(!fs->table.exists(fs->table.userdata, canonicalFrom, err)) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "No such file or directory"); - return; - } - if(fs->table.exists(fs->table.userdata, canonicalTo, err)) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "Destination exists"); - return; - } - nn_size_t movedCount = fs->table.rename(fs->table.userdata, canonicalFrom, canonicalTo, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return(computer, nn_values_boolean(movedCount > 0)); - - nn_fs_removeCost(fs, movedCount, component); - nn_fs_createCost(fs, movedCount, component); -} - -void nn_fs_exists(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_return_boolean(computer, fs->table.exists(fs->table.userdata, canonical, err)); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } -} - -void nn_fs_isDirectory(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_return_boolean(computer, fs->table.isDirectory(fs->table.userdata, canonical, err)); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - } -} - -void nn_fs_makeDirectory(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_return_boolean(computer, fs->table.makeDirectory(fs->table.userdata, canonical, err)); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - } - - nn_fs_createCost(fs, 1, component); -} - -void nn_fs_list(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - - nn_errorbuf_t err = ""; - nn_size_t fileCount = 0; - nn_lock(&fs->ctx, fs->lock); - char **files = fs->table.list(alloc, fs->table.userdata, canonical, &fileCount, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - if(files != NULL) { - for(nn_size_t i = 0; i < fileCount; i++) { - nn_deallocStr(alloc, files[i]); - } - nn_dealloc(alloc, files, sizeof(char *) * fileCount); - } - nn_setError(computer, err); - } - - if(files != NULL) { - // operation succeeded - nn_value arr = nn_values_array(alloc, fileCount); - for(nn_size_t i = 0; i < fileCount; i++) { - nn_values_set(arr, i, nn_values_string(alloc, files[i], nn_strlen(files[i]))); - nn_deallocStr(alloc, files[i]); - } - nn_dealloc(alloc, files, sizeof(char *) * fileCount); - nn_return(computer, arr); - } -} - -void nn_fs_open(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value pathValue = nn_getArgument(computer, 0); - const char *path = nn_toCString(pathValue); - if(path == NULL) { - nn_setCError(computer, "bad path (string expected)"); - return; - } - char canonical[NN_MAX_PATH]; - if(nn_path_canonical(path, canonical)) { - nn_setCError(computer, "bad path (illegal path)"); - return; - } - - nn_value modeValue = nn_getArgument(computer, 1); - const char *mode = nn_toCString(modeValue); - if(mode == NULL) { - mode = "r"; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - // technically wrongfully - if(!fs->table.exists(fs->table.userdata, canonical, err)) { - nn_fs_createCost(fs, 1, component); - } - if(!nn_error_isEmpty(err)) { - nn_unlock(&fs->ctx, fs->lock); - nn_setError(computer, err); - return; - } - - nn_size_t fd = 0; - while(fs->files[fd] != NULL) { - fd++; - if(fd == NN_MAX_OPEN_FILES) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "too many open files"); - return; - } - } - void *file = fs->table.open(fs->table.userdata, canonical, mode, err); - if(!nn_error_isEmpty(err)) { - if(file != NULL) { - fs->table.close(fs->table.userdata, file, err); - } - nn_unlock(&fs->ctx, fs->lock); - nn_setError(computer, err); - return; - } - if(file == NULL) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "no such file or directory"); - return; - } - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - fs->files[fd] = file; - nn_return(computer, nn_values_integer(fd)); -} - -void nn_fs_close(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value fdValue = nn_getArgument(computer, 0); - nn_size_t fd = nn_toInt(fdValue); - void *file = nn_fs_unwrapFD(fs, fd); - if(file == NULL) { - nn_setCError(computer, "bad file descriptor"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_bool_t closed = fs->table.close(fs->table.userdata, file, err); - if(closed) { - fs->files[fd] = NULL; - } - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return(computer, nn_values_boolean(closed)); -} - -void nn_fs_write(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value fdValue = nn_getArgument(computer, 0); - nn_size_t fd = nn_toInt(fdValue); - - nn_value bufferValue = nn_getArgument(computer, 1); - nn_size_t len = 0; - const char *buf = nn_toString(bufferValue, &len); - if(buf == NULL) { - nn_setCError(computer, "bad buffer (string expected)"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - nn_size_t spaceRemaining = nn_fs_getSpaceRemaining(fs); - - // overwriting would still work but OC does the same thing so... - if(spaceRemaining < len) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "out of space"); - return; - } - void *file = nn_fs_unwrapFD(fs, fd); - if(file == NULL) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "bad file descriptor"); - return; - } - - nn_bool_t written = fs->table.write(fs->table.userdata, file, buf, len, err); - nn_return(computer, nn_values_boolean(written)); - if(written) nn_fs_invalidateSpaceUsed(fs); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_fs_writeCost(fs, len, component); -} - -void nn_fs_read(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_value fdValue = nn_getArgument(computer, 0); - int fd = nn_toInt(fdValue); - - nn_value lenValue = nn_getArgument(computer, 1); - double len = nn_toNumber(lenValue); - // TODO: be smarter - nn_size_t capacity = fs->table.spaceTotal; - if(len > capacity) len = capacity; - nn_size_t byteLen = len; - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - void *file = nn_fs_unwrapFD(fs, fd); - if(file == NULL) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "bad file descriptor"); - return; - } - - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - char *buf = nn_alloc(alloc, byteLen); - if(buf == NULL) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "out of memory"); - return; - } - - nn_size_t readLen = fs->table.read(fs->table.userdata, file, buf, byteLen, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - if(readLen > 0) { - // Nothing read means EoF. - nn_return_string(computer, buf, readLen); - } - nn_dealloc(alloc, buf, byteLen); - - nn_fs_readCost(fs, len, component); -} - -nn_bool_t nn_fs_validWhence(const char *s) { - return - nn_strcmp(s, "set") == 0 || - nn_strcmp(s, "cur") == 0 || - nn_strcmp(s, "end") == 0; -} - -void nn_fs_seek(nn_filesystem *fs, nn_componentMethod *_, nn_component *component, nn_computer *computer) { - nn_size_t fd = nn_toInt(nn_getArgument(computer, 0)); - - const char *whence = nn_toCString(nn_getArgument(computer, 1)); - - int off = nn_toInt(nn_getArgument(computer, 2)); - - if(whence == NULL) { - nn_setCError(computer, "bad whence (string expected)"); - return; - } - - if(!nn_fs_validWhence(whence)) { - nn_setCError(computer, "bad whence"); - return; - } - - nn_errorbuf_t err = ""; - nn_lock(&fs->ctx, fs->lock); - void *file = nn_fs_unwrapFD(fs, fd); - if(file == NULL) { - nn_unlock(&fs->ctx, fs->lock); - nn_setCError(computer, "bad file descriptor"); - return; - } - - nn_size_t pos = fs->table.seek(fs->table.userdata, file, whence, off, err); - nn_unlock(&fs->ctx, fs->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_integer(computer, pos); -} - -void nn_loadFilesystemTable(nn_universe *universe) { - nn_componentTable *fsTable = nn_newComponentTable(nn_getAllocator(universe), "filesystem", NULL, NULL, (nn_componentDestructor *)nn_fs_destroy); - nn_storeUserdata(universe, "NN:FILESYSTEM", fsTable); - - nn_defineMethod(fsTable, "getLabel", (nn_componentMethod *)nn_fs_getLabel, "getLabel(): string - Returns the label of the filesystem."); - nn_defineMethod(fsTable, "setLabel", (nn_componentMethod *)nn_fs_setLabel, "setLabel(label: string): string - Sets a new label for the filesystem and returns the new label of the filesystem, which may have been truncated."); - nn_defineMethod(fsTable, "spaceUsed", (nn_componentMethod *)nn_fs_spaceUsed, "spaceUsed(): integer - Returns the amounts of bytes used."); - nn_defineMethod(fsTable, "spaceTotal", (nn_componentMethod *)nn_fs_spaceTotal, "spaceTotal(): integer - Returns the capacity of the filesystem."); - nn_defineMethod(fsTable, "isReadOnly", (nn_componentMethod *)nn_fs_isReadOnly, "isReadOnly(): boolean - Returns whether the filesystem is in read-only mode."); - nn_defineMethod(fsTable, "size", (nn_componentMethod *)nn_fs_size, "size(path: string): integer - Gets the size, in bytes, of a file."); - nn_defineMethod(fsTable, "remove", (nn_componentMethod *)nn_fs_remove, "remove(path: string): boolean - Removes a file. Returns whether the operation succeeded."); - nn_defineMethod(fsTable, "lastModified", (nn_componentMethod *)nn_fs_lastModified, "remove(path: string): boolean - Removes a file. Returns whether the operation succeeded."); - nn_defineMethod(fsTable, "rename", (nn_componentMethod *)nn_fs_rename, "rename(from: string, to: string): boolean - Moves files from one path to another."); - nn_defineMethod(fsTable, "exists", (nn_componentMethod *)nn_fs_exists, "exists(path: string): boolean - Checks whether a file exists."); - nn_defineMethod(fsTable, "isDirectory", (nn_componentMethod *)nn_fs_isDirectory, "isDirectory(path: string): boolean - Returns whether a file is actually a directory."); - nn_defineMethod(fsTable, "makeDirectory", (nn_componentMethod *)nn_fs_makeDirectory, "makeDirectory(path: string): boolean - Creates a new directory at the given path. Returns whether it succeeded."); - nn_defineMethod(fsTable, "list", (nn_componentMethod *)nn_fs_list, "list(path: string): string[] - Returns a list of file paths. Directories will have a / after them"); - nn_defineMethod(fsTable, "open", (nn_componentMethod *)nn_fs_open, "open(path: string[, mode: string = \"r\"]): integer - Opens a file, may create it."); - nn_defineMethod(fsTable, "close", (nn_componentMethod *)nn_fs_close, "close(fd: integer): boolean - Closes a file."); - nn_defineMethod(fsTable, "write", (nn_componentMethod *)nn_fs_write, "write(fd: integer, data: string): boolean - Writes data to a file."); - nn_defineMethod(fsTable, "read", (nn_componentMethod *)nn_fs_read, "read(fd: integer, len: number): string - Reads bytes from a file. Infinity is a valid length, in which case it reads as much as possible."); - nn_defineMethod(fsTable, "seek", (nn_componentMethod *)nn_fs_seek, "seek(fd: integer, whence: string, offset: integer): integer - Seeks a file. Returns the new position. Valid whences are set, cur and end."); -} - -nn_component *nn_addFileSystem(nn_computer *computer, nn_address address, int slot, nn_filesystem *filesystem) { - nn_componentTable *fsTable = nn_queryUserdata(nn_getUniverse(computer), "NN:FILESYSTEM"); - return nn_newComponent(computer, address, slot, fsTable, filesystem); -} diff --git a/src/components/gpu.c b/src/components/gpu.c deleted file mode 100644 index 71c010a..0000000 --- a/src/components/gpu.c +++ /dev/null @@ -1,960 +0,0 @@ -#include "../neonucleus.h" -#include "screen.h" - -typedef struct nni_buffer { - int width; - int height; - nn_scrchr_t *data; -} nni_buffer; - -typedef struct nni_gpu { - nn_Alloc alloc; - nn_screen *currentScreen; - nn_address screenAddress; - nn_gpuControl ctrl; - int currentFg; - int currentBg; - nn_bool_t isFgPalette; - nn_bool_t isBgPalette; - // TODO: think about buffers and stuff - int usedVRAM; - int activeBuffer; - int *vramIDBuf; // pre-allocated memory - nni_buffer **buffers; // array of pointers -} nni_gpu; - -// utils - -nn_scrchr_t nni_gpu_makePixel(nni_gpu *gpu, const char *s) { - return (nn_scrchr_t) { - .codepoint = nn_unicode_codepointAt(s, 0), - .fg = gpu->currentFg, - .bg = gpu->currentBg, - .isFgPalette = gpu->isFgPalette, - .isBgPalette = gpu->isBgPalette, - }; -} - -nn_bool_t nni_samePixel(nn_scrchr_t a, nn_scrchr_t b) { - return - a.codepoint == b.codepoint && - a.fg == b.fg && - a.bg == b.bg && - a.isFgPalette == b.isFgPalette && - a.isBgPalette == b.isBgPalette - ; -} - -nn_bool_t nni_inBounds(nni_gpu *gpu, int x, int y) { - if(gpu->currentScreen == NULL) return false; - return - x >= 0 && - y >= 0 && - x < gpu->currentScreen->width && - y < gpu->currentScreen->height && - true; -} - -nn_size_t nni_vramNeededForSize(int w, int h) { - return w * h; -} - -nn_size_t nni_vramNeededForScreen(nn_screen *screen) { - if(screen == NULL) return 0; - int w, h; - nn_maxResolution(screen, &w, &h); - return nni_vramNeededForSize(w, h); -} - -// VRAM - -nni_buffer *nni_vram_newBuffer(nn_Alloc *alloc, int width, int height) { - int area = width * height; - nni_buffer *buf = nn_alloc(alloc, sizeof(nni_buffer)); - if(buf == NULL) { - return NULL; - } - buf->width = width; - buf->height = height; - buf->data = nn_alloc(alloc, sizeof(nn_scrchr_t) * area); - for(int i = 0; i < area; i++) { - buf->data[i] = (nn_scrchr_t) { - .codepoint = ' ', - .fg = 0xFFFFFF, - .bg = 0x000000, - .isFgPalette = false, - .isBgPalette = false, - }; - } - if(buf->data == NULL) { - nn_dealloc(alloc, buf, sizeof(nni_buffer)); - } - return buf; -} - -void nni_vram_deinit(nn_Alloc *alloc, nni_buffer *buffer) { - int area = buffer->width * buffer->height; - nn_dealloc(alloc, buffer->data, sizeof(nn_scrchr_t) * area); - nn_dealloc(alloc, buffer, sizeof(nni_buffer)); -} - -nn_bool_t nni_vram_inBounds(nni_buffer *buffer, int x, int y) { - return - x >= 0 && - y >= 0 && - x < buffer->width && - y < buffer->height - ; -} - -nn_scrchr_t nni_vram_getPixel(nni_buffer *buffer, int x, int y) { - if(!nni_vram_inBounds(buffer, x, y)) { - return (nn_scrchr_t) { - .codepoint = 0, - .fg = 0xFFFFFF, - .bg = 0x000000, - .isFgPalette = false, - .isBgPalette = false, - }; - } - return buffer->data[x + y * buffer->width]; -} - -void nni_vram_setPixel(nni_buffer *buffer, int x, int y, nn_scrchr_t pixel) { - if(!nni_vram_inBounds(buffer, x, y)) return; - buffer->data[x + y * buffer->width] = pixel; -} - -void nni_vram_set(nni_gpu *gpu, int x, int y, const char *s, nn_bool_t vertical) { - nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1]; - - nn_size_t cur = 0; - while(s[cur]) { - unsigned int cp = nn_unicode_nextCodepointPermissive(s, &cur); - char encoded[NN_MAXIMUM_UNICODE_BUFFER]; - nn_unicode_codepointToChar(encoded, cp, NULL); - nni_vram_setPixel(buffer, x, y, nni_gpu_makePixel(gpu, encoded)); - // peak software - if(vertical) { - y++; - } else { - x++; - } - } -} - -void nni_vram_fill(nni_gpu *gpu, int x, int y, int w, int h, const char *s) { - nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1]; - // DoS mitigation - if(x < 0) x = 0; - if(y < 0) y = 0; - if(w > buffer->width) w = buffer->width - x; - if(h > buffer->height) h = buffer->height - y; - - nn_scrchr_t p = nni_gpu_makePixel(gpu, s); - - for(int py = 0; py < h; py++) { - for(int px = 0; px < w; px++) { - nni_vram_setPixel(buffer, px, py, p); - } - } -} - -void nni_vram_copy(nni_gpu *gpu, int x, int y, int w, int h, int tx, int ty, nn_errorbuf_t err) { - nni_buffer *buffer = gpu->buffers[gpu->activeBuffer - 1]; - // DoS mitigation - if(x < 0) x = 0; - if(y < 0) y = 0; - if(w > buffer->width) w = buffer->width - x; - if(h > buffer->height) h = buffer->height - y; - - nn_size_t tmpBufSize = sizeof(nn_scrchr_t) * w * h; - nn_scrchr_t *tmpBuf = nn_alloc(&gpu->alloc, tmpBufSize); - if(tmpBuf == NULL) { - nn_error_write(err, "out of memory"); - return; - } - - // copy - for(int iy = 0; iy < h; iy++) { - for(int ix = 0; ix < w; ix++) { - tmpBuf[ix + iy * w] = nni_vram_getPixel(buffer, x, y); - } - } - - for(int iy = 0; iy < h; iy++) { - for(int ix = 0; ix < w; ix++) { - nn_scrchr_t p = tmpBuf[ix + iy * w]; - nni_vram_setPixel(buffer, x + ix + tx, y + iy + ty, p); - } - } - - nn_dealloc(&gpu->alloc, tmpBuf, tmpBufSize); -} - -// GPU stuff - -nni_gpu *nni_newGPU(nn_Alloc *alloc, nn_gpuControl *ctrl) { - nni_gpu *gpu = nn_alloc(alloc, sizeof(nni_gpu)); - if(gpu == NULL) return NULL; - gpu->alloc = *alloc; - gpu->currentScreen = NULL; - gpu->screenAddress = NULL; - gpu->ctrl = *ctrl; - gpu->currentFg = 0xFFFFFF; - gpu->currentBg = 0x000000; - gpu->isFgPalette = false; - gpu->isBgPalette = false; - gpu->vramIDBuf = nn_alloc(alloc, sizeof(int) * ctrl->maximumBufferCount); - if(gpu->vramIDBuf == NULL) { - nn_dealloc(alloc, gpu, sizeof(nni_gpu)); - return NULL; - } - // gpu->vramIDBuf can be left uninitialized! Its only tmp storage! - gpu->buffers = nn_alloc(alloc, sizeof(nn_screen *) * ctrl->maximumBufferCount); - if(gpu->buffers == NULL) { - nn_dealloc(alloc, gpu->vramIDBuf, sizeof(int) * ctrl->maximumBufferCount); - nn_dealloc(alloc, gpu, sizeof(nni_gpu)); - return NULL; - } - for(int i = 0; i < ctrl->maximumBufferCount; i++) { - gpu->buffers[i] = NULL; - } - gpu->activeBuffer = 0; - gpu->usedVRAM = 0; - return gpu; -} - -void nni_gpuDeinit(nni_gpu *gpu) { - if(gpu->currentScreen != NULL) { - nn_destroyScreen(gpu->currentScreen); - } - nn_Alloc a = gpu->alloc; - if(gpu->screenAddress != NULL) { - nn_deallocStr(&a, gpu->screenAddress); - } - int maximumBufferCount = gpu->ctrl.maximumBufferCount; - for(int i = 0; i < maximumBufferCount; i++) { - } - nn_dealloc(&a, gpu->vramIDBuf, sizeof(int) * maximumBufferCount); - nn_dealloc(&a, gpu->buffers, sizeof(nn_screen) * maximumBufferCount); - nn_dealloc(&a, gpu, sizeof(nni_gpu)); -} - -nn_bool_t nni_gpu_validActiveScreen(nni_gpu *gpu, int activeBuffer) { - if(activeBuffer < 0 || activeBuffer > gpu->ctrl.maximumBufferCount) return false; - return true; -} - -void nni_gpu_bind(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_value argVal = nn_getArgument(computer, 0); - nn_value resetVal = nn_getArgument(computer, 1); - - const char *addr = nn_toCString(argVal); - if(addr == NULL) { - nn_setCError(computer, "bad argument #1 (address expected)"); - return; - } - nn_bool_t reset = false; - if(resetVal.tag == NN_VALUE_BOOL) reset = nn_toBoolean(resetVal); - - nn_component *c = nn_findComponent(computer, (nn_address)addr); - if(c == NULL) { - nn_setCError(computer, "no such screen"); - return; - } - - nn_componentTable *supportedTable = nn_getScreenTable(nn_getUniverse(computer)); - if(supportedTable != nn_getComponentTable(c)) { - nn_setCError(computer, "incompatible screen"); - return; - } - - nn_screen *oldScreen = gpu->currentScreen; - nn_size_t oldScreenVRAM = nni_vramNeededForScreen(oldScreen); - nn_screen *screen = nn_getComponentUserdata(c); - nn_size_t screenVRAM = nni_vramNeededForScreen(screen); - - if(gpu->usedVRAM - oldScreenVRAM + screenVRAM > gpu->ctrl.totalVRAM) { - nn_setCError(computer, "out of vram"); - return; - } - - gpu->usedVRAM -= oldScreenVRAM; - gpu->usedVRAM += screenVRAM; - - nn_retainScreen(screen); - if(oldScreen != NULL) nn_destroyScreen(oldScreen); - gpu->currentScreen = screen; - - if(reset) { - for(nn_size_t i = 0; i < screen->width; i++) { - for(nn_size_t j = 0; j < screen->height; j++) { - nn_setPixel(screen, i, j, nni_gpu_makePixel(gpu, " ")); - } - } - nn_size_t area = screen->width * screen->height; - nn_addHeat(computer, gpu->ctrl.heatPerPixelReset * area); - nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenFillPerTick); - nn_removeEnergy(computer, gpu->ctrl.energyPerPixelReset * area); - } - - gpu->currentScreen = screen; - if(gpu->screenAddress != NULL) { - nn_deallocStr(&gpu->alloc, gpu->screenAddress); - } - // TODO: fix OOM here - gpu->screenAddress = nn_strdup(&gpu->alloc, addr); - - nn_return(computer, nn_values_boolean(true)); -} - -void nni_gpu_set(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int x = nn_toInt(nn_getArgument(computer, 0)) - 1; - int y = nn_toInt(nn_getArgument(computer, 1)) - 1; - const char *s = nn_toCString(nn_getArgument(computer, 2)); - nn_bool_t isVertical = nn_toBoolean(nn_getArgument(computer, 3)); - - if(s == NULL) { - nn_setCError(computer, "bad argument #3 (string expected in set)"); - return; - } - - if(gpu->activeBuffer != 0) { - nni_vram_set(gpu, x, y, s, isVertical); - return; - } - - if(gpu->currentScreen == NULL) return; - - nn_size_t current = 0; - while(s[current] != 0) { - unsigned int codepoint = nn_unicode_nextCodepointPermissive(s, ¤t); - char buf[NN_MAXIMUM_UNICODE_BUFFER]; - nn_unicode_codepointToChar(buf, codepoint, NULL); - nn_setPixel(gpu->currentScreen, x, y, nni_gpu_makePixel(gpu, buf)); - if(isVertical) { - y++; - } else { - x++; - } - } - - nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenSetsPerTick); -} - -void nni_gpu_get(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->screenAddress == NULL) return; - int x = nn_toInt(nn_getArgument(computer, 0)) - 1; - int y = nn_toInt(nn_getArgument(computer, 1)) - 1; - nn_scrchr_t pxl = nn_getPixel(gpu->currentScreen, x, y); - - nn_size_t l; - char chr[NN_MAXIMUM_UNICODE_BUFFER]; - nn_unicode_codepointToChar(chr, pxl.codepoint, &l); - - // TODO: gosh darn palettes - nn_return_string(computer, chr, l); - nn_return_integer(computer, pxl.fg); - nn_return_integer(computer, pxl.bg); -} - -void nni_gpu_getScreen(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->screenAddress == NULL) return; - nn_return_string(computer, gpu->screenAddress, nn_strlen(gpu->screenAddress)); -} - -void nni_gpu_maxResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - int w, h; - nn_maxResolution(gpu->currentScreen, &w, &h); - nn_return(computer, nn_values_integer(w)); - nn_return(computer, nn_values_integer(h)); -} - -void nni_gpu_getResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - int w, h; - nn_getResolution(gpu->currentScreen, &w, &h); - nn_return(computer, nn_values_integer(w)); - nn_return(computer, nn_values_integer(h)); -} - -void nni_gpu_setResolution(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - int mw, mh; - nn_maxResolution(gpu->currentScreen, &mw, &mh); - - int lw, lh; - nn_getResolution(gpu->currentScreen, &lw, &lh); - - int w = nn_toInt(nn_getArgument(computer, 0)); - int h = nn_toInt(nn_getArgument(computer, 1)); - - nn_bool_t changed = w != lw || h != lh; - - if(w <= 0) w = 1; - if(h <= 0) h = 1; - if(w > mw) w = mw; - if(h > mh) h = mh; - nn_setResolution(gpu->currentScreen, w, h); - nn_setViewport(gpu->currentScreen, w, h); - - nn_return(computer, nn_values_boolean(changed)); - - nn_value signalShit[] = { - nn_values_cstring("screen_resized"), - nn_values_cstring(gpu->screenAddress), - nn_values_integer(w), - nn_values_integer(h), - }; - nn_pushSignal(computer, signalShit, 4); -} - -void nni_gpu_setBackground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - int color = nn_toInt(nn_getArgument(computer, 0)); - nn_bool_t isPalette = nn_toBoolean(nn_getArgument(computer, 1)); - - if(isPalette && (color < 0 || color >= gpu->currentScreen->paletteColors)) { - nn_setCError(computer, "invalid palette index"); - return; - } - - int old = gpu->currentBg; - int idx = -1; - if(gpu->isBgPalette) { - idx = old; - old = gpu->currentScreen->palette[old]; - } - - gpu->currentBg = color; - gpu->isBgPalette = isPalette; - - nn_return(computer, nn_values_integer(old)); - if(idx != -1) { - nn_return(computer, nn_values_integer(idx)); - } -} - -void nni_gpu_getBackground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return(computer, nn_values_integer(gpu->currentBg)); -} - -void nni_gpu_setForeground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - int color = nn_toInt(nn_getArgument(computer, 0)); - nn_bool_t isPalette = nn_toBoolean(nn_getArgument(computer, 1)); - - if(isPalette && (color < 0 || color >= gpu->currentScreen->paletteColors)) { - nn_setCError(computer, "invalid palette index"); - return; - } - - int old = gpu->currentFg; - int idx = -1; - if(gpu->isFgPalette) { - idx = old; - old = gpu->currentScreen->palette[old]; - } - - gpu->currentFg = color; - gpu->isFgPalette = isPalette; - - nn_return(computer, nn_values_integer(old)); - if(idx != -1) { - nn_return(computer, nn_values_integer(idx)); - } -} - -void nni_gpu_getForeground(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return(computer, nn_values_integer(gpu->currentFg)); -} - -void nni_gpu_fill(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int x = nn_toInt(nn_getArgument(computer, 0)) - 1; - int y = nn_toInt(nn_getArgument(computer, 1)) - 1; - int w = nn_toInt(nn_getArgument(computer, 2)); - int h = nn_toInt(nn_getArgument(computer, 3)); - const char *s = nn_toCString(nn_getArgument(computer, 4)); - if(s == NULL) { - nn_setCError(computer, "bad argument #5 (character expected)"); - return; - } - - if(gpu->activeBuffer != 0) { - nni_vram_fill(gpu, x, y, w, h, s); - return; - } - - if(gpu->currentScreen == NULL) return; - - nn_size_t startIdx = 0; - int codepoint = nn_unicode_nextCodepointPermissive(s, &startIdx); - - // prevent DoS - if(x < 0) x = 0; - if(y < 0) y = 0; - if(w > gpu->currentScreen->width - x) w = gpu->currentScreen->width - x; - if(h > gpu->currentScreen->height - y) h = gpu->currentScreen->height - y; - - int changes = 0, clears = 0; - - nn_scrchr_t new = nni_gpu_makePixel(gpu, s); - - for(int cx = x; cx < x + w; cx++) { - for(int cy = y; cy < y + h; cy++) { - nn_scrchr_t old = nn_getPixel(gpu->currentScreen, cx, cy); - if(!nni_samePixel(old, new)) { - nn_setPixel(gpu->currentScreen, cx, cy, new); - if(codepoint == ' ') - clears++; - else changes++; - } - } - } - - nn_addHeat(computer, gpu->ctrl.heatPerPixelChange * changes); - nn_removeEnergy(computer, gpu->ctrl.energyPerPixelChange * changes); - - nn_addHeat(computer, gpu->ctrl.heatPerPixelReset * clears); - nn_removeEnergy(computer, gpu->ctrl.energyPerPixelReset * clears); - - nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenFillPerTick); - - nn_return(computer, nn_values_boolean(true)); -} - -void nni_gpu_copy(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int x = nn_toInt(nn_getArgument(computer, 0)) - 1; - int y = nn_toInt(nn_getArgument(computer, 1)) - 1; - int w = nn_toInt(nn_getArgument(computer, 2)); - int h = nn_toInt(nn_getArgument(computer, 3)); - int tx = nn_toInt(nn_getArgument(computer, 4)); - int ty = nn_toInt(nn_getArgument(computer, 5)); - - if(gpu->activeBuffer != 0) { - nn_errorbuf_t err = ""; - nni_vram_copy(gpu, x, y, w, h, tx, ty, err); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - } - return; - } - - if(gpu->currentScreen == NULL) return; - - // prevent DoS - if(x < 0) x = 0; - if(y < 0) y = 0; - if(w > gpu->currentScreen->width) w = gpu->currentScreen->width; - if(h > gpu->currentScreen->height) y = gpu->currentScreen->height; - - int changes = 0, clears = 0; - - nn_scrchr_t *tmpBuffer = nn_alloc(&gpu->alloc, sizeof(nn_scrchr_t) * w * h); - if(tmpBuffer == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - - for(int cx = x; cx < x + w; cx++) { - for(int cy = y; cy < y + h; cy++) { - int ox = cx - x; - int oy = cy - y; - nn_scrchr_t src = nn_getPixel(gpu->currentScreen, cx, cy); - nn_scrchr_t old = nn_getPixel(gpu->currentScreen, cx + tx, cy + ty); - tmpBuffer[ox + oy * w] = src; - if(!nni_samePixel(old, src)) { - if(src.codepoint == ' ') - clears++; - else changes++; - } - } - } - - for(int ox = 0; ox < w; ox++) { - for(int oy = 0; oy < h; oy++) { - nn_scrchr_t p = tmpBuffer[ox + oy * w]; - nn_setPixel(gpu->currentScreen, ox + x + tx, oy + y + ty, p); - } - } - - nn_dealloc(&gpu->alloc, tmpBuffer, sizeof(nn_scrchr_t) * w * h); - - nn_addHeat(computer, gpu->ctrl.heatPerPixelChange * changes); - nn_removeEnergy(computer, gpu->ctrl.energyPerPixelChange * changes); - - nn_addHeat(computer, gpu->ctrl.heatPerPixelReset * clears); - nn_removeEnergy(computer, gpu->ctrl.energyPerPixelReset * clears); - - nn_simulateBufferedIndirect(component, 1, gpu->ctrl.screenCopyPerTick); - - nn_return(computer, nn_values_boolean(true)); -} - -void nni_gpu_getViewport(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int w, h; - nn_getViewport(gpu->currentScreen, &w, &h); - nn_return(computer, nn_values_integer(w)); - nn_return(computer, nn_values_integer(h)); -} -void nni_gpu_getDepth(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - nn_return(computer, nn_values_integer(gpu->currentScreen->depth)); -} - -void nni_gpu_setDepth(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - int depth = nn_toInt(nn_getArgument(computer, 0)); - int maxDepth = nn_maxDepth(gpu->currentScreen); - - if(nn_depthName(depth) == NULL) { - nn_setCError(computer, "invalid depth"); - return; - } - - if(depth > maxDepth) { - nn_setCError(computer, "depth out of range"); - return; - } - - int old = nn_getDepth(gpu->currentScreen); - nn_setDepth(gpu->currentScreen, depth); - - nn_return_cstring(computer, nn_depthName(old)); -} - -void nni_gpu_maxDepth(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - if(gpu->currentScreen == NULL) return; - nn_return(computer, nn_values_integer(gpu->currentScreen->maxDepth)); -} - -void nni_gpu_totalMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, gpu->ctrl.totalVRAM); -} - -void nni_gpu_usedMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, gpu->usedVRAM); -} - -void nni_gpu_freeMemory(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, gpu->ctrl.totalVRAM - gpu->usedVRAM); -} - -void nni_gpu_getActiveBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, gpu->activeBuffer); -} - -void nni_gpu_setActiveBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int buf = nn_toInt(nn_getArgument(computer, 0)); - if(!nni_gpu_validActiveScreen(gpu, buf)) { - nn_setCError(computer, "invalid buffer"); - return; - } - - gpu->activeBuffer = buf; - nn_return_integer(computer, buf); -} - -void nni_gpu_buffers(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int bufCount = 0; - for(int i = 0; i < gpu->ctrl.maximumBufferCount; i++) { - if(gpu->buffers[i] != NULL) { - gpu->vramIDBuf[bufCount] = i + 1; - bufCount++; - } - } - - nn_value arr = nn_return_array(computer, bufCount); - for(int i = 0; i < bufCount; i++) { - nn_values_set(arr, i, nn_values_integer(gpu->vramIDBuf[i])); - } -} - -void nni_gpu_allocateBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int width = gpu->ctrl.defaultBufferWidth; - int height = gpu->ctrl.defaultBufferHeight; - - nn_value widthVal = nn_getArgument(computer, 0); - nn_value heightVal = nn_getArgument(computer, 1); - - if(widthVal.tag != NN_VALUE_NIL) { - width = nn_toInt(widthVal); - } - if(heightVal.tag != NN_VALUE_NIL) { - height = nn_toInt(heightVal); - } - - if(width < 0 || height < 0) { - nn_setCError(computer, "invalid size"); - return; - } - - nn_size_t vramNeeded = nni_vramNeededForSize(width, height); - - if(gpu->usedVRAM + vramNeeded > gpu->ctrl.totalVRAM) { - nn_setCError(computer, "out of vram"); - return; - } - - nn_size_t idx = 0; - for(nn_size_t i = 0; i < gpu->ctrl.maximumBufferCount; i++) { - if(gpu->buffers[i] == NULL) { - idx = i + 1; - break; - } - } - - if(idx == 0) { - nn_setCError(computer, "too many buffers"); - return; - } - - nni_buffer *buf = nni_vram_newBuffer(&gpu->alloc, width, height); - if(buf == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - gpu->buffers[idx - 1] = buf; - - gpu->usedVRAM += vramNeeded; - - nn_return_integer(computer, idx); -} - -void nni_gpu_freeBuffer(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int bufidx = gpu->activeBuffer; - nn_value bufVal = nn_getArgument(computer, 0); - if(bufVal.tag != NN_VALUE_NIL) { - bufidx = nn_toInt(bufVal); - } - - if(!nni_gpu_validActiveScreen(gpu, bufidx) || bufidx == 0) { - nn_setCError(computer, "invalid buffer"); - return; - } - - nni_buffer *buf = gpu->buffers[bufidx - 1]; - if(buf == NULL) { - nn_setCError(computer, "invalid buffer"); - return; - } - - int vramUsed = buf->width * buf->height; - nni_vram_deinit(&gpu->alloc, buf); - gpu->buffers[bufidx - 1] = NULL; - - if(bufidx == gpu->activeBuffer) gpu->activeBuffer = 0; - gpu->usedVRAM -= vramUsed; -} - -void nni_gpu_freeAllBuffers(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - gpu->activeBuffer = 0; - - for(nn_size_t i = 0; i < gpu->ctrl.maximumBufferCount; i++) { - if(gpu->buffers[i] != NULL) { - int vramUsed = gpu->buffers[i]->width * gpu->buffers[i]->height; - nni_vram_deinit(&gpu->alloc, gpu->buffers[i]); - gpu->buffers[i] = NULL; - gpu->usedVRAM -= vramUsed; - } - } -} - -void nni_gpu_getBufferSize(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - int bufidx = gpu->activeBuffer; - nn_value bufVal = nn_getArgument(computer, 0); - if(bufVal.tag != NN_VALUE_NIL) { - bufidx = nn_toInt(bufVal); - } - - if(!nni_gpu_validActiveScreen(gpu, bufidx)) { - nn_setCError(computer, "invalid buffer"); - return; - } - - if(bufidx == 0) { - if(gpu->currentScreen == NULL) { - nn_setCError(computer, "invalid buffer"); - return; - } - int w, h; - nn_getResolution(gpu->currentScreen, &w, &h); - nn_return_integer(computer, w); - nn_return_integer(computer, h); - return; - } - - nni_buffer *buf = gpu->buffers[bufidx - 1]; - if(buf == NULL) { - nn_setCError(computer, "invalid buffer"); - return; - } - - nn_return_integer(computer, buf->width); - nn_return_integer(computer, buf->height); -} - -void nni_gpu_bitblt(nni_gpu *gpu, void *_, nn_component *component, nn_computer *computer) { - // I will kill OC creators for this - int dst = nn_toIntOr(nn_getArgument(computer, 0), 0); - int x = nn_toIntOr(nn_getArgument(computer, 1), 1); - int y = nn_toIntOr(nn_getArgument(computer, 2), 1); - int width = nn_toIntOr(nn_getArgument(computer, 3), 0); - int height = nn_toIntOr(nn_getArgument(computer, 4), 0); - int src = nn_toIntOr(nn_getArgument(computer, 5), gpu->activeBuffer); - int fromCol = nn_toIntOr(nn_getArgument(computer, 6), 1); - int fromRow = nn_toIntOr(nn_getArgument(computer, 7), 1); - - if(x < 1) x = 1; - if(y < 1) y = 1; - if(fromCol < 1) fromCol = 1; - if(fromRow < 1) fromRow = 1; - - if(!nni_gpu_validActiveScreen(gpu, dst)) { - nn_setCError(computer, "invalid destination buffer"); - return; - } - - if(!nni_gpu_validActiveScreen(gpu, src)) { - nn_setCError(computer, "invalid source buffer"); - return; - } - - // such great parsing - if(dst == 0) { - if(gpu->currentScreen == NULL) return; - int w, h; - nn_getResolution(gpu->currentScreen, &w, &h); - width = width == 0 ? w : width; - height = height == 0 ? h : height; - } else { - nni_buffer *buffer = gpu->buffers[dst - 1]; - if(buffer == NULL) return; - width = width == 0 ? buffer->width : width; - height = height == 0 ? buffer->height : height; - } - - if(dst == src) { - nn_setCError(computer, "invalid operation, use copy() instead"); - return; - } - - // from buffer to screen - if(dst == 0 && src != 0) { - nn_screen *screen = gpu->currentScreen; - nni_buffer *buf = gpu->buffers[src - 1]; - if(buf == NULL) { - nn_setCError(computer, "invalid source buffer"); - return; - } - - int w, h; - nn_getResolution(gpu->currentScreen, &w, &h); - - if(width > w) width = w; - if(height > h) height = h; - - for(int j = 0; j < height; j++) { - for(int i = 0; i < width; i++) { - nn_scrchr_t src = nni_vram_getPixel(buf, i + fromCol - 1, j + fromRow - 1); - nn_setPixel(screen, i + x - 1, j + y - 1, src); - } - } - return; - } - // from screen to buffer - if(dst != 0 && src == 0) { - nn_screen *screen = gpu->currentScreen; - nni_buffer *buf = gpu->buffers[dst - 1]; - if(buf == NULL) { - nn_setCError(computer, "invalid destination buffer"); - return; - } - - if(width > buf->width) width = buf->width; - if(height > buf->height) height = buf->height; - - for(int j = 0; j < height; j++) { - for(int i = 0; i < width; i++) { - nn_scrchr_t src = nn_getPixel(screen, i + fromCol - 1, j + fromRow - 1); - nni_vram_setPixel(buf, i + x - 1, j + y - 1, src); - } - } - return; - } - // from buffer to buffer - if(dst != 0 && src != 0) { - nni_buffer *srcBuf = gpu->buffers[src - 1]; - if(srcBuf == NULL) { - nn_setCError(computer, "invalid destination buffer"); - return; - } - nni_buffer *destBuf = gpu->buffers[dst - 1]; - if(destBuf == NULL) { - nn_setCError(computer, "invalid destination buffer"); - return; - } - - if(width > destBuf->width) width = destBuf->width; - if(height > destBuf->height) height = destBuf->height; - - for(int j = 0; j < height; j++) { - for(int i = 0; i < width; i++) { - nn_scrchr_t src = nni_vram_getPixel(srcBuf, i + fromCol - 1, j + fromRow - 1); - nni_vram_setPixel(destBuf, i + x - 1, j + y - 1, src); - } - } - return; - } -} - -void nn_loadGraphicsCardTable(nn_universe *universe) { - nn_componentTable *gpuTable = nn_newComponentTable(nn_getAllocator(universe), "gpu", NULL, NULL, (nn_componentDestructor *)nni_gpuDeinit); - nn_storeUserdata(universe, "NN:GPU", gpuTable); - - nn_method_t *method = NULL; - - method = nn_defineMethod(gpuTable, "bind", (nn_componentMethod *)nni_gpu_bind, "bind(addr: string[, reset: boolean = false]): boolean - Bind a GPU to a screen. Very expensive. If reset is true, it will clear the screen."); - nn_method_setDirect(method, false); - - nn_defineMethod(gpuTable, "getScreen", (nn_componentMethod *)nni_gpu_getScreen, "getScreen(): string"); - nn_defineMethod(gpuTable, "set", (nn_componentMethod *)nni_gpu_set, "set(x: integer, y: integer, text: string[, vertical: boolean = false]) - Modifies the screen at a specific x or y. If vertical is false, it will display it horizontally. If it is true, it will display it vertically."); - nn_defineMethod(gpuTable, "get", (nn_componentMethod *)nni_gpu_get, "get(x: integer, y: integer): string, integer, integer, integer?, integer? - Returns the character, foreground color, background color, foreground palette index (if applicable), background palette index (if applicable) of a pixel"); - nn_defineMethod(gpuTable, "maxResolution", (nn_componentMethod *)nni_gpu_maxResolution, "maxResolution(): integer, integer - Gets the maximum resolution supported by the bound screen."); - nn_defineMethod(gpuTable, "getResolution", (nn_componentMethod *)nni_gpu_getResolution, "getResolution(): integer, integer - Gets the current resolution of the bound screen."); - nn_defineMethod(gpuTable, "setResolution", (nn_componentMethod *)nni_gpu_setResolution, "maxResolution(): integer, integer - Changes the resolution of the bound screen."); - nn_defineMethod(gpuTable, "setBackground", (nn_componentMethod *)nni_gpu_setBackground, "setBackground(color: integer, isPalette: boolean): integer, integer? - Sets the current background color. Returns the old one and palette index if applicable."); - nn_defineMethod(gpuTable, "setForeground", (nn_componentMethod *)nni_gpu_setForeground, "setForeground(color: integer, isPalette: boolean): integer, integer? - Sets the current foreground color. Returns the old one and palette index if applicable."); - nn_defineMethod(gpuTable, "getBackground", (nn_componentMethod *)nni_gpu_getBackground, "setBackground(color: integer, isPalette: boolean): integer, integer? - Sets the current background color. Returns the old one and palette index if applicable."); - nn_defineMethod(gpuTable, "getForeground", (nn_componentMethod *)nni_gpu_getForeground, "setForeground(color: integer, isPalette: boolean): integer, integer? - Sets the current foreground color. Returns the old one and palette index if applicable."); - nn_defineMethod(gpuTable, "getDepth", (nn_componentMethod *)nni_gpu_getDepth, "getDepth(): number - The currently set color depth of the screen, in bits. Can be 1, 4 or 8."); - nn_defineMethod(gpuTable, "setDepth", (nn_componentMethod *)nni_gpu_setDepth, "setDepth(depth: integer): string - Changes the screen depth. Valid values can be 1, 4, 8, 16 or 24, however check maxDepth for the maximum supported value of the screen. Using a depth higher than what is supported by the screen will error. Returns the name of the new depth."); - nn_defineMethod(gpuTable, "maxDepth", (nn_componentMethod *)nni_gpu_maxDepth, "maxDepth(): number - The maximum supported depth of the screen."); - nn_defineMethod(gpuTable, "fill", (nn_componentMethod *)nni_gpu_fill, "fill(x: integer, y: integer, w: integer, h: integer, s: string)"); - nn_defineMethod(gpuTable, "copy", (nn_componentMethod *)nni_gpu_copy, "copy(x: integer, y: integer, w: integer, h: integer, tx: integer, ty: integer) - Copies stuff"); - nn_defineMethod(gpuTable, "getViewport", (nn_componentMethod *)nni_gpu_getViewport, "getViewport(): integer, integer - Gets the current viewport resolution"); - - // VRAM buffers - nn_defineMethod(gpuTable, "totalMemory", (nn_componentMethod *)nni_gpu_totalMemory, "totalMemory(): integer - Returns the VRAM capacity of the card"); - nn_defineMethod(gpuTable, "usedMemory", (nn_componentMethod *)nni_gpu_usedMemory, "usedMemory(): integer - Returns the amount of used VRAM"); - nn_defineMethod(gpuTable, "freeMemory", (nn_componentMethod *)nni_gpu_freeMemory, "freeMemory(): integer - Returns the amount of unused VRAM"); - nn_defineMethod(gpuTable, "buffers", (nn_componentMethod *)nni_gpu_buffers, "buffers(): integer[] - Returns the VRAM buffers allocated (not including the screen)"); - nn_defineMethod(gpuTable, "setActiveBuffer", (nn_componentMethod *)nni_gpu_setActiveBuffer, "setActiveBuffer(buffer: integer): integer - Changes the current buffer"); - nn_defineMethod(gpuTable, "getActiveBuffer", (nn_componentMethod *)nni_gpu_getActiveBuffer, "getActiveBuffer(): integer - Returns the current buffer"); - nn_defineMethod(gpuTable, "allocateBuffer", (nn_componentMethod *)nni_gpu_allocateBuffer, "allocateBuffer([width: integer, height: integer]): integer - Allocates a new VRAM buffer. Default size depends on GPU."); - nn_defineMethod(gpuTable, "freeBuffer", (nn_componentMethod *)nni_gpu_freeBuffer, "freeBuffer([buffer: integer]): boolean - Frees a buffer. By default, the current buffer. If the current buffer is freed, it will switch back to the screen."); - nn_defineMethod(gpuTable, "freeAllBuffers", (nn_componentMethod *)nni_gpu_freeAllBuffers, "freeAllBuffers() - Frees every VRAM buffer (if any). Also switches back to the screen."); - nn_defineMethod(gpuTable, "getBufferSize", (nn_componentMethod *)nni_gpu_getBufferSize, "getBufferSize(buffer: integer): integer, integer - Returns the size of the specified buffer."); - nn_defineMethod(gpuTable, "bitblt", (nn_componentMethod *)nni_gpu_bitblt, "bitblt([dst: integer, x: integer, y: integer, w: integer, h: integer, src: integer, fromCol: integer, fromRow: integer]) - Copy regions between buffers"); -} - -nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control) { - nn_componentTable *gpuTable = nn_queryUserdata(nn_getUniverse(computer), "NN:GPU"); - nni_gpu *gpu = nni_newGPU(nn_getAllocator(nn_getUniverse(computer)), control); - if(gpu == NULL) { - return NULL; - } - return nn_newComponent(computer, address, slot, gpuTable, gpu); -} diff --git a/src/components/hologram.c b/src/components/hologram.c deleted file mode 100644 index 65f087b..0000000 --- a/src/components/hologram.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "hologram.h" - -// safety: -// For valid indexes to be valid, -// stuff must be from 0 to limit - 1 -nn_size_t nn_positionToIndex(nn_hologram *h, unsigned x, unsigned y, unsigned z) { - return x + y * h->width_x + z * h->width_x * h->height; -} - -void nn_hologram_clear(nn_hologram *h) { - -} - diff --git a/src/components/hologram.h b/src/components/hologram.h deleted file mode 100644 index d6450ed..0000000 --- a/src/components/hologram.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef NN_HOLOGRAM_H -#define NN_HOLOGRAM_H - -#include "../neonucleus.h" - -typedef struct nn_hologram { - nn_Context ctx; - nn_guard *lock; - nn_refc refc; - - int pallette_len; - int* pallette_array; - - int width_x; - int width_z; - int height; - - float minScale; - float maxScale; - float scale; - int depth; - - float min_translationX; - float max_translationX; - float translationX; - - float min_translationY; - float max_translationY; - float translationY; - - float min_translationZ; - float max_translationZ; - float translationZ; - - int* grid; // I don't know what to call this -} nn_hologram; - -#endif \ No newline at end of file diff --git a/src/components/keyboard.c b/src/components/keyboard.c deleted file mode 100644 index bbb300e..0000000 --- a/src/components/keyboard.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "../neonucleus.h" - -void nn_loadKeyboardTable(nn_universe *universe) { - nn_componentTable *keyboardTable = nn_newComponentTable(nn_getAllocator(universe), "keyboard", NULL, NULL, NULL); - nn_storeUserdata(universe, "NN:KEYBOARD", keyboardTable); -} - -nn_component *nn_mountKeyboard(nn_computer *computer, nn_address address, int slot) { - nn_componentTable *keyboardTable = nn_queryUserdata(nn_getUniverse(computer), "NN:KEYBOARD"); - return nn_newComponent(computer, address, slot, keyboardTable, NULL); -} diff --git a/src/components/loopbackModem.c b/src/components/loopbackModem.c deleted file mode 100644 index 7f89f74..0000000 --- a/src/components/loopbackModem.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_modemLoop { - nn_Context ctx; - nn_debugLoopbackNetworkOpts opts; - nn_size_t *openPorts; - nn_size_t strength; - char wakeup[NN_MAX_WAKEUPMSG]; - nn_size_t wakeupLen; -} nn_modemLoop; - -void nn_loopModem_deinit(nn_modemLoop *loop) { - nn_Context ctx = loop->ctx; - - nn_deallocStr(&ctx.allocator, loop->opts.address); - nn_dealloc(&ctx.allocator, loop->openPorts, loop->opts.maxOpenPorts * sizeof(nn_size_t)); - nn_dealloc(&ctx.allocator, loop, sizeof(nn_modemLoop)); -} - -nn_bool_t nn_loopModem_isOpen(nn_modemLoop *loop, nn_size_t port, nn_errorbuf_t err) { - for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) { - if(loop->openPorts[i] == port) { - return true; - } - } - - return false; -} - -nn_bool_t nn_loopModem_open(nn_modemLoop *loop, nn_size_t port, nn_errorbuf_t err) { - int slot = -1; - for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) { - if(loop->openPorts[i] == NN_PORT_CLOSEALL) { - slot = i; - break; - } - } - - if(slot == -1) { - nn_error_write(err, "too many open ports"); - return false; - } - - loop->openPorts[slot] = port; - - return true; -} - -nn_bool_t nn_loopModem_close(nn_modemLoop *loop, nn_size_t port, nn_errorbuf_t err) { - if(port == NN_PORT_CLOSEALL) { - for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) loop->openPorts[i] = NN_PORT_CLOSEALL; - return true; - } - - for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) { - if(loop->openPorts[i] == port) { - loop->openPorts[i] = NN_PORT_CLOSEALL; - return true; - } - } - - nn_error_write(err, "port already closed"); - return false; -} - -nn_size_t nn_loopModem_getPorts(nn_modemLoop *loop, nn_size_t *ports, nn_errorbuf_t err) { - nn_size_t len = 0; - for(nn_size_t i = 0; i < loop->opts.maxOpenPorts; i++) { - if(loop->openPorts[i] != NN_PORT_CLOSEALL) { - ports[len] = loop->openPorts[i]; - len++; - } - } - return len; -} - -nn_bool_t nn_loopModem_send(nn_modemLoop *loop, nn_address address, nn_size_t port, nn_value *values, nn_size_t valuec, nn_errorbuf_t err) { - if(address == NULL) { - // broadcasting, set it to our address - address = loop->opts.address; - } - - // error is discarded as packet loss - nn_pushNetworkMessage(loop->opts.computer, loop->opts.address, address, port, loop->strength, values, valuec); - - return true; -} - -double nn_loopModem_getStrength(nn_modemLoop *loop, nn_errorbuf_t err) { - return loop->strength; -} - -double nn_loopModem_setStrength(nn_modemLoop *loop, double n, nn_errorbuf_t err) { - loop->strength = n; - return n; -} - -nn_size_t nn_loopModem_getWakeMessage(nn_modemLoop *loop, char *msg, nn_errorbuf_t err) { - nn_memcpy(msg, loop->wakeup, loop->wakeupLen); - return loop->wakeupLen; -} - -nn_size_t nn_loopModem_setWakeMessage(nn_modemLoop *loop, const char *msg, nn_size_t msglen, nn_bool_t fuzzy, nn_errorbuf_t err) { - loop->wakeupLen = msglen; - nn_memcpy(loop->wakeup, msg, loop->wakeupLen); - return loop->wakeupLen; -} - -nn_modem *nn_debugLoopbackModem(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control) { - opts.address = nn_strdup(&context->allocator, opts.address); - nn_modemLoop *m = nn_alloc(&context->allocator, sizeof(nn_modemLoop)); - m->ctx = *context; - m->opts = opts; - m->strength = opts.maxStrength; - m->wakeupLen = 0; - m->openPorts = nn_alloc(&context->allocator, opts.maxOpenPorts * sizeof(nn_size_t)); - for(nn_size_t i = 0; i < opts.maxOpenPorts; i++) { - m->openPorts[i] = NN_PORT_CLOSEALL; // used as a NULL port - } - nn_modemTable table = { - .userdata = m, - .deinit = (void *)nn_loopModem_deinit, - - .wireless = opts.isWireless, - .maxValues = opts.maxValues, - .maxOpenPorts = opts.maxOpenPorts, - .maxPacketSize = opts.maxPacketSize, - - .isOpen = (void *)nn_loopModem_isOpen, - .open = (void *)nn_loopModem_open, - .close = (void *)nn_loopModem_close, - .getPorts = (void *)nn_loopModem_getPorts, - - .send = (void *)nn_loopModem_send, - - .maxStrength = opts.maxStrength, - .getStrength = (void *)nn_loopModem_getStrength, - .setStrength = (void *)nn_loopModem_setStrength, - - .setWakeMessage = (void *)nn_loopModem_setWakeMessage, - .getWakeMessage = (void *)nn_loopModem_getWakeMessage, - }; - return nn_newModem(context, table, control); -} diff --git a/src/components/loopbackTunnel.c b/src/components/loopbackTunnel.c deleted file mode 100644 index df2f26a..0000000 --- a/src/components/loopbackTunnel.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_loopTunnel_t { - nn_Context ctx; - nn_debugLoopbackNetworkOpts opts; - char wakeup[NN_MAX_WAKEUPMSG]; - nn_size_t wakeupLen; -} nn_loopTunnel_t; - -static void nni_debugTunnel_deinit(nn_loopTunnel_t *t) { - nn_Alloc a = t->ctx.allocator; - nn_deallocStr(&a, t->opts.address); - nn_dealloc(&a, t, sizeof(nn_loopTunnel_t)); -} - -static nn_size_t nni_debugTunnel_getChannel(nn_loopTunnel_t *t, char *buf, nn_errorbuf_t err) { - nn_strcpy(buf, "loopback"); - return 8; -} - -static nn_size_t nni_debugTunnel_getWakeMessage(nn_loopTunnel_t *t, char *buf, nn_errorbuf_t err) { - nn_memcpy(buf, t->wakeup, t->wakeupLen); - return t->wakeupLen; -} - -static nn_size_t nni_debugTunnel_setWakeMessage(nn_loopTunnel_t *t, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err) { - if(buflen > NN_MAX_CHANNEL_SIZE) buflen = NN_MAX_CHANNEL_SIZE; - nn_memcpy(t->wakeup, buf, buflen); - t->wakeupLen = buflen; - return buflen; -} - -static void nni_debugTunnel_send(nn_loopTunnel_t *t, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err) { - nn_pushNetworkMessage(t->opts.computer, t->opts.address, "loopback", NN_TUNNEL_PORT, 0, values, valueCount); -} - -nn_tunnel *nn_debugLoopbackTunnel(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control) { - nn_Alloc *alloc = &context->allocator; - - nn_loopTunnel_t *t = nn_alloc(alloc, sizeof(nn_loopTunnel_t)); - t->ctx = *context; - t->opts = opts; - t->opts.address = nn_strdup(alloc, t->opts.address); - t->wakeupLen = 0; - - nn_tunnelTable table = { - .userdata = t, - .deinit = (void *)nni_debugTunnel_deinit, - - .maxValues = opts.maxValues, - .maxPacketSize = opts.maxPacketSize, - .getChannel = (void *)nni_debugTunnel_getChannel, - .getWakeMessage = (void *)nni_debugTunnel_getWakeMessage, - .setWakeMessage = (void *)nni_debugTunnel_setWakeMessage, - .send = (void *)nni_debugTunnel_send, - }; - - return nn_newTunnel(context, table, control); -} diff --git a/src/components/modem.c b/src/components/modem.c deleted file mode 100644 index 4cc23de..0000000 --- a/src/components/modem.c +++ /dev/null @@ -1,351 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_modem { - nn_Context ctx; - nn_guard *lock; - nn_refc refc; - nn_modemTable table; - nn_networkControl ctrl; -} nn_modem; - -nn_modem *nn_newModem(nn_Context *context, nn_modemTable table, nn_networkControl control) { - nn_modem *m = nn_alloc(&context->allocator, sizeof(nn_modem)); - if(m == NULL) return m; - m->ctx = *context; - m->lock = nn_newGuard(context); - m->refc = 1; - m->table = table; - m->ctrl = control; - return m; -} - -nn_guard *nn_getModemLock(nn_modem *modem) { - return modem->lock; -} - -void nn_retainModem(nn_modem *modem) { - nn_incRef(&modem->refc); -} - -nn_bool_t nn_destroyModem(nn_modem *modem) { - if(!nn_decRef(&modem->refc)) return false; - - if(modem->table.deinit != NULL) { - modem->table.deinit(modem->table.userdata); - } - - nn_Context ctx = modem->ctx; - nn_deleteGuard(&ctx, modem->lock); - nn_dealloc(&ctx.allocator, modem, sizeof(nn_modem)); - return true; -} - -void nn_modem_destroy(void *_, nn_component *component, nn_modem *modem) { - nn_destroyModem(modem); -} - -static void nni_modem_isWireless(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_return_boolean(computer, modem->table.wireless); -} - -static void nni_modem_maxPacketSize(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, modem->table.maxPacketSize); -} - -static void nni_modem_maxOpenPorts(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, modem->table.maxOpenPorts); -} - -static void nni_modem_maxValues(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, modem->table.maxValues); -} - -static nn_bool_t nni_modem_wirelessOnly(nn_modem *modem, void *_) { - return modem->table.wireless; -} - -static void nni_modem_maxStrength(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_return_number(computer, modem->table.maxStrength); -} - -static void nni_modem_getStrength(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - - nn_lock(&modem->ctx, modem->lock); - double n = modem->table.getStrength(modem->table.userdata, err); - nn_unlock(&modem->ctx, modem->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_number(computer, n); -} - -static void nni_modem_setStrength(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - double n = nn_toNumber(nn_getArgument(computer, 0)); - - if(n < 0) n = 0; - if(n > modem->table.maxStrength) n = modem->table.maxStrength; - - nn_errorbuf_t err = ""; - - nn_lock(&modem->ctx, modem->lock); - n = modem->table.setStrength(modem->table.userdata, n, err); - nn_unlock(&modem->ctx, modem->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_number(computer, n); -} - -static nn_bool_t nni_modem_validSendPort(nn_integer_t port) { - // 9 quintillion ports just died - if(port < 0) return false; - // the only valid range - if(port > NN_PORT_MAX) return false; - if(port < 1) return false; - // whichever it is reserved as (clean code moment) - if(port == NN_TUNNEL_PORT) return false; - return true; -} - -static void nni_modem_isOpen(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_integer_t port = nn_toInt(nn_getArgument(computer, 0)); - if(!nni_modem_validSendPort(port)) { - nn_setCError(computer, "invalid port"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&modem->ctx, modem->lock); - nn_bool_t res = modem->table.isOpen(modem->table.userdata, port, err); - nn_unlock(&modem->ctx, modem->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_boolean(computer, res); -} - -static void nni_modem_open(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_integer_t port = nn_toInt(nn_getArgument(computer, 0)); - if(!nni_modem_validSendPort(port)) { - nn_setCError(computer, "invalid port"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&modem->ctx, modem->lock); - nn_bool_t res = modem->table.open(modem->table.userdata, port, err); - nn_unlock(&modem->ctx, modem->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_boolean(computer, res); -} - -static void nni_modem_close(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_value portVal = nn_getArgument(computer, 0); - nn_integer_t port = portVal.tag == NN_VALUE_NIL ? NN_PORT_CLOSEALL : nn_toInt(portVal); - if(!nni_modem_validSendPort(port) && port != NN_PORT_CLOSEALL) { - nn_setCError(computer, "invalid port"); - return; - } - nn_errorbuf_t err = ""; - nn_lock(&modem->ctx, modem->lock); - nn_bool_t res = modem->table.close(modem->table.userdata, port, err); - nn_unlock(&modem->ctx, modem->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_boolean(computer, res); -} - -static void nni_modem_getPorts(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_size_t ports[modem->table.maxOpenPorts]; - nn_errorbuf_t err = ""; - nn_lock(&modem->ctx, modem->lock); - nn_size_t portCount = modem->table.getPorts(modem->table.userdata, ports, err); - nn_unlock(&modem->ctx, modem->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_value arr = nn_return_array(computer, portCount); - for(nn_size_t i = 0; i < portCount; i++) { - nn_values_set(arr, i, nn_values_integer(ports[i])); - } -} - -static void nni_modem_send(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - // we pinky promise it won't do a fucky wucky - nn_address addr = (nn_address)nn_toCString(nn_getArgument(computer, 0)); - if(addr == NULL) { - nn_setCError(computer, "invalid address"); - return; - } - nn_value portVal = nn_getArgument(computer, 1); - nn_integer_t port = portVal.tag == NN_VALUE_NIL ? NN_PORT_CLOSEALL : nn_toInt(portVal); - if(!nni_modem_validSendPort(port) && port != NN_PORT_CLOSEALL) { - nn_setCError(computer, "invalid port"); - return; - } - nn_value vals[modem->table.maxValues]; - nn_size_t valLen = nn_getArgumentCount(computer) - 2; - - if(valLen > modem->table.maxValues) { - nn_setCError(computer, "too many values"); - return; - } - - for(nn_size_t i = 0; i < valLen; i++) { - vals[i] = nn_getArgument(computer, i + 2); - } - - nn_size_t bytesSent = nn_measurePacketSize(vals, valLen); - if(bytesSent > modem->table.maxPacketSize) { - nn_setCError(computer, "packet too big"); - return; - } - nn_simulateBufferedIndirect(component, bytesSent, modem->ctrl.packetBytesPerTick); - double d = (double)bytesSent / modem->table.maxPacketSize; - nn_addHeat(computer, d * modem->ctrl.heatPerFullPacket); - nn_removeEnergy(computer, d * modem->ctrl.energyPerFullPacket); - - nn_errorbuf_t err = ""; - nn_lock(&modem->ctx, modem->lock); - nn_bool_t res = modem->table.send(modem->table.userdata, addr, port, vals, valLen, err); - nn_unlock(&modem->ctx, modem->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, res); -} - -static void nni_modem_broadcast(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_value portVal = nn_getArgument(computer, 0); - nn_integer_t port = portVal.tag == NN_VALUE_NIL ? NN_PORT_CLOSEALL : nn_toInt(portVal); - if(!nni_modem_validSendPort(port) && port != NN_PORT_CLOSEALL) { - nn_setCError(computer, "invalid port"); - return; - } - nn_value vals[modem->table.maxValues]; - nn_size_t valLen = nn_getArgumentCount(computer) - 1; - - if(valLen > modem->table.maxValues) { - nn_setCError(computer, "too many values"); - return; - } - - for(nn_size_t i = 0; i < valLen; i++) { - vals[i] = nn_getArgument(computer, i + 1); - } - - nn_size_t bytesSent = nn_measurePacketSize(vals, valLen); - if(bytesSent > modem->table.maxPacketSize) { - nn_setCError(computer, "packet too big"); - return; - } - nn_simulateBufferedIndirect(component, bytesSent, modem->ctrl.packetBytesPerTick); - double d = (double)bytesSent / modem->table.maxPacketSize; - nn_addHeat(computer, d * modem->ctrl.heatPerFullPacket); - nn_removeEnergy(computer, d * modem->ctrl.energyPerFullPacket); - - nn_errorbuf_t err = ""; - nn_lock(&modem->ctx, modem->lock); - nn_bool_t res = modem->table.send(modem->table.userdata, NULL, port, vals, valLen, err); - nn_unlock(&modem->ctx, modem->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, res); -} - -static void nni_modem_getWake(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - char msg[NN_MAX_WAKEUPMSG]; - nn_errorbuf_t err = ""; - - nn_lock(&modem->ctx, modem->lock); - nn_size_t len = modem->table.getWakeMessage(modem->table.userdata, msg, err); - nn_unlock(&modem->ctx, modem->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_string(computer, msg, len); -} - -static void nni_modem_setWake(nn_modem *modem, void *_, nn_component *component, nn_computer *computer) { - nn_size_t buflen; - const char *buf = nn_toString(nn_getArgument(computer, 0), &buflen); - - if(buf == NULL) { - nn_setCError(computer, "invalid wake message"); - return; - } - - if(buflen > NN_MAX_WAKEUPMSG) { - buflen = NN_MAX_WAKEUPMSG; - } - - nn_bool_t fuzzy = nn_toBoolean(nn_getArgument(computer, 1)); // nil is false - - nn_errorbuf_t err = ""; - - nn_lock(&modem->ctx, modem->lock); - buflen = modem->table.setWakeMessage(modem->table.userdata, buf, buflen, fuzzy, err); - nn_unlock(&modem->ctx, modem->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_string(computer, buf, buflen); -} - -void nn_loadModemTable(nn_universe *universe) { - nn_componentTable *modemTable = nn_newComponentTable(nn_getAllocator(universe), "modem", NULL, NULL, (nn_componentDestructor *)nn_modem_destroy); - nn_storeUserdata(universe, "NN:MODEM", modemTable); - - nn_method_t *method; - nn_defineMethod(modemTable, "isWireless", (nn_componentMethod *)nni_modem_isWireless, "isWireless(): boolean - Returns whether this modem has wireless capabilities"); - nn_defineMethod(modemTable, "maxPacketSize", (nn_componentMethod *)nni_modem_maxPacketSize, "maxPacketSize(): integer - Returns the maximum size of a packet"); - nn_defineMethod(modemTable, "maxOpenPorts", (nn_componentMethod *)nni_modem_maxOpenPorts, "maxOpenPorts(): integer - Returns the maximum number of ports that can be open at the same time"); - nn_defineMethod(modemTable, "maxValues", (nn_componentMethod *)nni_modem_maxValues, "maxValues(): integer - Returns the maximum number of values which can be sent in a packet"); - nn_defineMethod(modemTable, "isOpen", (nn_componentMethod *)nni_modem_isOpen, "isOpen(port: integer): boolean - Returns whether a port is open (allows receiving)"); - nn_defineMethod(modemTable, "open", (nn_componentMethod *)nni_modem_open, "open(port: integer): boolean - Opens a port, returns whether it was successful"); - nn_defineMethod(modemTable, "close", (nn_componentMethod *)nni_modem_close, "close([port: integer]): boolean - Closes a port, or nil for all ports"); - nn_defineMethod(modemTable, "getPorts", (nn_componentMethod *)nni_modem_getPorts, "getPorts(): integer[] - Returns the open ports"); - nn_defineMethod(modemTable, "send", (nn_componentMethod *)nni_modem_send, "send(address: string, port: integer, ...): boolean - Sends a message to the specified address at the given port. It returns whether the message was sent, not received"); - nn_defineMethod(modemTable, "broadcast", (nn_componentMethod *)nni_modem_broadcast, "broadcast(port: integer, ...): boolean - Broadcasts a message at the given port. It returns whether the message was sent, not received"); - nn_defineMethod(modemTable, "setWakeMessage", (nn_componentMethod *)nni_modem_setWake, "setWakeMessage(msg: string[, fuzzy: boolean]): string - Sets the wake-up message. This will be compared with the first value of modem messages to turn on computers. Set it to nothing to disable this functionality."); - nn_defineMethod(modemTable, "getWakeMessage", (nn_componentMethod *)nni_modem_getWake, "getWakeMessage(): string - Returns the current wake-up message"); - - // wireless stuff - method = nn_defineMethod(modemTable, "maxStrength", (nn_componentMethod *)nni_modem_maxStrength, "maxStrength(): number - Returns the maximum strength of the device"); - nn_method_setCondition(method, (nn_componentMethodCondition_t *)nni_modem_wirelessOnly); - - method = nn_defineMethod(modemTable, "getStrength", (nn_componentMethod *)nni_modem_getStrength, "getStrength(): number - Returns the current strength of the device"); - nn_method_setCondition(method, (nn_componentMethodCondition_t *)nni_modem_wirelessOnly); - - method = nn_defineMethod(modemTable, "setStrength", (nn_componentMethod *)nni_modem_setStrength, "setStrength(value: number): number - Returns the current strength of the device"); - nn_method_setCondition(method, (nn_componentMethodCondition_t *)nni_modem_wirelessOnly); -} - -nn_component *nn_addModem(nn_computer *computer, nn_address address, int slot, nn_modem *modem) { - nn_componentTable *modemTable = nn_queryUserdata(nn_getUniverse(computer), "NN:MODEM"); - return nn_newComponent(computer, address, slot, modemTable, modem); -} diff --git a/src/components/screen.c b/src/components/screen.c deleted file mode 100644 index f01eeae..0000000 --- a/src/components/screen.c +++ /dev/null @@ -1,512 +0,0 @@ -#include "screen.h" - -nn_screen *nn_newScreen(nn_Context *context, int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors) { - nn_Alloc *alloc = &context->allocator; - // TODO: handle OOMs - nn_screen *screen = nn_alloc(alloc, sizeof(nn_screen)); - screen->ctx = *context; - screen->buffer = nn_alloc(alloc, sizeof(nn_scrchr_t) * maxWidth * maxHeight); - screen->lock = nn_newGuard(context); - screen->refc = 1; - screen->width = maxWidth; - screen->height = maxHeight; - screen->viewportWidth = maxWidth; - screen->viewportHeight = maxHeight; - screen->maxWidth = maxWidth; - screen->maxHeight = maxHeight; - screen->maxDepth = maxDepth; - screen->depth = maxDepth; - screen->editableColors = editableColors; - screen->paletteColors = paletteColors; - screen->palette = nn_alloc(alloc, sizeof(int) * screen->paletteColors); - nn_memset(screen->palette, 0, sizeof(int) * screen->paletteColors); - screen->aspectRatioWidth = 1; - screen->aspectRatioHeight = 1; - screen->isOn = true; - screen->isTouchModeInverted = true; - screen->isPrecise = true; - screen->isDirty = true; - screen->keyboardCount = 0; - return screen; -} - -void nn_retainScreen(nn_screen *screen) { - nn_incRef(&screen->refc); -} - -void nn_destroyScreen(nn_screen *screen) { - if(!nn_decRef(&screen->refc)) return; - nn_Alloc a = screen->ctx.allocator; - nn_deleteGuard(&screen->ctx, screen->lock); - nn_dealloc(&a, screen->buffer, sizeof(nn_scrchr_t) * screen->maxWidth * screen->maxHeight); - nn_dealloc(&a, screen->palette, sizeof(int) * screen->paletteColors); - nn_dealloc(&a, screen, sizeof(nn_screen)); -} - -void nn_lockScreen(nn_screen *screen) { - nn_lock(&screen->ctx, screen->lock); -} - -void nn_unlockScreen(nn_screen *screen) { - nn_unlock(&screen->ctx, screen->lock); -} - -void nn_getResolution(nn_screen *screen, int *width, int *height) { - *width = screen->width; - *height = screen->height; -} - -void nn_maxResolution(nn_screen *screen, int *width, int *height) { - *width = screen->maxWidth; - *height = screen->maxHeight; -} - -void nn_setResolution(nn_screen *screen, int width, int height) { - screen->width = width; - screen->height = height; -} - -nn_bool_t nn_unsafeReallocateScreenBuffer(nn_screen *screen, int maxWidth, int maxHeight) { - nn_Alloc *alloc = &screen->ctx.allocator; - - nn_scrchr_t *newBuffer = nn_alloc(alloc, sizeof(nn_scrchr_t) * maxWidth * maxHeight); - if(newBuffer == NULL) { - return false; - } - - for(nn_size_t y = 0; y < maxHeight; y++) { - for(nn_size_t x = 0; x < maxWidth; x++) { - nn_size_t destIdx = x + y * maxWidth; - - newBuffer[destIdx] = nn_getPixel(screen, x, y); - } - } - - nn_dealloc(alloc, screen->buffer, sizeof(nn_scrchr_t) * screen->maxWidth * screen->maxHeight); - - screen->buffer = newBuffer; - screen->maxWidth = maxWidth; - screen->maxHeight = maxHeight; - return true; -} - -void nn_getViewport(nn_screen *screen, int *width, int *height) { - *width = screen->viewportWidth; - *height = screen->viewportHeight; -} - -void nn_setViewport(nn_screen *screen, int width, int height) { - screen->viewportWidth = width; - screen->viewportHeight = height; -} - -void nn_getAspectRatio(nn_screen *screen, int *width, int *height) { - *width = screen->aspectRatioWidth; - *height = screen->aspectRatioHeight; -} - -void nn_setAspectRatio(nn_screen *screen, int width, int height) { - screen->aspectRatioWidth = width; - screen->aspectRatioHeight = height; -} - -void nn_addKeyboard(nn_screen *screen, nn_address address) { - if(screen->keyboardCount == NN_MAX_SCREEN_KEYBOARDS) return; - char *kb = nn_strdup(&screen->ctx.allocator, address); - if(kb == NULL) return; - screen->keyboards[screen->keyboardCount++] = kb; -} - -void nn_removeKeyboard(nn_screen *screen, nn_address address) { - nn_size_t j = 0; - for(nn_size_t i = 0; i < screen->keyboardCount; i++) { - if(nn_strcmp(screen->keyboards[i], address) == 0) { - nn_deallocStr(&screen->ctx.allocator, screen->keyboards[i]); - } else { - screen->keyboards[j] = screen->keyboards[i]; - j++; - } - } - screen->keyboardCount = j; -} - -nn_address nn_getKeyboard(nn_screen *screen, nn_size_t idx) { - if(idx >= screen->keyboardCount) return NULL; - return screen->keyboards[idx]; -} - -nn_size_t nn_getKeyboardCount(nn_screen *screen) { - return screen->keyboardCount; -} - -void nn_setEditableColors(nn_screen *screen, int count) { - screen->editableColors = count; -} - -int nn_getEditableColors(nn_screen *screen) { - return screen->editableColors; -} - -void nn_setPaletteColor(nn_screen *screen, int idx, int color) { - if(idx >= screen->paletteColors) return; - screen->palette[idx] = color; -} - -int nn_getPaletteColor(nn_screen *screen, int idx) { - if(idx >= screen->paletteColors) return 0; - return screen->palette[idx]; -} - -int nn_getPaletteCount(nn_screen *screen) { - return screen->paletteColors; -} - -int nn_maxDepth(nn_screen *screen) { - return screen->maxDepth; -} - -int nn_getDepth(nn_screen *screen) { - return screen->depth; -} - -void nn_setDepth(nn_screen *screen, int depth) { - if(depth > screen->maxDepth) depth = screen->maxDepth; - screen->depth = depth; -} - -void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel) { - if(x < 0) return; - if(y < 0) return; - if(x >= screen->width) return; - if(y >= screen->height) return; - screen->buffer[x + y * screen->maxWidth] = pixel; - screen->isDirty = true; // stuff changed -} - -nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y) { - nn_scrchr_t blank = { - .codepoint = ' ', - .fg = 0xFFFFFF, - .bg = 0x000000, - .isFgPalette = false, - .isBgPalette = false, - }; - if(x < 0) return blank; - if(y < 0) return blank; - if(x >= screen->width) return blank; - if(y >= screen->height) return blank; - return screen->buffer[x + y * screen->maxWidth]; -} - -nn_bool_t nn_isDirty(nn_screen *screen) { - return screen->isDirty; -} - -void nn_setDirty(nn_screen *screen, nn_bool_t dirty) { - screen->isDirty = dirty; -} - -nn_bool_t nn_isPrecise(nn_screen *screen) { - return screen->isPrecise; -} - -void nn_setPrecise(nn_screen *screen, nn_bool_t precise) { - screen->isPrecise = precise; -} - -nn_bool_t nn_isTouchModeInverted(nn_screen *screen) { - return screen->isTouchModeInverted; -} - -void nn_setTouchModeInverted(nn_screen *screen, nn_bool_t touchModeInverted) { - screen->isTouchModeInverted = touchModeInverted; -} - -nn_bool_t nn_isOn(nn_screen *buffer) { - return buffer->isOn; -} - -void nn_setOn(nn_screen *buffer, nn_bool_t on) { - buffer->isOn = on; -} - -void nn_screenComp_destroy(void *_, nn_component *component, nn_screen *screen) { - nn_destroyScreen(screen); -} - -void nn_screenComp_getKeyboards(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - nn_value arr = nn_values_array(&screen->ctx.allocator, nn_getKeyboardCount(screen)); - - nn_size_t len = arr.array->len; - for(nn_size_t i = 0; i < len; i++) { - nn_size_t addrlen = nn_strlen(nn_getKeyboard(screen, i)); - nn_value addr = nn_values_string(&screen->ctx.allocator, nn_getKeyboard(screen, i), addrlen); - nn_values_set(arr, i, addr); - } - - nn_unlockScreen(screen); - nn_return(computer, arr); -} - -void nn_screenComp_getAspectRatio(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - - int w, h; - nn_getAspectRatio(screen, &w, &h); - - nn_unlockScreen(screen); - - nn_return_integer(computer, w); - nn_return_integer(computer, h); -} - -void nn_screenComp_isOn(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - - nn_bool_t isOn = nn_isOn(screen); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isOn); -} - -void nn_screenComp_turnOn(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - - nn_bool_t isOff = !nn_isOn(screen); - nn_setOn(screen, true); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isOff); - nn_return_boolean(computer, true); -} - -void nn_screenComp_turnOff(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - - nn_bool_t isOn = nn_isOn(screen); - nn_setOn(screen, false); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isOn); - nn_return_boolean(computer, false); -} - -void nn_screenComp_setPrecise(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_bool_t isPrecise = nn_toBooleanOr(nn_getArgument(computer, 0), true); - - nn_lockScreen(screen); - - nn_setPrecise(screen, isPrecise); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isPrecise); -} - -void nn_screenComp_isPrecise(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - - nn_bool_t isPrecise = nn_isPrecise(screen); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isPrecise); -} - -void nn_screenComp_setTouchModeInverted(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_bool_t isTouchModeInverted = nn_toBooleanOr(nn_getArgument(computer, 0), true); - - nn_lockScreen(screen); - - nn_setTouchModeInverted(screen, isTouchModeInverted); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isTouchModeInverted); -} - -void nn_screenComp_isTouchModeInverted(nn_screen *screen, void *_, nn_component *component, nn_computer *computer) { - nn_lockScreen(screen); - - nn_bool_t isTouchModeInverted = nn_isTouchModeInverted(screen); - - nn_unlockScreen(screen); - - nn_return_boolean(computer, isTouchModeInverted); -} - -void nn_loadScreenTable(nn_universe *universe) { - nn_componentTable *screenTable = nn_newComponentTable(nn_getAllocator(universe), "screen", NULL, NULL, (nn_componentDestructor *)nn_screenComp_destroy); - nn_storeUserdata(universe, "NN:SCREEN", screenTable); - - nn_defineMethod(screenTable, "getKeyboards", (nn_componentMethod *)nn_screenComp_getKeyboards, "getKeyboards(): string[] - Returns the keyboards registered to this screen."); - nn_defineMethod(screenTable, "getAspectRatio", (nn_componentMethod *)nn_screenComp_getAspectRatio, "getAspectRatio(): integer, integer - Returns the dimensions, in blocks, of the screen."); - nn_defineMethod(screenTable, "isOn", (nn_componentMethod *)nn_screenComp_isOn, "isOn(): boolean - Returns whether the screen is on."); - nn_defineMethod(screenTable, "turnOn", (nn_componentMethod *)nn_screenComp_turnOn, "turnOn(): boolean, boolean - Turns the screen on. Returns whether the screen was off and the new power state."); - nn_defineMethod(screenTable, "turnOff", (nn_componentMethod *)nn_screenComp_turnOff, "turnOff(): boolean, boolean - Turns the screen off. Returns whether the screen was on and the new power state."); - nn_defineMethod(screenTable, "setPrecise", (nn_componentMethod *)nn_screenComp_setPrecise, "setPrecise(precise: boolean) - Sets whether precise (sub-pixel) mouse events are enabled"); - nn_defineMethod(screenTable, "isPrecise", (nn_componentMethod *)nn_screenComp_isPrecise, "isPrecise(): boolean - Checks whether precise mouse events are enabled"); - nn_defineMethod(screenTable, "setTouchModeInverted", (nn_componentMethod *)nn_screenComp_setTouchModeInverted, "setTouchModeInverted(mode: boolean) - Sets whether inverted touch mode is enabled"); - nn_defineMethod(screenTable, "isTouchModeInverted", (nn_componentMethod *)nn_screenComp_isTouchModeInverted, "isTouchModeInverted(): boolean - Checks whether inverted touch mode is enabled"); -} - -nn_componentTable *nn_getScreenTable(nn_universe *universe) { - return nn_queryUserdata(universe, "NN:SCREEN"); -} - -nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen) { - nn_componentTable *screenTable = nn_queryUserdata(nn_getUniverse(computer), "NN:SCREEN"); - return nn_newComponent(computer, address, slot, screenTable, screen); -} - -static const int nni_mcBlack = 0x000000; -static const int nni_mcWhite = 0xFFFFFF; - -void nn_getStd4BitPalette(int color[16]) { - color[0] = nni_mcWhite; // white - color[1] = 0xF9801D; // orange - color[2] = 0xC74EBD; // magenta - color[3] = 0x3AB3DA; // lightblue - color[4] = 0xFED83D; // yellow - color[5] = 0x80C71F; // lime - color[6] = 0xF38BAA; // pink - color[7] = 0x474F52; // gray - color[8] = 0x9D9D97; // silver - color[9] = 0x169C9C; // cyan - color[10] = 0x8932B8; // purple - color[11] = 0x3C44AA; // blue - color[12] = 0x835432; // brown - color[13] = 0x5E7C16; // green - color[14] = 0xB02E26; // red - color[15] = nni_mcBlack; // black -} - -void nn_getLegacy4BitPalette(int color[16]) { - // taken from https://github.com/MightyPirates/OpenComputers/blob/master-MC1.12/src/main/scala/li/cil/oc/util/PackedColor.scala - - color[0] = 0xFFFFFF; - color[1] = 0xFFCC33; - color[2] = 0xCC66CC; - color[3] = 0x6699FF; - color[4] = 0xFFFF33; - color[5] = 0x33CC33; - color[6] = 0xFF6699; - color[7] = 0x333333; - color[8] = 0xCCCCCC; - color[9] = 0x336699; - color[10] = 0x9933CC; - color[11] = 0x333399; - color[12] = 0x663300; - color[13] = 0x336600; - color[14] = 0xFF3333; - color[15] = 0x000000; -} - -void nn_getStd8BitPalette(int color[256]) { - // source: https://ocdoc.cil.li/component:gpu - int reds[6] = {0x00, 0x33, 0x66, 0x99, 0xCC, 0xFF}; - int greens[8] = {0x00, 0x24, 0x49, 0x6D, 0x92, 0xB6, 0xDB, 0xFF}; - int blues[5] = {0x00, 0x40, 0x80, 0xC0, 0xFF}; - - for(int r = 0; r < 6; r++) { - for(int g = 0; g < 8; g++) { - for(int b = 0; b < 5; b++) { - int i = r * 8 * 5 + g * 5 + b; - color[i] = (reds[r] << 16) | (greens[g] << 8) | (blues[b]); - } - } - } - - // TODO: turn into an algorithm - color[240] = 0x0F0F0F; - color[241] = 0x1E1E1E; - color[242] = 0x2D2D2D; - color[243] = 0x3C3C3C; - color[244] = 0x4B4B4B; - color[245] = 0x5A5A5A; - color[246] = 0x696969; - color[247] = 0x787878; - color[248] = 0x878787; - color[249] = 0x969696; - color[250] = 0xA5A5A5; - color[251] = 0xB4B4B4; - color[252] = 0xC3C3C3; - color[253] = 0xD2D2D2; - color[254] = 0xE1E1E1; - color[255] = 0xF0F0F0; -} - -static int nni_4bit_colors[16]; -static nn_bool_t nni_4bit_did = false; -static int nni_8bit_colors[256]; -static nn_bool_t nni_8bit_did = false; - -static int nni_4bitl_colors[16]; -static nn_bool_t nni_4bitl_did = false; - -const char *nn_depthName(int depth) { - if(depth == 1) return "OneBit"; - if(depth == 2) return "TwoBit"; - if(depth == 3) return "ThreeBit"; - if(depth == 4) return "FourBit"; - if(depth == 8) return "EightBit"; - if(depth == 16) return "SixteenBit"; - if(depth == 24) return "TwentyFourBit"; - return NULL; -} - -int nn_mapDepth(int color, int depth, nn_bool_t legacy) { - if(depth == 1) { - if(color == 0) return nni_mcBlack; - return nni_mcWhite; - } - if(depth == 2) { - int palette[4] = { - 0x000000, - 0x444444, - 0x999999, - 0xFFFFFF, - }; - return nn_mapColor(color, palette, 4); - } - if(depth == 3) { - int palette[8] = { - 0x000000, - 0xFF0000, - 0x00FF00, - 0xFFFF00, - 0x0000FF, - 0xFF00FF, - 0x00FFFF, - 0xFFFFFF, - }; - return nn_mapColor(color, palette, 8); - } - if(depth == 4) { - if(legacy) { - if(!nni_4bitl_did) { - nni_4bitl_did = true; - nn_getLegacy4BitPalette(nni_4bitl_colors); - } - return nn_mapColor(color, nni_4bitl_colors, 16); - } else { - if(!nni_4bit_did) { - nni_4bit_did = true; - nn_getStd4BitPalette(nni_4bit_colors); - } - return nn_mapColor(color, nni_4bit_colors, 16); - } - } - if(depth == 8) { - if(!nni_8bit_did) { - nni_8bit_did = true; - nn_getStd8BitPalette(nni_8bit_colors); - } - return nn_mapColor(color, nni_8bit_colors, 256); - } - return color; -} diff --git a/src/components/screen.h b/src/components/screen.h deleted file mode 100644 index 864b804..0000000 --- a/src/components/screen.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef NN_SCREEN_H -#define NN_SCREEN_H - -#include "../neonucleus.h" - -typedef struct nn_screen { - nn_Context ctx; - nn_scrchr_t *buffer; - nn_guard *lock; - nn_refc refc; - int width; - int height; - int viewportWidth; - int viewportHeight; - int maxWidth; - int maxHeight; - int maxDepth; - int depth; - int editableColors; - int paletteColors; - int *palette; - int aspectRatioWidth; - int aspectRatioHeight; - nn_bool_t isOn; - nn_bool_t isTouchModeInverted; - nn_bool_t isPrecise; - nn_bool_t isDirty; - nn_address keyboards[NN_MAX_SCREEN_KEYBOARDS]; - nn_size_t keyboardCount; -} nn_screen; - -#endif diff --git a/src/components/tunnel.c b/src/components/tunnel.c deleted file mode 100644 index e746501..0000000 --- a/src/components/tunnel.c +++ /dev/null @@ -1,158 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_tunnel { - nn_Context ctx; - nn_refc refc; - nn_guard *lock; - nn_tunnelTable table; - nn_networkControl ctrl; -} nn_tunnel; - -nn_tunnel *nn_newTunnel(nn_Context *context, nn_tunnelTable table, nn_networkControl control) { - nn_Alloc *a = &context->allocator; - - nn_tunnel *t = nn_alloc(a, sizeof(nn_tunnel)); - if(t == NULL) return NULL; - t->lock = nn_newGuard(context); - if(t->lock == NULL) { - nn_dealloc(a, t, sizeof(nn_tunnel)); - return NULL; - } - t->ctx = *context; - t->refc = 1; - t->table = table; - t->ctrl = control; - return t; -} - -nn_guard *nn_getTunnelLock(nn_tunnel *tunnel) { - return tunnel->lock; -} - -void nn_retainTunnel(nn_tunnel *tunnel) { - nn_incRef(&tunnel->refc); -} - -nn_bool_t nn_destroyTunnel(nn_tunnel *tunnel) { - if(!nn_decRef(&tunnel->refc)) return false; - return true; -} - -void nn_tunnel_destroy(void *_, nn_component *component, nn_tunnel *tunnel) { - nn_destroyTunnel(tunnel); -} - -static void nni_tunnel_maxPacketSize(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, tunnel->table.maxPacketSize); -} - -static void nni_tunnel_maxValues(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) { - nn_return_integer(computer, tunnel->table.maxValues); -} - -static void nni_tunnel_getChannel(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - char buf[NN_MAX_CHANNEL_SIZE]; - nn_lock(&tunnel->ctx, tunnel->lock); - nn_size_t len = tunnel->table.getChannel(tunnel->table.userdata, buf, err); - nn_unlock(&tunnel->ctx, tunnel->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_string(computer, buf, len); -} - -static void nni_tunnel_getWakeMessage(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) { - nn_errorbuf_t err = ""; - char buf[NN_MAX_CHANNEL_SIZE]; - nn_lock(&tunnel->ctx, tunnel->lock); - nn_size_t len = tunnel->table.getWakeMessage(tunnel->table.userdata, buf, err); - nn_unlock(&tunnel->ctx, tunnel->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - nn_return_string(computer, buf, len); -} - -static void nni_tunnel_setWakeMessage(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) { - nn_size_t buflen; - const char *buf = nn_toString(nn_getArgument(computer, 0), &buflen); - - if(buf == NULL) { - nn_setCError(computer, "invalid wake message"); - return; - } - - if(buflen > NN_MAX_WAKEUPMSG) { - buflen = NN_MAX_WAKEUPMSG; - } - - nn_bool_t fuzzy = nn_toBoolean(nn_getArgument(computer, 1)); // nil is false - - nn_errorbuf_t err = ""; - - nn_lock(&tunnel->ctx, tunnel->lock); - buflen = tunnel->table.setWakeMessage(tunnel->table.userdata, buf, buflen, fuzzy, err); - nn_unlock(&tunnel->ctx, tunnel->lock); - - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_string(computer, buf, buflen); -} - -static void nni_tunnel_send(nn_tunnel *tunnel, void *_, nn_component *component, nn_computer *computer) { - nn_value vals[tunnel->table.maxValues]; - nn_size_t valLen = nn_getArgumentCount(computer); - - if(valLen > tunnel->table.maxValues) { - nn_setCError(computer, "too many values"); - return; - } - - for(nn_size_t i = 0; i < valLen; i++) { - vals[i] = nn_getArgument(computer, i); - } - - nn_size_t bytesSent = nn_measurePacketSize(vals, valLen); - if(bytesSent > tunnel->table.maxPacketSize) { - nn_setCError(computer, "packet too big"); - return; - } - nn_simulateBufferedIndirect(component, bytesSent, tunnel->ctrl.packetBytesPerTick); - double d = (double)bytesSent / tunnel->table.maxPacketSize; - nn_addHeat(computer, d * tunnel->ctrl.heatPerFullPacket); - nn_removeEnergy(computer, d * tunnel->ctrl.energyPerFullPacket); - - nn_errorbuf_t err = ""; - nn_lock(&tunnel->ctx, tunnel->lock); - tunnel->table.send(tunnel->table.userdata, vals, valLen, err); - nn_unlock(&tunnel->ctx, tunnel->lock); - if(!nn_error_isEmpty(err)) { - nn_setError(computer, err); - return; - } - - nn_return_boolean(computer, true); -} - -void nn_loadTunnelTable(nn_universe *universe) { - nn_componentTable *tunnelTable = nn_newComponentTable(nn_getAllocator(universe), "tunnel", NULL, NULL, (nn_componentDestructor *)nn_tunnel_destroy); - nn_storeUserdata(universe, "NN:TUNNEL", tunnelTable); - - nn_defineMethod(tunnelTable, "maxPacketSize", (nn_componentMethod *)nni_tunnel_maxPacketSize, "maxPacketSize(): integer - Returns the maximum size of a packet"); - nn_defineMethod(tunnelTable, "maxValues", (nn_componentMethod *)nni_tunnel_maxValues, "maxValues(): integer - the maximum number of values which can be sent in a packet"); - nn_defineMethod(tunnelTable, "getChannel", (nn_componentMethod *)nni_tunnel_getChannel, "getChannel(): string - Gets the ID of the communications channel of the tunnel. Under normal conditions, there are only 2 cards in the same universe which share this."); - nn_defineMethod(tunnelTable, "getWakeMessage", (nn_componentMethod *)nni_tunnel_getWakeMessage, "getWakeMessage(): string - Returns the current wake message"); - nn_defineMethod(tunnelTable, "setWakeMessage", (nn_componentMethod *)nni_tunnel_setWakeMessage, "setWakeMessage(msg: string[, fuzzy: boolean]): string - Sets the new wake message"); - nn_defineMethod(tunnelTable, "send", (nn_componentMethod *)nni_tunnel_send, "send(...): boolean - Sends a tunnel message. Returns whether it was sent."); -} - -nn_component *nn_addTunnel(nn_computer *computer, nn_address address, int slot, nn_tunnel *tunnel) { - nn_componentTable *tunnelTable = nn_queryUserdata(nn_getUniverse(computer), "NN:TUNNEL"); - return nn_newComponent(computer, address, slot, tunnelTable, tunnel); -} diff --git a/src/components/volatileDrive.c b/src/components/volatileDrive.c deleted file mode 100644 index b227409..0000000 --- a/src/components/volatileDrive.c +++ /dev/null @@ -1,74 +0,0 @@ -#include "../neonucleus.h" - -// TODO: make it allocate lazily - -typedef struct nn_vdrive { - nn_Context ctx; - char *buffer; - nn_size_t sectorSize; - nn_size_t capacity; - char label[NN_LABEL_SIZE]; - nn_size_t labelLen; -} nn_vdrive; - -static void nni_vdrive_deinit(nn_vdrive *vdrive) { - nn_Alloc a = vdrive->ctx.allocator; - nn_dealloc(&a, vdrive->buffer, vdrive->capacity); - nn_dealloc(&a, vdrive, sizeof(nn_vdrive)); -} - -static void nni_vdrive_getLabel(nn_vdrive *vdrive, char *buf, nn_size_t *buflen) { - nn_memcpy(buf, vdrive->label, vdrive->labelLen); - *buflen = vdrive->labelLen; -} - -static nn_size_t nni_vdrive_setLabel(nn_vdrive *vdrive, const char *buf, nn_size_t buflen) { - if(buflen > NN_LABEL_SIZE) buflen = NN_LABEL_SIZE; - nn_memcpy(vdrive->label, buf, buflen); - vdrive->labelLen = buflen; - return buflen; -} - -static void nni_vdrive_readSector(nn_vdrive *vdrive, int sector, char *buf) { - nn_memcpy(buf, vdrive->buffer + (sector - 1) * vdrive->sectorSize, vdrive->sectorSize); -} - -static void nni_vdrive_writeSector(nn_vdrive *vdrive, int sector, const char *buf) { - nn_memcpy(vdrive->buffer + (sector - 1) * vdrive->sectorSize, buf, vdrive->sectorSize); -} - -nn_drive *nn_volatileDrive(nn_Context *context, nn_vdriveOptions opts, nn_driveControl control) { - nn_Alloc *alloc = &context->allocator; - - char *buffer = nn_alloc(alloc, opts.capacity); - if(buffer == NULL) return NULL; - nn_vdrive *drive = nn_alloc(alloc, sizeof(nn_vdrive)); - if(drive == NULL) { - nn_dealloc(alloc, buffer, opts.capacity); - return NULL; - } - drive->ctx = *context; - drive->buffer = buffer; - drive->sectorSize = opts.sectorSize; - drive->capacity = opts.capacity; - nn_memcpy(drive->label, opts.label, opts.labelLen); - drive->labelLen = opts.labelLen; - if(opts.data == NULL) { - nn_memset(buffer, 0, opts.capacity); - } else { - nn_memcpy(buffer, opts.data, opts.capacity); - } - - nn_driveTable table = { - .userdata = drive, - .deinit = (void *)nni_vdrive_deinit, - .getLabel = (void *)nni_vdrive_getLabel, - .setLabel = (void *)nni_vdrive_setLabel, - .readSector = (void *)nni_vdrive_readSector, - .writeSector = (void *)nni_vdrive_writeSector, - .sectorSize = opts.sectorSize, - .platterCount = opts.platterCount, - .capacity = opts.capacity, - }; - return nn_newDrive(context, table, control); -} diff --git a/src/components/volatileEeprom.c b/src/components/volatileEeprom.c deleted file mode 100644 index e32439c..0000000 --- a/src/components/volatileEeprom.c +++ /dev/null @@ -1,98 +0,0 @@ -#include "../neonucleus.h" - -typedef struct nn_veeprom { - nn_Context ctx; - char *code; - nn_size_t codeLen; - nn_size_t codeSize; - char *data; - nn_size_t dataLen; - nn_size_t dataSize; - char label[NN_LABEL_SIZE]; - nn_size_t labelLen; - nn_bool_t isReadOnly; -} nn_veeprom; - -static void nni_veeprom_deinit(nn_veeprom *veeprom) { - nn_Alloc a = veeprom->ctx.allocator; - nn_dealloc(&a, veeprom->code, veeprom->codeSize); - nn_dealloc(&a, veeprom->data, veeprom->dataSize); - nn_dealloc(&a, veeprom, sizeof(nn_veeprom)); -} - -static void nni_veeprom_getLabel(nn_veeprom *veeprom, char *buf, nn_size_t *buflen, nn_errorbuf_t err) { - nn_memcpy(buf, veeprom->label, veeprom->labelLen); - *buflen = veeprom->labelLen; -} - -static nn_size_t nni_veeprom_setLabel(nn_veeprom *veeprom, const char *buf, nn_size_t buflen, nn_errorbuf_t err) { - if(buflen > NN_LABEL_SIZE) buflen = NN_LABEL_SIZE; - nn_memcpy(veeprom->label, buf, buflen); - veeprom->labelLen = buflen; - return buflen; -} - -static nn_size_t nni_veeprom_get(nn_veeprom *veeprom, char *buf, nn_errorbuf_t err) { - nn_memcpy(buf, veeprom->code, veeprom->codeLen); - return veeprom->codeLen; -} - -static void nni_veeprom_set(nn_veeprom *veeprom, const char *buf, nn_size_t len, nn_errorbuf_t err) { - nn_memcpy(veeprom->code, buf, len); - veeprom->codeLen = len; -} - -static nn_size_t nni_veeprom_getData(nn_veeprom *veeprom, char *buf, nn_errorbuf_t err) { - nn_memcpy(buf, veeprom->data, veeprom->dataLen); - return veeprom->dataLen; -} - -static void nni_veeprom_setData(nn_veeprom *veeprom, const char *buf, nn_size_t len, nn_errorbuf_t err) { - nn_memcpy(veeprom->data, buf, len); - veeprom->dataLen = len; -} - -static nn_bool_t nni_veeprom_isReadonly(nn_veeprom *eeprom, nn_errorbuf_t err) { - return eeprom->isReadOnly; -} - -static void nni_veeprom_makeReadonly(nn_veeprom *eeprom, nn_errorbuf_t err) { - eeprom->isReadOnly = true; -} - -nn_eeprom *nn_volatileEEPROM(nn_Context *context, nn_veepromOptions opts, nn_eepromControl control) { - nn_Alloc *a = &context->allocator; - - // TODO: handle OOM - nn_veeprom *veeprom = nn_alloc(a, sizeof(nn_veeprom)); - veeprom->ctx = *context; - veeprom->codeSize = opts.size; - veeprom->code = nn_alloc(a, veeprom->codeSize); - veeprom->codeLen = opts.len; - veeprom->dataSize = opts.dataSize; - veeprom->data = nn_alloc(a, veeprom->dataSize); - veeprom->dataLen = opts.dataLen; - veeprom->isReadOnly = opts.isReadOnly; - veeprom->labelLen = opts.labelLen; - nn_memcpy(veeprom->label, opts.label, veeprom->labelLen); - - nn_memcpy(veeprom->code, opts.code, veeprom->codeLen); - nn_memcpy(veeprom->data, opts.data, veeprom->dataLen); - - nn_eepromTable table = { - .userdata = veeprom, - .deinit = (void *)nni_veeprom_deinit, - .size = opts.size, - .dataSize = opts.dataSize, - .getLabel = (void *)nni_veeprom_getLabel, - .setLabel = (void *)nni_veeprom_setLabel, - .get = (void *)nni_veeprom_get, - .set = (void *)nni_veeprom_set, - .getData = (void *)nni_veeprom_getData, - .setData = (void *)nni_veeprom_setData, - .isReadonly = (void *)nni_veeprom_isReadonly, - .makeReadonly = (void *)nni_veeprom_makeReadonly, - }; - - return nn_newEEPROM(context, table, control); -} diff --git a/src/components/volatileFilesystem.c b/src/components/volatileFilesystem.c deleted file mode 100644 index c4ab36b..0000000 --- a/src/components/volatileFilesystem.c +++ /dev/null @@ -1,639 +0,0 @@ -#include "../neonucleus.h" - -// Data structures - -typedef struct nn_vfnode { - struct nn_vfilesystem *fs; - char name[NN_MAX_PATH]; - struct nn_vfnode *parent; - nn_bool_t isDirectory; - union { - // if directory - struct nn_vfnode **entries; - // if file - char *data; - }; - nn_size_t len; - nn_size_t cap; - nn_timestamp_t lastModified; - // this is used to block deleting - nn_refc handleCount; -} nn_vfnode; - -typedef enum nn_vfmode { - NN_VFMODE_READ, - NN_VFMODE_WRITE, - NN_VFMODE_APPEND, -} nn_vfmode; - -typedef struct nn_vfhandle { - nn_vfnode *node; - nn_integer_t position; - nn_vfmode mode; -} nn_vfhandle; - -typedef struct nn_vfilesystem { - nn_Context ctx; - nn_vfilesystemOptions opts; - double birthday; - nn_vfnode *root; -} nn_vfilesystem; - -// virtual node helpers - -nn_timestamp_t nn_vf_now(nn_vfilesystem *fs) { - nn_Clock c = fs->ctx.clock; - double time = c.proc(c.userdata); - double elapsed = time - fs->birthday; - nn_timestamp_t elapsedMS = elapsed * 1000; - return fs->opts.creationTime + elapsedMS; -} - -nn_vfnode *nn_vf_allocFile(nn_vfilesystem *fs, const char *name) { - nn_Alloc *alloc = &fs->ctx.allocator; - nn_vfnode *node = nn_alloc(alloc, sizeof(nn_vfnode)); - if(node == NULL) return NULL; - *node = (nn_vfnode) { - .fs = fs, - .lastModified = nn_vf_now(fs), - .parent = NULL, - .isDirectory = false, - .data = NULL, - .len = 0, - .cap = 0, - .handleCount = 0, - }; - // we pray - nn_strcpy(node->name, name); - return node; -} - -nn_vfnode *nn_vf_allocDirectory(nn_vfilesystem *fs, const char *name) { - nn_Alloc *alloc = &fs->ctx.allocator; - nn_vfnode *node = nn_alloc(alloc, sizeof(nn_vfnode)); - if(node == NULL) return NULL; - nn_vfnode **buffer = nn_alloc(alloc, sizeof(nn_vfnode *) * fs->opts.maxDirEntries); - if(buffer == NULL) { - nn_dealloc(alloc, node, sizeof(nn_vfnode)); - return NULL; - } - *node = (nn_vfnode) { - .fs = fs, - .lastModified = nn_vf_now(fs), - .parent = NULL, - .isDirectory = true, - .entries = buffer, - .len = 0, - .cap = fs->opts.maxDirEntries, - .handleCount = 0, - }; - // we pray - nn_strcpy(node->name, name); - return node; -} - -void nn_vf_freeNode(nn_vfnode *node) { - nn_Alloc *alloc = &node->fs->ctx.allocator; - - if(node->isDirectory) { - for(nn_size_t i = 0; i < node->len; i++) { - nn_vf_freeNode(node->entries[i]); - } - nn_dealloc(alloc, node->entries, sizeof(nn_vfnode *) * node->cap); - } else { - nn_dealloc(alloc, node->data, node->cap); - } - - nn_dealloc(alloc, node, sizeof(nn_vfnode)); -} - -nn_size_t nn_vf_spaceUsedByNode(nn_vfnode *node) { - if(node->isDirectory) { - nn_size_t sum = 0; - for(nn_size_t i = 0; i < node->len; i++) { - sum += nn_vf_spaceUsedByNode(node->entries[i]); - } - return sum; - } else { - return node->len; - } -} - -nn_vfnode *nn_vf_find(nn_vfnode *parent, const char *name) { - if(!parent->isDirectory) return NULL; - for(nn_size_t i = 0; i < parent->len; i++) { - nn_vfnode *entry = parent->entries[i]; - - if(nn_strcmp(entry->name, name) == 0) { - return entry; - } - } - return NULL; -} - -nn_bool_t nn_vf_ensureFileCapacity(nn_vfnode *file, nn_size_t capacity) { - if(file->isDirectory) return false; - nn_Alloc *alloc = &file->fs->ctx.allocator; - - if(file->cap >= capacity) return true; // already at that point - - char *newData = nn_resize(alloc, file->data, file->cap, capacity); - if(newData == NULL) { - return false; // OOM - } - file->data = newData; - file->cap = capacity; - - return true; -} - -// this is used to compute exponential backoff -// TODO: add an option to select either a slower growth rate or -// linear backoff to reduce memory usage at the cost of speed -nn_size_t nn_vf_getIdealCapacity(nn_vfnode *file, nn_size_t spaceNeeded) { - nn_size_t cap = file->cap; - if(cap == 0) cap = 1; - while(cap < spaceNeeded) cap *= 2; // this would mean a file with 1,048,577 bytes takes up 2,097,152 bytes, potentially wasting 1,048,575 bytes - return cap; -} - -void nn_vf_clampHandlePosition(nn_vfhandle *handle) { - nn_size_t len = handle->node->len; - if(handle->mode == NN_VFMODE_APPEND) { - handle->position = len; - return; - } - if(handle->position < 0) handle->position = 0; - if(handle->position > len) handle->position = len; -} - -nn_vfnode *nn_vf_resolvePathFromNode(nn_vfnode *node, const char *path) { - if(path[0] == 0) { - return node; - } - char firstDirectory[NN_MAX_PATH]; - char subpath[NN_MAX_PATH]; - if(nn_path_firstName(path, firstDirectory, subpath)) { - return nn_vf_find(node, firstDirectory); - } - - nn_vfnode *dir = nn_vf_find(node, firstDirectory); - if(dir == NULL) return NULL; - if(!dir->isDirectory) return NULL; - return nn_vf_resolvePathFromNode(dir, subpath); -} - -nn_vfnode *nn_vf_resolvePath(nn_vfilesystem *fs, const char *path) { - return nn_vf_resolvePathFromNode(fs->root, path); -} - -nn_size_t nn_vf_countTree(nn_vfnode *node) { - if(!node->isDirectory) return 1; - nn_size_t n = 1; - for(nn_size_t i = 0; i < node->len; i++) { - n += nn_vf_countTree(node->entries[i]); - } - return n; -} - -nn_bool_t nn_vf_treeHasHandles(nn_vfnode *node) { - if(!node->isDirectory) return node->handleCount > 0; - for(nn_size_t i = 0; i < node->len; i++) { - if(nn_vf_treeHasHandles(node->entries[i])) return true; - } - return false; -} - -void nn_vf_appendNode(nn_vfnode *parent, nn_vfnode *node) { - if(!parent->isDirectory) return; - if(parent->len == parent->cap) return; - parent->entries[parent->len] = node; - parent->len++; - node->parent = parent; // just to be sure -} - -void nn_vf_removeNode(nn_vfnode *parent, nn_vfnode *node) { - if(!parent->isDirectory) return; - nn_size_t j = 0; - for(nn_size_t i = 0; i < parent->len; i++) { - if(parent->entries[i] != node) { - parent->entries[j] = parent->entries[i]; - j++; - } - } - parent->len = j; -} - -// methods - -void nn_vfs_deinit(nn_vfilesystem *fs) { - nn_Context ctx = fs->ctx; - - nn_vf_freeNode(fs->root); - nn_dealloc(&ctx.allocator, fs, sizeof(nn_vfilesystem)); -} - -void nn_vfs_getLabel(nn_vfilesystem *fs, char *buf, nn_size_t *buflen, nn_errorbuf_t err) { - *buflen = fs->opts.labelLen; - nn_memcpy(buf, fs->opts.label, fs->opts.labelLen); -} - -void nn_vfs_setLabel(nn_vfilesystem *fs, const char *buf, nn_size_t buflen, nn_errorbuf_t err) { - nn_memcpy(fs->opts.label, buf, buflen); - fs->opts.labelLen = buflen; -} - -nn_size_t nn_vfs_spaceUsed(nn_vfilesystem *fs) { - return nn_vf_spaceUsedByNode(fs->root); -} - -nn_bool_t nn_vfs_isReadOnly(nn_vfilesystem *fs, nn_errorbuf_t err) { - return fs->opts.isReadOnly; -} - -nn_size_t nn_vfs_size(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node == NULL) { - nn_error_write(err, "No such file"); - return 0; - } - if(node->isDirectory) return 0; - return node->len; -} - -nn_size_t nn_vfs_remove(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node == NULL) { - nn_error_write(err, "No such file"); - return 0; - } - nn_vfnode *parent = node->parent; - if(parent == NULL) { - // root, can't delete - nn_error_write(err, "Unable to delete root"); - return 0; - } - if(nn_vf_treeHasHandles(node)) { - nn_error_write(err, "Files are pinned by handles"); - return 0; - } - nn_size_t removed = nn_vf_countTree(node); - // it is super easy to delete a tree - nn_vf_removeNode(parent, node); - parent->lastModified = nn_vf_now(fs); - nn_vf_freeNode(node); - return removed; -} - -nn_timestamp_t nn_vfs_lastModified(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node == NULL) { - nn_error_write(err, "No such file"); - return 0; - } - return node->lastModified; -} - -nn_size_t nn_vfs_rename(nn_vfilesystem *fs, const char *from, const char *to, nn_errorbuf_t err) { - // TODO: implement rename - nn_error_write(err, "Unsupported operation"); - nn_vfnode *srcNode = nn_vf_resolvePath(fs, from); - if(srcNode == NULL) { - nn_error_write(err, "No such file"); - return 0; - } - nn_vfnode *srcParent = srcNode->parent; - if(srcParent == NULL) { - // root, can't move - nn_error_write(err, "Unable to move root"); - return 0; - } - if(nn_vf_treeHasHandles(srcNode)) { - nn_error_write(err, "Files are pinned by handles"); - return 0; - } - - char name[NN_MAX_PATH]; - char parentPath[NN_MAX_PATH]; - nn_bool_t rootOut = nn_path_lastName(to, name, parentPath); - nn_vfnode *destParent = rootOut ? fs->root : nn_vf_resolvePath(fs, parentPath); - if(destParent == NULL) { - nn_error_write(err, "No such directory"); - return 0; - } - if(!destParent->isDirectory) { - nn_error_write(err, "Is a file"); - return 0; - } - if(nn_vf_find(destParent, name) != NULL) { - nn_error_write(err, "Already exists"); - return 0; - } - - if(destParent->len == destParent->cap) { - nn_error_write(err, "Too many entries"); - return 0; - } - nn_size_t moved = nn_vf_countTree(srcNode); - // super efficient moving - nn_vf_removeNode(srcParent, srcNode); - nn_vf_appendNode(destParent, srcNode); - return moved; -} - -nn_bool_t nn_vfs_exists(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - nn_vfnode *node = nn_vf_resolvePath(fs, path); - return node != NULL; -} - -nn_bool_t nn_vfs_isDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node == NULL) { - nn_error_write(err, "No such file"); - return 0; - } - return node->isDirectory; -} - -static nn_bool_t nn_vfs_recursiveMkdir(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - // this code is horribly unoptimized, with a time complexity of O(F^U), - // where F is 8.1 * 10^53 (monsterous) and U is 7 * 10^27 (very human) - // TODO: burn it with fire and make something good - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node != NULL) { - if(node->isDirectory) { - return true; - } - nn_error_write(err, "Is a file"); - return false; - } - char name[NN_MAX_PATH]; - char parentPath[NN_MAX_PATH]; - nn_bool_t isRootDir = nn_path_lastName(path, name, parentPath); - - if(!isRootDir) { - if(!nn_vfs_recursiveMkdir(fs, parentPath, err)) return false; - } - nn_vfnode *parent = isRootDir ? fs->root : nn_vf_resolvePath(fs, parentPath); - if(parent == NULL) { - nn_error_write(err, "Bad state"); // just a sanity check - return false; - } - if(parent->len == parent->cap) { - nn_error_write(err, "Too many entries"); - return false; - } - nn_vfnode *dir = nn_vf_allocDirectory(fs, name); - if(dir == NULL) { - nn_error_write(err, "Out of memory"); - return false; - } - dir->parent = parent; - parent->entries[parent->len] = dir; - parent->len++; - return true; -} - -nn_bool_t nn_vfs_makeDirectory(nn_vfilesystem *fs, const char *path, nn_errorbuf_t err) { - return nn_vfs_recursiveMkdir(fs, path, err); -} - -char **nn_vfs_list(nn_Alloc *alloc, nn_vfilesystem *fs, const char *path, nn_size_t *len, nn_errorbuf_t err) { - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node == NULL) { - nn_error_write(err, "No such file"); - return NULL; - } - if(!node->isDirectory) { - nn_error_write(err, "Not a directory"); - return NULL; - } - nn_size_t count = node->len; - *len = count; - char **buf = nn_alloc(alloc, sizeof(char *) * count); - if(buf == NULL) { - nn_error_write(err, "Out of memory"); - return NULL; - } - for(nn_size_t i = 0; i < count; i++) { - nn_vfnode *entry = node->entries[i]; - char *s = NULL; - if(entry->isDirectory) { - nn_size_t l = nn_strlen(entry->name); - s = nn_alloc(alloc, l + 2); - if(s != NULL) { - nn_memcpy(s, entry->name, l); - s[l] = '/'; - s[l+1] = 0; - } - } else { - s = nn_strdup(alloc, entry->name); - } - if(s == NULL) { - for(nn_size_t j = 0; j < i; j++) { - nn_deallocStr(alloc, buf[j]); - } - nn_error_write(err, "Out of memory"); - return NULL; - } - buf[i] = s; - } - return buf; -} - -nn_vfhandle *nn_vfs_open(nn_vfilesystem *fs, const char *path, const char *mode, nn_errorbuf_t err) { - // TODO: complete - char m = mode[0]; - nn_vfmode fmode = NN_VFMODE_READ; - if(m == 'w') fmode = NN_VFMODE_WRITE; - if(m == 'a') fmode = NN_VFMODE_APPEND; - - nn_vfnode *node = nn_vf_resolvePath(fs, path); - if(node == NULL && fmode != NN_VFMODE_READ && !fs->opts.isReadOnly) { - char parentPath[NN_MAX_PATH]; - char name[NN_MAX_PATH]; - nn_bool_t isRootFile = nn_path_lastName(path, name, parentPath); - - nn_vfnode *parent = isRootFile ? fs->root : nn_vf_resolvePath(fs, parentPath); - if(parent == NULL) { - nn_error_write(err, "Missing parent directory"); - return NULL; - } - if(!parent->isDirectory) { - nn_error_write(err, "Parent is not a directory"); - return NULL; - } - - if(parent->len == parent->cap) { - nn_error_write(err, "Too many entries"); - return NULL; - } - - node = nn_vf_allocFile(fs, name); - if(node == NULL) { - nn_error_write(err, "Out of memory"); - return NULL; - } - node->parent = parent; - - parent->entries[parent->len] = node; - parent->len++; - } - if(node == NULL) { - nn_error_write(err, "No such file"); - return NULL; - } - if(fs->opts.isReadOnly && fmode != NN_VFMODE_READ) { - nn_error_write(err, "readonly"); - return NULL; - } - if(node->isDirectory) { - nn_error_write(err, "Is a directory"); - return NULL; - } - if(fmode == NN_VFMODE_WRITE) { - node->len = 0; - node->lastModified = nn_vf_now(fs); - } - nn_vfhandle *handle = nn_alloc(&fs->ctx.allocator, sizeof(nn_vfhandle)); - if(handle == NULL) { - nn_error_write(err, "Out of memory"); - return NULL; - } - handle->mode = fmode; - handle->node = node; - handle->position = 0; - node->handleCount++; - return handle; -} - -nn_bool_t nn_vfs_close(nn_vfilesystem *fs, nn_vfhandle *handle, nn_errorbuf_t err) { - handle->node->handleCount--; - nn_dealloc(&fs->ctx.allocator, handle, sizeof(nn_vfhandle)); - return true; -} - -nn_bool_t nn_vfs_write(nn_vfilesystem *fs, nn_vfhandle *handle, const char *buf, nn_size_t len, nn_errorbuf_t err) { - nn_vf_clampHandlePosition(handle); - if(!nn_vf_ensureFileCapacity(handle->node, handle->position + len)) { - nn_error_write(err, "Out of memory"); - return false; - } - nn_memcpy(handle->node->data + handle->position, buf, len); - handle->position += len; - if(handle->node->len < handle->position) handle->node->len = handle->position; - return true; -} - -nn_size_t nn_vfs_read(nn_vfilesystem *fs, nn_vfhandle *handle, char *buf, nn_size_t required, nn_errorbuf_t err) { - nn_size_t remaining = handle->node->len - handle->position; - if(required > remaining) required = remaining; - if(required == 0) return 0; - nn_memcpy(buf, handle->node->data + handle->position, required); - handle->position += required; - return required; -} - -nn_size_t nn_vfs_seek(nn_vfilesystem *fs, nn_vfhandle *handle, const char *whence, int off, nn_errorbuf_t err) { - if(handle->mode == NN_VFMODE_APPEND) { - nn_error_write(err, "Bad file descriptor"); - return handle->node->len; - } - nn_integer_t ptr = handle->position; - if(nn_strcmp(whence, "set") == 0) { - ptr = off; - } - if(nn_strcmp(whence, "cur") == 0) { - ptr += off; - } - if(nn_strcmp(whence, "end") == 0) { - ptr = handle->node->len - off; - } - handle->position = ptr; - nn_vf_clampHandlePosition(handle); - return handle->position; -} - -typedef struct nn_vfilesystemImage { - nn_vfilesystemImageNode *nodes; - nn_size_t ptr; -} nn_vfilesystemImage; - -static nn_vfilesystemImageNode nni_vfsimg_nextNode(nn_vfilesystemImage *stream) { - nn_vfilesystemImageNode node = stream->nodes[stream->ptr]; - stream->ptr++; - return node; -} - -static nn_vfnode *nni_vfsimg_parseNode(nn_vfilesystem *fs, nn_vfilesystemImage *stream, nn_vfnode *parent) { - // TODO: make this handle OOMs - nn_vfilesystemImageNode node = nni_vfsimg_nextNode(stream); - if(node.data == NULL) { - // directory!!!!! - nn_vfnode *dir = nn_vf_allocDirectory(fs, node.name); - dir->len = node.len; - dir->parent = parent; - for(int i = 0; i < node.len; i++) { - nn_vfnode *entry = nni_vfsimg_parseNode(fs, stream, dir); - dir->entries[i] = entry; - } - return dir; - } - // file!!!!! - nn_vfnode *file = nn_vf_allocFile(fs, node.name); - nn_vf_ensureFileCapacity(file, node.len); - file->len = node.len; - file->parent = parent; - nn_memcpy(file->data, node.data, node.len); - return file; -} - -// constructor - -nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control) { - // TODO: handle OOM - nn_vfilesystem *fs = nn_alloc(&context->allocator, sizeof(nn_vfilesystem)); - fs->ctx = *context; - nn_Clock c = fs->ctx.clock; - double time = c.proc(c.userdata); - fs->birthday = time; - fs->opts = opts; - fs->root = nn_vf_allocDirectory(fs, "/"); - - if(opts.image != NULL) { - nn_vfilesystemImage stream = { - .nodes = opts.image, - .ptr = 0, - }; - // we got supplied an image, shit - fs->root->len = opts.rootEntriesInImage; - for(int i = 0; i < opts.rootEntriesInImage; i++) { - nn_vfnode *entry = nni_vfsimg_parseNode(fs, &stream, fs->root); - fs->root->entries[i] = entry; - } - } - - nn_filesystemTable table = { - .userdata = fs, - .deinit = (void *)nn_vfs_deinit, - .getLabel = (void *)nn_vfs_getLabel, - .setLabel = (void *)nn_vfs_setLabel, - .spaceUsed = (void *)nn_vfs_spaceUsed, - .spaceTotal = opts.capacity, - .isReadOnly = (void *)nn_vfs_isReadOnly, - .size = (void *)nn_vfs_size, - .remove = (void *)nn_vfs_remove, - .lastModified = (void *)nn_vfs_lastModified, - .rename = (void *)nn_vfs_rename, - .exists = (void *)nn_vfs_exists, - .isDirectory = (void *)nn_vfs_isDirectory, - .makeDirectory = (void *)nn_vfs_makeDirectory, - .list = (void *)nn_vfs_list, - .open = (void *)nn_vfs_open, - .close = (void *)nn_vfs_close, - .write = (void *)nn_vfs_write, - .read = (void *)nn_vfs_read, - .seek = (void *)nn_vfs_seek, - }; - return nn_newFilesystem(context, table, control); -} diff --git a/src/computer.c b/src/computer.c deleted file mode 100644 index 0770a57..0000000 --- a/src/computer.c +++ /dev/null @@ -1,674 +0,0 @@ -#include "computer.h" -#include "component.h" -#include "universe.h" -#include "neonucleus.h" -#include "resource.h" - -nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_architecture *arch, void *userdata, nn_size_t memoryLimit, nn_size_t componentLimit) { - nn_Alloc *alloc = &universe->ctx.allocator; - nn_computer *c = nn_alloc(alloc, sizeof(nn_computer)); - c->components = nn_alloc(alloc, sizeof(nn_component) * componentLimit); - if(c->components == NULL) { - nn_dealloc(alloc, c, sizeof(nn_computer)); - return NULL; - } - c->address = nn_strdup(alloc, address); - if(c->address == NULL) { - nn_dealloc(alloc, c->components, sizeof(nn_component) * componentLimit); - nn_dealloc(alloc, c, sizeof(nn_computer)); - return NULL; - } - c->lock = nn_newGuard(&universe->ctx); - if(c->lock == NULL) { - nn_deallocStr(alloc, c->address); - nn_dealloc(alloc, c->components, sizeof(nn_component) * componentLimit); - nn_dealloc(alloc, c, sizeof(nn_computer)); - return NULL; - } - // TODO: handle OOM - c->deviceInfo = nn_newDeviceInfoList(&universe->ctx, 16); - c->timeOffset = nn_getTime(universe); - c->supportedArchCount = 0; - c->argc = 0; - c->retc = 0; - c->err = NULL; - c->allocatedError = false; - c->state = NN_STATE_SETUP; - c->componentLen = 0; - c->componentCap = componentLimit; - c->userCount = 0; - c->maxEnergy = 5000; - c->signalCount = 0; - c->universe = universe; - c->arch = arch; - c->nextArch = arch; - c->userdata = userdata; - c->memoryTotal = memoryLimit; - c->tmpAddress = NULL; - c->temperature = 30; - c->roomTemperature = 30; - c->temperatureCoefficient = 1; - c->callCost = 0; - c->callBudget = 256; - - // Setup Architecture - c->archState = c->arch->setup(c, c->arch->userdata); - if(c->archState == NULL) { - nn_deleteGuard(&universe->ctx, c->lock); - nn_deallocStr(alloc, c->address); - nn_dealloc(alloc, c->components, sizeof(nn_component) * componentLimit); - nn_dealloc(alloc, c, sizeof(nn_computer)); - return NULL; - } - - c->rid = NN_NULL_RESOURCE; - for(nn_size_t i = 0; i < NN_MAX_CONCURRENT_RESOURCES; i++) { - c->resources[i].id = NN_NULL_RESOURCE; - } - - c->hasBeep = false; - - return c; -} - -nn_universe *nn_getUniverse(nn_computer *computer) { - return computer->universe; -} - -void nn_setTmpAddress(nn_computer *computer, nn_address tmp) { - nn_deallocStr(&computer->universe->ctx.allocator, computer->tmpAddress); - computer->tmpAddress = nn_strdup(&computer->universe->ctx.allocator, tmp); -} - -nn_address nn_getComputerAddress(nn_computer *computer) { - return computer->address; -} - -nn_address nn_getTmpAddress(nn_computer *computer) { - return computer->tmpAddress; -} - -int nn_tickComputer(nn_computer *computer) { - computer->callCost = 0; - computer->state = NN_STATE_RUNNING; - nn_clearError(computer); - computer->arch->tick(computer, computer->archState, computer->arch->userdata); - return nn_getState(computer); -} - -double nn_getUptime(nn_computer *computer) { - return nn_getTime(computer->universe) - computer->timeOffset; -} - -nn_size_t nn_getComputerMemoryUsed(nn_computer *computer) { - return computer->arch->getMemoryUsage(computer, computer->archState, computer->arch->userdata); -} - -nn_size_t nn_getComputerMemoryTotal(nn_computer *computer) { - return computer->memoryTotal; -} - -void *nn_getComputerUserData(nn_computer *computer) { - return computer->userdata; -} - -void nn_addSupportedArchitecture(nn_computer *computer, nn_architecture *arch) { - if(computer->supportedArchCount == NN_MAX_ARCHITECTURES) return; - computer->supportedArch[computer->supportedArchCount] = arch; - computer->supportedArchCount++; -} - -nn_architecture *nn_getSupportedArchitecture(nn_computer *computer, nn_size_t idx) { - if(idx >= computer->supportedArchCount) return NULL; - return computer->supportedArch[idx]; -} - -nn_architecture *nn_getArchitecture(nn_computer *computer) { - return computer->arch; -} - -nn_architecture *nn_getNextArchitecture(nn_computer *computer) { - return computer->nextArch; -} - -void nn_setNextArchitecture(nn_computer *computer, nn_architecture *arch) { - computer->nextArch = arch; -} - -void nn_deleteComputer(nn_computer *computer) { - nn_clearError(computer); - nn_resetCall(computer); - while(computer->signalCount > 0) { - nn_popSignal(computer); - } - nn_Alloc *a = &computer->universe->ctx.allocator; - for(nn_size_t i = 0; i < computer->userCount; i++) { - nn_deallocStr(a, computer->users[i]); - } - for(nn_size_t i = 0; i < NN_MAX_CONCURRENT_RESOURCES; i++) { - if(computer->resources[i].id != NN_NULL_RESOURCE) { - nn_resource_release(computer, computer->resources[i].id); - } - } - computer->arch->teardown(computer, computer->archState, computer->arch->userdata); - nn_deleteGuard(&computer->universe->ctx, computer->lock); - nn_deallocStr(a, computer->address); - nn_deallocStr(a, computer->tmpAddress); - nn_dealloc(a, computer->components, sizeof(nn_component) * computer->componentCap); - nn_dealloc(a, computer, sizeof(nn_computer)); -} - -const char *nn_pushSignal(nn_computer *computer, nn_value *values, nn_size_t len) { - if(len > NN_MAX_SIGNAL_VALS) return "too many values"; - if(len == 0) return "missing event"; - // no OOM for you hehe - if(nn_measurePacketSize(values, len) > NN_MAX_SIGNAL_SIZE) { - return "too big"; - } - if(computer->signalCount == NN_MAX_SIGNALS) return "too many signals"; - computer->signals[computer->signalCount].len = len; - for(nn_size_t i = 0; i < len; i++) { - computer->signals[computer->signalCount].values[i] = values[i]; - } - computer->signalCount++; - return NULL; -} - -nn_value nn_fetchSignalValue(nn_computer *computer, nn_size_t index) { - if(computer->signalCount == 0) return nn_values_nil(); - nn_signal *p = computer->signals; - if(index >= p->len) return nn_values_nil(); - return p->values[index]; -} - -nn_size_t nn_signalSize(nn_computer *computer) { - if(computer->signalCount == 0) return 0; - return computer->signals[0].len; -} - -void nn_popSignal(nn_computer *computer) { - if(computer->signalCount == 0) return; - nn_signal *p = computer->signals; - for(nn_size_t i = 0; i < p->len; i++) { - nn_values_drop(p->values[i]); - } - for(nn_size_t i = 1; i < computer->signalCount; i++) { - computer->signals[i-1] = computer->signals[i]; - } - computer->signalCount--; -} - -const char *nn_addUser(nn_computer *computer, const char *name) { - if(computer->userCount == NN_MAX_USERS) return "too many users"; - char *user = nn_strdup(&computer->universe->ctx.allocator, name); - if(user == NULL) return "out of memory"; - computer->users[computer->userCount] = user; - computer->userCount++; - return NULL; -} - -void nn_deleteUser(nn_computer *computer, const char *name) { - nn_size_t j = 0; - for(nn_size_t i = 0; i < computer->userCount; i++) { - char *user = computer->users[i]; - if(nn_strcmp(user, name) == 0) { - nn_deallocStr(&computer->universe->ctx.allocator, user); - } else { - computer->users[j] = user; - j++; - } - } - computer->userCount = j; -} - -const char *nn_indexUser(nn_computer *computer, nn_size_t idx) { - if(idx >= computer->userCount) return NULL; - return computer->users[idx]; -} - -nn_bool_t nn_isUser(nn_computer *computer, const char *name) { - if(computer->userCount == 0) return true; - for(nn_size_t i = 0; i < computer->userCount; i++) { - if(nn_strcmp(computer->users[i], name) == 0) return true; - } - return false; -} - -void nn_setCallBudget(nn_computer *computer, double callBudget) { - computer->callBudget = callBudget; -} - -double nn_getCallBudget(nn_computer *computer) { - return computer->callBudget; -} - -void nn_callCost(nn_computer *computer, double cost) { - computer->callCost += cost; - if(computer->callCost >= computer->callBudget) nn_triggerIndirect(computer); -} - -double nn_getCallCost(nn_computer *computer) { - return computer->callCost; -} - -nn_bool_t nn_isOverworked(nn_computer *computer) { - return computer->state == NN_STATE_OVERWORKED; -} - -void nn_triggerIndirect(nn_computer *computer) { - computer->state = NN_STATE_OVERWORKED; -} - -int nn_getState(nn_computer *computer) { - return computer->state; -} - -void nn_setState(nn_computer *computer, int state) { - computer->state = state; -} - -void nn_setEnergyInfo(nn_computer *computer, double energy, double capacity) { - computer->energy = energy; - computer->maxEnergy = capacity; -} - -double nn_getEnergy(nn_computer *computer) { - return computer->energy; -} - -double nn_getMaxEnergy(nn_computer *computer) { - return computer->maxEnergy; -} - -void nn_removeEnergy(nn_computer *computer, double energy) { - if(computer->energy < energy) { - // blackout - computer->energy = 0; - computer->state = NN_STATE_BLACKOUT; - return; - } - computer->energy -= energy; -} - -void nn_addEnergy(nn_computer *computer, double amount) { - if(computer->maxEnergy - computer->energy < amount) { - computer->energy = computer->maxEnergy; - return; - } - computer->energy += amount; -} - -double nn_getTemperature(nn_computer *computer) { - return computer->temperature; -} - -double nn_getThermalCoefficient(nn_computer *computer) { - return computer->temperatureCoefficient; -} - -double nn_getRoomTemperature(nn_computer *computer) { - return computer->roomTemperature; -} - -void nn_setTemperature(nn_computer *computer, double temperature) { - computer->temperature = temperature; - if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature; -} - -void nn_setTemperatureCoefficient(nn_computer *computer, double coefficient) { - computer->temperatureCoefficient = coefficient; -} - -void nn_setRoomTemperature(nn_computer *computer, double roomTemperature) { - computer->roomTemperature = roomTemperature; - if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature; -} - -void nn_addHeat(nn_computer *computer, double heat) { - computer->temperature += heat * computer->temperatureCoefficient; - if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature; -} - -void nn_removeHeat(nn_computer *computer, double heat) { - computer->temperature -= heat; - if(computer->temperature < computer->roomTemperature) computer->temperature = computer->roomTemperature; -} - -nn_bool_t nn_isOverheating(nn_computer *computer) { - return computer->temperature > NN_OVERHEAT_MIN; -} - -const char *nn_getError(nn_computer *computer) { - return computer->err; -} - -void nn_clearError(nn_computer *computer) { - if(computer->allocatedError) { - nn_deallocStr(&computer->universe->ctx.allocator, computer->err); - } - computer->err = NULL; - computer->allocatedError = false; -} - -void nn_setError(nn_computer *computer, const char *err) { - nn_clearError(computer); - char *copy = nn_strdup(&computer->universe->ctx.allocator, err); - if(copy == NULL) { - nn_setCError(computer, "out of memory"); - return; - } - computer->err = copy; - computer->allocatedError = true; -} - -void nn_setCError(nn_computer *computer, const char *err) { - nn_clearError(computer); - // we pinky promise this is safe - computer->err = (char *)err; - computer->allocatedError = false; -} - -nn_component *nn_newComponent(nn_computer *computer, nn_address address, int slot, nn_componentTable *table, void *userdata) { - nn_component *c = NULL; - for(nn_size_t i = 0; i < computer->componentLen; i++) { - if(computer->components[i].address == NULL) { - c = computer->components + i; - break; - } - } - if(c == NULL) { - if(computer->componentLen == computer->componentCap) return NULL; // too many - c = computer->components + computer->componentLen; - computer->componentLen++; - } - - if(address == NULL) { - c->address = nn_randomUUID(&computer->universe->ctx); - } else { - c->address = nn_strdup(&computer->universe->ctx.allocator, address); - } - if(c->address == NULL) return NULL; - c->table = table; - c->slot = slot; - c->computer = computer; - if(table->constructor == NULL) { - c->statePtr = userdata; - } else { - c->statePtr = table->constructor(table->userdata, userdata); - } - return c; -} - -void nn_removeComponent(nn_computer *computer, nn_address address) { - for(nn_size_t i = 0; i < computer->componentLen; i++) { - if(nn_strcmp(computer->components[i].address, address) == 0) { - nn_destroyComponent(computer->components + i); - } - } -} - -void nn_destroyComponent(nn_component *component) { - nn_deallocStr(&component->computer->universe->ctx.allocator, component->address); - if(component->table->destructor != NULL) { - component->table->destructor(component->table->userdata, component, component->statePtr); - } - component->address = NULL; // marks component as freed -} - -nn_component *nn_findComponent(nn_computer *computer, nn_address address) { - for(nn_size_t i = 0; i < computer->componentLen; i++) { - if(computer->components[i].address == NULL) continue; // empty slot - if(nn_strcmp(computer->components[i].address, address) == 0) { - return computer->components + i; - } - } - return NULL; -} - -nn_component *nn_iterComponent(nn_computer *computer, nn_size_t *internalIndex) { - for(nn_size_t i = *internalIndex; i < computer->componentLen; i++) { - if(computer->components[i].address == NULL) continue; - *internalIndex = i+1; - return computer->components + i; - } - return NULL; -} - -void nn_resetCall(nn_computer *computer) { - for(nn_size_t i = 0; i < computer->argc; i++) { - nn_values_drop(computer->args[i]); - } - - for(nn_size_t i = 0; i < computer->retc; i++) { - nn_values_drop(computer->rets[i]); - } - - computer->argc = 0; - computer->retc = 0; -} - -void nn_addArgument(nn_computer *computer, nn_value arg) { - if(computer->argc == NN_MAX_ARGS) return; - computer->args[computer->argc] = arg; - computer->argc++; -} - -void nn_return(nn_computer *computer, nn_value val) { - if(computer->retc == NN_MAX_RETS) return; - computer->rets[computer->retc] = val; - computer->retc++; -} - -nn_value nn_getArgument(nn_computer *computer, nn_size_t idx) { - if(idx >= computer->argc) return nn_values_nil(); - return computer->args[idx]; -} - -nn_value nn_getReturn(nn_computer *computer, nn_size_t idx) { - if(idx >= computer->retc) return nn_values_nil(); - return computer->rets[idx]; -} - -nn_size_t nn_getArgumentCount(nn_computer *computer) { - return computer->argc; -} - -nn_size_t nn_getReturnCount(nn_computer *computer) { - return computer->retc; -} - -char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len) { - return computer->arch->serialize(computer, alloc, computer->archState, computer->arch->userdata, len); -} - -void nn_deserializeProgram(nn_computer *computer, const char *memory, nn_size_t len) { - computer->arch->deserialize(computer, memory, len, computer->archState, computer->arch->userdata); -} - -nn_Context *nn_getComputerContext(nn_computer *computer) { - return &computer->universe->ctx; -} - -nn_guard *nn_getComputerLock(nn_computer *computer) { - return computer->lock; -} - -void nn_return_nil(nn_computer *computer) { - nn_return(computer, nn_values_nil()); -} - -void nn_return_integer(nn_computer *computer, nn_integer_t integer) { - nn_return(computer, nn_values_integer(integer)); -} - -void nn_return_number(nn_computer *computer, double number) { - nn_return(computer, nn_values_number(number)); -} - -void nn_return_boolean(nn_computer *computer, nn_bool_t boolean) { - nn_return(computer, nn_values_boolean(boolean)); -} - -void nn_return_cstring(nn_computer *computer, const char *cstr) { - nn_return(computer, nn_values_cstring(cstr)); -} - -void nn_return_string(nn_computer *computer, const char *str, nn_size_t len) { - nn_value val = nn_values_string(&computer->universe->ctx.allocator, str, len); - if(val.tag == NN_VALUE_NIL) { - nn_setCError(computer, "out of memory"); - } - nn_return(computer, val); -} - -nn_value nn_return_array(nn_computer *computer, nn_size_t len) { - nn_value val = nn_values_array(&computer->universe->ctx.allocator, len); - if(val.tag == NN_VALUE_NIL) { - nn_setCError(computer, "out of memory"); - } - nn_return(computer, val); - return val; -} - -nn_value nn_return_table(nn_computer *computer, nn_size_t len) { - nn_value val = nn_values_table(&computer->universe->ctx.allocator, len); - if(val.tag == NN_VALUE_NIL) { - nn_setCError(computer, "out of memory"); - } - nn_return(computer, val); - return val; -} - -void nn_return_resource(nn_computer *computer, nn_size_t userdata) { - nn_return(computer, nn_values_resource(userdata)); -} - -nn_bool_t nn_wakeupMatches(nn_value *values, nn_size_t valueLen, const char *wakeUp, nn_bool_t fuzzy) { - if(valueLen == 0) return false; - nn_value header = values[0]; - const char *headerStr = nn_toCString(header); - - if(fuzzy) { - return nn_strbegin(headerStr, wakeUp); - } else { - return nn_strcmp(headerStr, wakeUp) == 0; - } -} - -const char *nn_pushNetworkMessage(nn_computer *computer, nn_address receiver, nn_address sender, nn_size_t port, double distance, nn_value *values, nn_size_t valueLen) { - nn_Alloc *alloc = &computer->universe->ctx.allocator; - - nn_value buffer[valueLen + 5]; - buffer[0] = nn_values_cstring("modem_message"); - buffer[1] = nn_values_string(alloc, receiver, nn_strlen(receiver)); - buffer[2] = nn_values_string(alloc, sender, nn_strlen(sender)); - buffer[3] = nn_values_integer(port); - buffer[4] = nn_values_number(distance); - for(nn_size_t i = 0; i < valueLen; i++) { - buffer[i + 5] = nn_values_retain(values[i]); - } - - return nn_pushSignal(computer, buffer, valueLen + 5); -} - -static nn_resource_t *nn_resource_find(nn_computer *computer, nn_size_t id) { - for(nn_size_t i = 0; i < NN_MAX_CONCURRENT_RESOURCES; i++) { - if(computer->resources[i].id == id) { - return computer->resources + i; - } - } - return NULL; -} - -nn_size_t nn_resource_allocate(nn_computer *computer, void *userdata, nn_resourceTable_t *table) { - nn_size_t i = 0; - for(nn_size_t j = 0; j < NN_MAX_CONCURRENT_RESOURCES; j++) { - if(computer->resources[j].id == NN_NULL_RESOURCE) { - i = j; - goto slotFound; - } - } - return NN_NULL_RESOURCE; -slotFound: - computer->rid++; - nn_size_t rid = computer->rid; - computer->resources[i] = (nn_resource_t) { - .id = rid, - .ptr = userdata, - .table = table, - }; - return rid; -} - -void nn_resource_release(nn_computer *computer, nn_size_t id) { - nn_resource_t *res = nn_resource_find(computer, id); - if(res == NULL) return; - res->id = NN_NULL_RESOURCE; - if(res->table->dtor != NULL) { - res->table->dtor(res->ptr); - } -} - -nn_resourceTable_t *nn_resource_fetchTable(nn_computer *computer, nn_size_t resourceID) { - nn_resource_t *res = nn_resource_find(computer, resourceID); - if(res == NULL) return NULL; - return res->table; -} - -nn_bool_t nn_resource_invoke(nn_computer *computer, nn_size_t resourceID, const char *method) { - nn_resource_t *res = nn_resource_find(computer, resourceID); - if(res == NULL) return false; - nn_resourceTable_t *t = res->table; - for(nn_size_t i = 0; i < t->methodCount; i++) { - nn_resourceMethod_t m = t->methods[i]; - if(nn_strcmp(m.name, method) != 0) continue; - if(m.condition != NULL) { - if(!m.condition(res->ptr, m.userdata)) continue; - } - m.callback(res->ptr, m.userdata, computer); - return true; - } - return false; -} - -// returns the name, and NULL for out of bounds -const char *nn_resource_nextMethodInfo(nn_computer *computer, nn_size_t id, const char **doc, nn_size_t *idx) { - nn_resource_t *res = nn_resource_find(computer, id); - if(res == NULL) return false; - nn_resourceTable_t *t = res->table; - for(nn_size_t i = *idx; i < t->methodCount; i++) { - nn_resourceMethod_t method = t->methods[i]; - if(method.condition != NULL) { - if(!method.condition(res->ptr, method.userdata)) { - continue; - } - } - - *idx = i + 1; - *doc = method.doc; - return method.name; - } - return NULL; -} - -nn_deviceInfoList_t *nn_getComputerDeviceInfoList(nn_computer *computer) { - return computer->deviceInfo; -} - -void nn_computer_clearBeep(nn_computer *computer) { - computer->hasBeep = false; -} - -void nn_computer_setBeep(nn_computer *computer, double frequency, double duration, double volume) { - computer->hasBeep = true; - computer->beepFrequency = frequency; - computer->beepDuration = duration; - computer->beepVolume = volume; -} - -nn_bool_t nn_computer_getBeep(nn_computer *computer, double *frequency, double *duration, double *volume) { - if(frequency != NULL) *frequency = computer->beepFrequency; - if(duration != NULL) *duration = computer->beepDuration; - if(volume != NULL) *volume = computer->beepVolume; - return computer->hasBeep; -} diff --git a/src/computer.h b/src/computer.h deleted file mode 100644 index b5fe3d3..0000000 --- a/src/computer.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef NEONUCLEUS_COMPUTER_H -#define NEONUCLEUS_COMPUTER_H - -#include "neonucleus.h" - -typedef struct nn_signal { - nn_size_t len; - nn_value values[NN_MAX_SIGNAL_VALS]; -} nn_signal; - -typedef struct nn_resource_t { - nn_size_t id; - void *ptr; - nn_resourceTable_t *table; -} nn_resource_t; - -typedef struct nn_computer { - char state; - nn_bool_t hasBeep; - nn_bool_t allocatedError; - char *err; - void *userdata; - nn_guard *lock; - nn_component *components; - nn_size_t componentLen; - nn_size_t componentCap; - nn_value args[NN_MAX_ARGS]; - nn_size_t argc; - nn_value rets[NN_MAX_RETS]; - nn_size_t retc; - nn_architecture *arch; // btw - void *archState; - nn_architecture *nextArch; - nn_architecture *supportedArch[NN_MAX_ARCHITECTURES]; - nn_size_t supportedArchCount; - double timeOffset; - nn_universe *universe; - char *users[NN_MAX_USERS]; - nn_size_t userCount; - double energy; - double maxEnergy; - nn_signal signals[NN_MAX_SIGNALS]; - nn_size_t signalCount; - nn_size_t memoryTotal; - nn_address address; - nn_address tmpAddress; - double temperature; - double temperatureCoefficient; - double roomTemperature; - double callCost; - double callBudget; - nn_size_t rid; - nn_resource_t resources[NN_MAX_CONCURRENT_RESOURCES]; - nn_deviceInfoList_t *deviceInfo; - double beepFrequency; - double beepDuration; - double beepVolume; -} nn_computer; - -#endif diff --git a/src/data.zig b/src/data.zig deleted file mode 100644 index e125d84..0000000 --- a/src/data.zig +++ /dev/null @@ -1,20 +0,0 @@ -const std = @import("std"); -const c = @cImport({ - @cInclude("neonucleus.h"); -}); - -pub export fn nn_data_crc32(inBuf: [*]const u8, len: usize, outBuf: [*]u8) void { - var digest = std.hash.Crc32.hash(inBuf[0..len]); - digest = std.mem.nativeToLittle(u32, digest); - - const digestBuf: [4]u8 = @bitCast(digest); - std.mem.copyForwards(u8, outBuf[0..4], &digestBuf); -} - -pub export fn nn_data_md5(inBuf: [*]const u8, len: usize, outBuf: [*]u8) void { - std.crypto.hash.Md5.hash(inBuf[0..len], @ptrCast(outBuf), .{}); -} - -pub export fn nn_data_sha256(inBuf: [*]const u8, len: usize, outBuf: [*]u8) void { - std.crypto.hash.sha2.Sha256.hash(inBuf[0..len], @ptrCast(outBuf), .{}); -} diff --git a/src/deviceInfo.c b/src/deviceInfo.c deleted file mode 100644 index 7cb9580..0000000 --- a/src/deviceInfo.c +++ /dev/null @@ -1,130 +0,0 @@ -#include "neonucleus.h" - -typedef struct nn_deviceInfoPair_t { - char *key; - char *value; -} nn_deviceInfoPair_t; - -typedef struct nn_deviceInfo_t { - nn_deviceInfoPair_t *pairs; - nn_size_t len; - nn_size_t capacity; - char *address; - nn_Alloc alloc; -} nn_deviceInfo_t; - -typedef struct nn_deviceInfoList_t { - nn_Context ctx; - nn_deviceInfo_t *info; - nn_size_t len; - nn_size_t cap; -} nn_deviceInfoList_t; - -nn_deviceInfoList_t *nn_newDeviceInfoList(nn_Context *ctx, nn_size_t preallocate) { - nn_Alloc *alloc = &ctx->allocator; - - nn_deviceInfoList_t *list = nn_alloc(alloc, sizeof(nn_deviceInfoList_t)); - if(list == NULL) return NULL; - list->ctx = *ctx; - list->len = 0; - list->cap = preallocate; - list->info = nn_alloc(alloc, sizeof(nn_deviceInfo_t) * list->cap); - if(list->info == NULL) { - nn_dealloc(alloc, list, sizeof(nn_deviceInfoList_t)); - return NULL; - } - return list; -} - -static void nn_deleteDeviceInfo(nn_Context *ctx, nn_deviceInfo_t *info) { - nn_Alloc *alloc = &ctx->allocator; - - nn_deallocStr(alloc, info->address); - nn_dealloc(alloc, info->pairs, sizeof(nn_deviceInfoPair_t) * info->capacity); -} - -void nn_deleteDeviceInfoList(nn_deviceInfoList_t *list) { - for(nn_size_t i = 0; i < list->len; i++) { - nn_deleteDeviceInfo(&list->ctx, &list->info[i]); - } - - nn_Alloc alloc = list->ctx.allocator; - - nn_dealloc(&alloc, list->info, sizeof(nn_deviceInfo_t) * list->cap); - nn_dealloc(&alloc, list, sizeof(nn_deviceInfoList_t)); -} - -nn_deviceInfo_t *nn_addDeviceInfo(nn_deviceInfoList_t *list, nn_address address, nn_size_t maxKeys) { - if(list->len == list->cap) { - nn_size_t neededCap = list->cap; - if(neededCap < 1) neededCap = 1; - while(neededCap < (list->len + 1)) neededCap *= 2; - - nn_deviceInfo_t *newBuf = nn_resize(&list->ctx.allocator, list->info, sizeof(nn_deviceInfo_t) * list->cap, sizeof(nn_deviceInfo_t) * neededCap); - if(newBuf == NULL) return NULL; - list->cap = neededCap; - list->info = newBuf; - } - - nn_deviceInfoPair_t *pairs = nn_alloc(&list->ctx.allocator, sizeof(nn_deviceInfoPair_t) * maxKeys); - if(pairs == NULL) return NULL; - - nn_size_t i = list->len; - list->info[i] = (nn_deviceInfo_t) { - .alloc = list->ctx.allocator, - .pairs = pairs, - .len = 0, - .address = address == NULL ? nn_randomUUID(&list->ctx) : nn_strdup(&list->ctx.allocator, address), // TODO: handle OOM - .capacity = maxKeys, - }; - list->len++; - return list->info + i; -} - -void nn_removeDeviceInfo(nn_deviceInfoList_t *list, const char *address) { - nn_size_t j = 0; - for(nn_size_t i = 0; i < list->len; i++) { - if(nn_strcmp(list->info[i].address, address) == 0) { - nn_deleteDeviceInfo(&list->ctx, &list->info[i]); - } else { - list->info[j] = list->info[i]; - j++; - } - } - list->len = j; -} - -nn_bool_t nn_registerDeviceKey(nn_deviceInfo_t *deviceInfo, const char *key, const char *value) { - if(deviceInfo->len == deviceInfo->capacity) return false; - nn_size_t i = deviceInfo->len; - nn_Alloc *alloc = &deviceInfo->alloc; - // TODO: handle OOM - deviceInfo->pairs[i].key = nn_strdup(alloc, key); - deviceInfo->pairs[i].value = nn_strdup(alloc, value); - deviceInfo->len++; - return true; -} - -nn_deviceInfo_t *nn_getDeviceInfoAt(nn_deviceInfoList_t *list, nn_size_t idx) { - if(idx >= list->len) return NULL; - return &list->info[idx]; -} - -const char *nn_getDeviceInfoAddress(nn_deviceInfo_t *deviceInfo) { - return deviceInfo->address; -} - -nn_size_t nn_getDeviceCount(nn_deviceInfoList_t *list) { - return list->len; -} - -const char *nn_iterateDeviceInfoKeys(nn_deviceInfo_t *deviceInfo, nn_size_t idx, const char **value) { - if(idx >= deviceInfo->len) return NULL; - nn_deviceInfoPair_t pair = deviceInfo->pairs[idx]; - if(value != NULL) *value = pair.value; - return pair.key; -} - -nn_size_t nn_getDeviceKeyCount(nn_deviceInfo_t *deviceInfo) { - return deviceInfo->len; -} diff --git a/src/emulator.c b/src/emulator.c deleted file mode 100644 index 7b79f35..0000000 --- a/src/emulator.c +++ /dev/null @@ -1,1016 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "neonucleus.h" -#include "testLuaArch.h" -#include -#include - -#ifdef NN_BAREMETAL - -#ifdef NN_POSIX - -static double nni_realTime() { - struct timespec time; - if(clock_gettime(CLOCK_MONOTONIC, &time) < 0) return 0; // oh no - return time.tv_sec + ((double)time.tv_nsec) / 1e9; -} - -#else - -#include - -static double nni_realTime() { - LARGE_INTEGER frequency = {0}; - if(!QueryPerformanceFrequency(&frequency)) return 0; - - LARGE_INTEGER now = {0}; - if(!QueryPerformanceCounter(&now)) return 0; - - return (double)now.QuadPart / frequency.QuadPart; -} - -#endif - -static double nni_realTimeClock(void *_) { - return nni_realTime(); -} - -nn_Clock nn_libcRealTime() { - return (nn_Clock) { - .userdata = NULL, - .proc = nni_realTimeClock, - }; -} - -static void *nn_libcAllocProc(void *_, void *ptr, nn_size_t oldSize, nn_size_t newSize, void *__) { - if(newSize == 0) { - //printf("Freed %lu bytes from %p\n", oldSize, ptr); - free(ptr); - return NULL; - } else { - void *rptr = realloc(ptr, newSize); - //printf("Allocated %lu bytes for %p\n", newSize - oldSize, rptr); - return rptr; - } -} - -nn_Alloc nn_libcAllocator() { - return (nn_Alloc) { - .userdata = NULL, - .proc = nn_libcAllocProc, - }; -} - -static nn_size_t nni_rand(void *userdata) { - return rand(); -} - -nn_Rng nn_libcRng() { - srand(time(NULL)); - return (nn_Rng) { - .userdata = NULL, - .maximum = RAND_MAX, - .proc = nni_rand, - }; -} - -nn_Context nn_libcContext() { - return (nn_Context) { - .allocator = nn_libcAllocator(), - .clock = nn_libcRealTime(), - .lockManager = nn_noMutex(), - .rng = nn_libcRng(), - }; -} - -#endif - -Color ne_processColor(unsigned int color) { - color <<= 8; - color |= 0xFF; - return GetColor(color); -} - -nn_eepromControl ne_eeprom_ctrl = { - .readHeatPerByte = 0.0015, - .writeHeatPerByte = 0.03, - .readEnergyCostPerByte = 0.001, - .writeEnergyCostPerByte = 0.05, - .bytesReadPerTick = 32768, - .bytesWrittenPerTick = 4096, -}; - -void ne_eeprom_getLabel(void *_, char *buf, size_t *buflen, nn_errorbuf_t err) { - *buflen = 0; -} - -nn_size_t ne_eeprom_setLabel(void *_, const char *buf, size_t buflen, nn_errorbuf_t err) { - nn_error_write(err, "unsupported"); - return 0; -} - -char *ne_eeprom_getArchitecture(nn_Alloc *alloc, void *_, nn_errorbuf_t err) { - return NULL; -} - -void ne_eeprom_setArchitecture(void *_, const char *buf, nn_errorbuf_t err) { - nn_error_write(err, "unsupported"); -} - -const char *ne_location(nn_address address) { - static char buffer[256]; - snprintf(buffer, 256, "data/%s", address); - return buffer; -} - -size_t ne_eeprom_get(void *addr, char *buf, nn_errorbuf_t err) { - FILE *f = fopen(ne_location(addr), "rb"); - if (f == NULL) { - nn_error_write(err, strerror(errno)); - return 0; - } - fseek(f, 0, SEEK_END); - size_t len = ftell(f); - fseek(f, 0, SEEK_SET); - fread(buf, sizeof(char), len, f); - fclose(f); - return len; -} - -nn_bool_t ne_eeprom_set(void *addr, const char *buf, size_t len, nn_errorbuf_t err) { - FILE *f = fopen(ne_location(addr), "wb"); - if (f == NULL) { - nn_error_write(err, strerror(errno)); - return false; - } - fwrite(buf, sizeof(char), len, f); - fclose(f); - return true; -} - -nn_size_t ne_eeprom_getData(void *_, char *buf, nn_errorbuf_t err) { - return 0; -} - -nn_bool_t ne_eeprom_setData(void *_, const char *buf, size_t len, nn_errorbuf_t err) { - return true; -} - -nn_bool_t ne_eeprom_isReadonly(void *userdata, nn_errorbuf_t err) { - return false; -} - -nn_bool_t ne_eeprom_makeReadonly(void *userdata, nn_errorbuf_t err) { - nn_error_write(err, "unsupported"); - return false; -} - -nn_filesystemControl ne_fs_ctrl = { - .readBytesPerTick = 65536, - .writeBytesPerTick = 32768, - .removeFilesPerTick = 16, - .createFilesPerTick = 16, - - .readHeatPerByte = 0.00000015, - .writeHeatPerByte = 0.0000015, - .removeHeat = 0.035, - .createHeat = 0.045, - - .readEnergyPerByte = 0.0015, - .writeEnergyPerByte = 0.0035, - .removeEnergy = 0.135, - .createEnergy = 0.325, -}; - -void ne_fs_getLabel(void *_, char *buf, size_t *buflen) { - *buflen = 0; -} - -nn_size_t ne_fs_setLabel(void *_, const char *buf, size_t buflen) { - return 0; -} - -nn_bool_t ne_fs_isReadOnly(void *_, nn_errorbuf_t err) { - return false; -} - -size_t ne_fs_spaceUsed(void *_) { - return 0; // ultra accurate -} - -const char *ne_fs_diskPath(nn_address addr, const char *path) { - static char buf[256]; - const char *root = ne_location(addr); - if(path[0] == '/') { - snprintf(buf, 256, "%s%s", root, path); - } else { - snprintf(buf, 256, "%s/%s", root, path); - } - - return buf; -} - -void *ne_fs_open(nn_address address, const char *path, const char *mode, nn_errorbuf_t err) { - const char *trueMode = "rb"; - if(strcmp(mode, "w") == 0) { - trueMode = "wb"; - } - if(strcmp(mode, "a") == 0) { - trueMode = "ab"; - } - - const char *p = ne_fs_diskPath(address, path); - if(p[0] == '/') p++; - if(DirectoryExists(p)) { - nn_error_write(err, "Is a directory"); - return NULL; - } - FILE *f = fopen(p, trueMode); - if(f == NULL) { - nn_error_write(err, strerror(errno)); - return NULL; - } - setvbuf(f, NULL, _IONBF, BUFSIZ); - return f; -} - -bool ne_fs_close(nn_address addr, FILE *f, nn_errorbuf_t err) { - fclose(f); - return true; -} - -bool ne_fs_write(nn_address addr, FILE *f, const char *buf, size_t len, nn_errorbuf_t err) { - return fwrite(buf, sizeof(char), len, f) > 0; -} - -size_t ne_fs_read(nn_address addr, FILE *f, char *buf, size_t required, nn_errorbuf_t err) { - nn_size_t len = 0; - while(true) { - if(feof(f)) break; - if(len >= required) break; - len += fread(buf + len, 1, required - len, f); - } - return len; -} - -size_t ne_fs_seek(nn_address addr, FILE *f, const char *whence, int off, nn_errorbuf_t err) { - int w = SEEK_SET; - if(strcmp(whence, "cur") == 0) { - w = SEEK_CUR; - } - if(strcmp(whence, "end") == 0) { - w = SEEK_END; - } - if(fseek(f, off, w) != 0) { - nn_error_write(err, strerror(errno)); - } - return ftell(f); -} - -char **ne_fs_list(nn_Alloc *alloc, nn_address addr, const char *path, size_t *len, nn_errorbuf_t err) { - const char *p = ne_fs_diskPath(addr, path); - if(p[0] == '/') p++; - - FilePathList files = LoadDirectoryFiles(p); - *len = files.count; - - char **buf = nn_alloc(alloc, sizeof(char *) * files.count); - for(size_t i = 0; i < files.count; i++) { - buf[i] = nn_strdup(alloc, GetFileName(files.paths[i])); - } - - UnloadDirectoryFiles(files); - - return buf; -} - -size_t ne_fs_size(nn_address addr, const char *path, nn_errorbuf_t err) { - const char *p = ne_fs_diskPath(addr, path); - if(p[0] == '/') p++; - - if(DirectoryExists(p)) return 0; - - return GetFileLength(p); -} - -size_t ne_fs_lastModified(nn_address addr, const char *path, nn_errorbuf_t err) { - const char *p = ne_fs_diskPath(addr, path); - if(p[0] == '/') p++; - - return GetFileModTime(p); -} - -bool ne_fs_isDirectory(nn_address addr, const char *path, nn_errorbuf_t err) { - const char *p = ne_fs_diskPath(addr, path); - if(p[0] == '/') p++; - - return DirectoryExists(p); -} - -bool ne_fs_makeDirectory(nn_address addr, const char *path, nn_errorbuf_t err) { - const char *p = ne_fs_diskPath(addr, path); - if(p[0] == '/') p++; - - return MakeDirectory(p) == 0; -} - -bool ne_fs_exists(nn_address addr, const char *path, nn_errorbuf_t err) { - const char *p = ne_fs_diskPath(addr, path); - if(p[0] == '/') p++; - - return FileExists(p) || DirectoryExists(p); -} - -int keycode_to_oc(int keycode) { - switch (keycode) { - case KEY_NULL: - return 0; - case KEY_APOSTROPHE: - return 0x28; - case KEY_COMMA: - return 0x33; - case KEY_MINUS: - return 0x0C; - case KEY_PERIOD: - return 0x34; - case KEY_SLASH: - return 0x35; - case KEY_ZERO: - return 0x0B; - case KEY_ONE: - return 0x02; - case KEY_TWO: - return 0x03; - case KEY_THREE: - return 0x04; - case KEY_FOUR: - return 0x05; - case KEY_FIVE: - return 0x06; - case KEY_SIX: - return 0x07; - case KEY_SEVEN: - return 0x08; - case KEY_EIGHT: - return 0x09; - case KEY_NINE: - return 0x0A; - case KEY_SEMICOLON: - return 0x27; - case KEY_EQUAL: - return 0x0D; - case KEY_A: - return 0x1E; - case KEY_B: - return 0x30; - case KEY_C: - return 0x2E; - case KEY_D: - return 0x20; - case KEY_E: - return 0x12; - case KEY_F: - return 0x21; - case KEY_G: - return 0x22; - case KEY_H: - return 0x23; - case KEY_I: - return 0x17; - case KEY_J: - return 0x24; - case KEY_K: - return 0x25; - case KEY_L: - return 0x26; - case KEY_M: - return 0x32; - case KEY_N: - return 0x31; - case KEY_O: - return 0x18; - case KEY_P: - return 0x19; - case KEY_Q: - return 0x10; - case KEY_R: - return 0x13; - case KEY_S: - return 0x1F; - case KEY_T: - return 0x14; - case KEY_U: - return 0x16; - case KEY_V: - return 0x2F; - case KEY_W: - return 0x11; - case KEY_X: - return 0x2D; - case KEY_Y: - return 0x15; - case KEY_Z: - return 0x2C; - case KEY_LEFT_BRACKET: - return 0x1A; - case KEY_BACKSLASH: - return 0x2B; - case KEY_RIGHT_BRACKET: - return 0x1B; - case KEY_GRAVE: - return 0x29; - case KEY_SPACE: - return 0x39; - case KEY_ESCAPE: - return 0; - case KEY_ENTER: - return 0x1C; - case KEY_TAB: - return 0x0F; - case KEY_BACKSPACE: - return 0x0E; - case KEY_INSERT: - return 0xD2; - case KEY_DELETE: - return 0xD3; - case KEY_RIGHT: - return 0xCD; - case KEY_LEFT: - return 0xCB; - case KEY_DOWN: - return 0xD0; - case KEY_UP: - return 0xC8; - case KEY_PAGE_UP: - return 0xC9; - case KEY_PAGE_DOWN: - return 0xD1; - case KEY_HOME: - return 0xC7; - case KEY_END: - return 0xCF; - case KEY_CAPS_LOCK: - return 0x3A; - case KEY_SCROLL_LOCK: - return 0x46; - case KEY_NUM_LOCK: - return 0x45; - case KEY_PRINT_SCREEN: - return 0; - case KEY_PAUSE: - return 0xC5; - case KEY_F1: - return 0x3B; - case KEY_F2: - return 0x3C; - case KEY_F3: - return 0x3D; - case KEY_F4: - return 0x3E; - case KEY_F5: - return 0x3F; - case KEY_F6: - return 0x40; - case KEY_F7: - return 0x41; - case KEY_F8: - return 0x42; - case KEY_F9: - return 0x43; - case KEY_F10: - return 0x44; - case KEY_F11: - return 0x57; - case KEY_F12: - return 0x58; - case KEY_LEFT_SHIFT: - return 0x2A; - case KEY_LEFT_CONTROL: - return 0x1D; - case KEY_LEFT_ALT: - return 0x38; - case KEY_LEFT_SUPER: - return 0; - case KEY_RIGHT_SHIFT: - return 0x36; - case KEY_RIGHT_CONTROL: - return 0x9D; - case KEY_RIGHT_ALT: - return 0xB8; - case KEY_RIGHT_SUPER: - return 0; - case KEY_KB_MENU: - return 0; - case KEY_KP_0: - return 0x52; - case KEY_KP_1: - return 0x4F; - case KEY_KP_2: - return 0x50; - case KEY_KP_3: - return 0x51; - case KEY_KP_4: - return 0x4B; - case KEY_KP_5: - return 0x4C; - case KEY_KP_6: - return 0x4D; - case KEY_KP_7: - return 0x47; - case KEY_KP_8: - return 0x48; - case KEY_KP_9: - return 0x49; - case KEY_KP_DECIMAL: - return 0x54; - case KEY_KP_DIVIDE: - return 0xB5; - case KEY_KP_MULTIPLY: - return 0x37; - case KEY_KP_SUBTRACT: - return 0x4A; - case KEY_KP_ADD: - return 0x4E; - case KEY_KP_ENTER: - return 0x9C; - case KEY_KP_EQUAL: - return 0x8D; - case KEY_BACK: - return 0; - case KEY_MENU: - return 0; - case KEY_VOLUME_DOWN: - return 0; - case KEY_VOLUME_UP: - return 0; - } - return 0; -} - -typedef struct ne_premappedPixel { - int codepoint; - int mappedDepth; - int mappedFgFor; - int mappedFgRes; - int mappedBgFor; - int mappedBgRes; - nn_bool_t legacyColors; -} ne_premappedPixel; - -bool ne_legacyColors = false; - -ne_premappedPixel ne_getPremap(ne_premappedPixel *pixels, nn_screen *screen, int x, int y) { - int maxW, maxH; - nn_maxResolution(screen, &maxW, &maxH); - int depth = nn_getDepth(screen); - - int i = y * maxW + x; - ne_premappedPixel premapped = pixels[i]; - - nn_scrchr_t pixel = nn_getPixel(screen, x, y); - int fg = pixel.fg; - int bg = pixel.bg; - - if(premapped.legacyColors != ne_legacyColors) { - premapped.legacyColors = ne_legacyColors; - premapped.mappedDepth = -1; - } - - if(premapped.mappedDepth != depth) { - premapped.mappedDepth = depth; - premapped.mappedFgFor = -1; - premapped.mappedBgFor = -1; - } - - bool miss = false; - - if(premapped.mappedFgFor != fg) { - premapped.mappedFgFor = fg; - premapped.mappedFgRes = nn_mapDepth(fg, depth, ne_legacyColors); - miss = true; - } - if(premapped.mappedBgFor != bg) { - premapped.mappedBgFor = bg; - premapped.mappedBgRes = nn_mapDepth(bg, depth, ne_legacyColors); - miss = true; - } - premapped.codepoint = pixel.codepoint; - - pixels[i] = premapped; - return premapped; -} - -ne_premappedPixel *ne_allocPremap(int width, int height) { - int len = width * height; - ne_premappedPixel *pixels = malloc(sizeof(ne_premappedPixel) * len); - for(int i = 0; i < len; i++) pixels[i] = (ne_premappedPixel) { - .mappedDepth = -1, - .mappedFgFor = -1, - .mappedBgFor = -1, - }; - return pixels; -} - -typedef struct ne_pressedKey { - int charcode; - int keycode; - bool repeat; -} ne_pressedKey; - -void ne_log(void *_, void *__, nn_component *component, nn_computer *computer) { - const char *s = nn_toCString(nn_getArgument(computer, 0)); - if(s) { - printf("Sandbox: %s\n", s); - } -} - -int main(int argc, char **argv) { - printf("Setting up universe\n"); - nn_Context ctx = nn_libcContext(); - nn_Alloc alloc = ctx.allocator; - nn_universe *universe = nn_newUniverse(ctx); - if(universe == NULL) { - printf("Failed to create universe\n"); - return 1; - } - nn_loadCoreComponentTables(universe); - - nn_architecture *arch = testLuaArch_getArchitecture("src/sandbox.lua"); - assert(arch != NULL && "Loading architecture failed"); - - // 1MB of RAM, 16 components max - nn_computer *computer = nn_newComputer(universe, "testMachine", arch, NULL, 4*1024*1024, 16); - nn_setEnergyInfo(computer, 5000, 5000); - nn_setCallBudget(computer, 1*1024*1024); - nn_addSupportedArchitecture(computer, arch); - - // sandbox shit - nn_componentTable *sandboxTable = nn_newComponentTable(&alloc, "sandbox", NULL, NULL, NULL); - nn_defineMethod(sandboxTable, "log", ne_log, "log(msg: string) - Basic logging"); - - nn_newComponent(computer, NULL, -1, sandboxTable, NULL); - - nn_eepromTable genericEEPROMTable = { - .userdata = "luaBios.lua", - .deinit = NULL, - .size = 4096, - .dataSize = 1024, - .getLabel = ne_eeprom_getLabel, - .setLabel = ne_eeprom_setLabel, - .get = ne_eeprom_get, - .set = ne_eeprom_set, - .getData = ne_eeprom_getData, - .setData = ne_eeprom_setData, - .getArchitecture = ne_eeprom_getArchitecture, - .setArchitecture = ne_eeprom_setArchitecture, - .isReadonly = ne_eeprom_isReadonly, - .makeReadonly = ne_eeprom_makeReadonly, - }; - - nn_eeprom *genericEEPROM = nn_newEEPROM(&ctx, genericEEPROMTable, ne_eeprom_ctrl); - - nn_addEEPROM(computer, NULL, 0, genericEEPROM); - - nn_address fsFolder = argc > 1 ? argv[1] : "OpenOS"; - nn_filesystemTable genericFSTable = { - .userdata = fsFolder, - .deinit = NULL, - .getLabel = ne_eeprom_getLabel, - .setLabel = ne_eeprom_setLabel, - .spaceUsed = ne_fs_spaceUsed, - .spaceTotal = 1*1024*1024, - .isReadOnly = ne_fs_isReadOnly, - .size = (void *)ne_fs_size, - .remove = NULL, - .lastModified = (void *)ne_fs_lastModified, - .rename = NULL, - .exists = (void *)ne_fs_exists, - .isDirectory = (void *)ne_fs_isDirectory, - .makeDirectory = (void *)ne_fs_makeDirectory, - .list = (void *)ne_fs_list, - .open = (void *)ne_fs_open, - .close = (void *)ne_fs_close, - .write = (void *)ne_fs_write, - .read = (void *)ne_fs_read, - .seek = (void *)ne_fs_seek, - }; - nn_filesystem *genericFS = nn_newFilesystem(&ctx, genericFSTable, ne_fs_ctrl); - nn_addFileSystem(computer, NULL, 1, genericFS); - - nn_vfilesystemImageNode tmpfsImg[] = { - (nn_vfilesystemImageNode) { - .name = "testScript.lua", - .data = "print('Hello, world!')", - .len = nn_strlen("print('Hello, world!')"), - }, - }; - - nn_vfilesystemOptions tmpfsOpts = { - .isReadOnly = false, - .capacity = 64*1024, - .label = "tmpfs", - .labelLen = 5, - .creationTime = 0, // we are at the start of time - .maxDirEntries = 64, - .image = tmpfsImg, - .rootEntriesInImage = 1, - }; - - nn_filesystem *tmpFS = nn_volatileFilesystem(&ctx, tmpfsOpts, ne_fs_ctrl); - nn_component *tmpfsComp = nn_addFileSystem(computer, NULL, 2, tmpFS); - - nn_setTmpAddress(computer, nn_getComponentAddress(tmpfsComp)); - - nn_vdriveOptions vdriveOpts = { - .sectorSize = 512, - .capacity = 1*1024*1024, - .platterCount = 1, - .labelLen = 0, - .data = NULL, - }; - nn_driveControl vdriveCtrl = { - .readSectorsPerTick = 32768, - .writeSectorsPerTick = 16384, - .seekSectorsPerTick = 8192, - .readHeatPerSector = 0.0015, - .writeHeatPerSector = 0.015, - .motorHeatPerSector = 0.000005, - .readEnergyPerSector = 0.0015, - .writeEnergyPerSector = 0.015, - .motorEnergyPerSector = 0.00005, - }; - nn_drive *genericDrive = nn_volatileDrive(&ctx, vdriveOpts, vdriveCtrl); - nn_addDrive(computer, NULL, 4, genericDrive); - - nn_networkControl netCtrl = { - .energyPerFullPacket = 0, - .heatPerFullPacket = 0, - .packetBytesPerTick = 16384, - }; - - nn_debugLoopbackNetworkOpts netOpts = { - .computer = computer, - .address = "loop", - .isWireless = false, - .maxValues = 8, - .maxOpenPorts = 64, - .maxPacketSize = 8192, - .maxStrength = 200, - }; - - nn_tunnel *t = nn_debugLoopbackTunnel(&ctx, netOpts, netCtrl); - nn_addTunnel(computer, NULL, -1, t); - - int maxWidth = 80, maxHeight = 32; - - nn_screen *s = nn_newScreen(&ctx, maxWidth, maxHeight, 24, 16, 256); - nn_setDepth(s, 8); // looks cool - nn_addKeyboard(s, "shitty keyboard"); - nn_mountKeyboard(computer, "shitty keyboard", 2); - nn_addScreen(computer, NULL, 2, s); - - ne_premappedPixel *premap = ne_allocPremap(maxWidth, maxHeight); - - nn_gpuControl gpuCtrl = { - .totalVRAM = 16*1024, - .maximumBufferCount = 64, // probably too many - .defaultBufferWidth = maxWidth, - .defaultBufferHeight = maxHeight, - - .screenCopyPerTick = 8, - .screenFillPerTick = 16, - .screenSetsPerTick = 32, - .bitbltPerTick = 8, - - .heatPerPixelChange = 0.00005, - .heatPerPixelReset = 0.00001, - .heatPerVRAMChange = 0.00000015, - - .energyPerPixelChange = 0.05, - .energyPerPixelReset = 0.01, - .energyPerVRAMChange = 0.0015, - }; - - nn_addGPU(computer, NULL, 3, &gpuCtrl); - - SetConfigFlags(FLAG_WINDOW_RESIZABLE); - InitWindow(800, 600, "emulator"); - - Font unscii = LoadFont("unscii-16-full.ttf"); - - static ne_pressedKey release_check_list[256]; - memset(release_check_list, 0, sizeof(ne_pressedKey)*256); - uint8_t release_check_ptr; - - SetExitKey(KEY_NULL); - - double idleTime = 0; - int tps = 20; // mc TPS - double interval = 1.0/tps; - double totalTime = 0; - - nn_deviceInfoList_t *list = nn_getComputerDeviceInfoList(computer); - - nn_deviceInfo_t *theRamStick = nn_addDeviceInfo(list, NULL, 8); - nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_CLASS, NN_DEVICEINFO_CLASS_RAM); - nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_CAPACITY, "4MiB"); - nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_DESCRIPTION, "Volatile storage device"); - nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_PRODUCT, "RAM 1:5"); - nn_registerDeviceKey(theRamStick, NN_DEVICEINFO_KEY_VENDOR, "NeoComputers Technologies L.L.C."); - - nn_addDeviceInfo(list, "test", 8); - nn_removeDeviceInfo(list, "test"); - - nn_deviceInfo_t *theCPU = nn_addDeviceInfo(list, NULL, 8); - nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_CLASS, NN_DEVICEINFO_CLASS_CPU); - nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_DESCRIPTION, "Processor"); - nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_PRODUCT, "Cryzen 9 6900XXL"); - nn_registerDeviceKey(theCPU, NN_DEVICEINFO_KEY_VENDOR, "NeoComputers Technologies L.L.C."); - - while(true) { - if(WindowShouldClose()) break; - nn_setEnergyInfo(computer, 5000, 5000); - - double dt = GetFrameTime(); - - totalTime += dt; - - double heat = nn_getTemperature(computer); - double roomHeat = nn_getRoomTemperature(computer); - - double tx = 0.1; - - // remove some heat per second - nn_removeHeat(computer, dt * (rand() % 3) * tx * (heat - roomHeat)); - if(nn_isOverheating(computer)) { - TraceLog(LOG_WARNING, "%3.2lf OVERHEATING", totalTime); - goto render; - } - - while (true) { // TODO: find out if we can check if the keycode and unicode are for the same key event or not - int keycode = GetKeyPressed(); - int unicode = GetCharPressed(); - - if (keycode == 0 && unicode == 0) { - break; - } - - if (keycode != 0) { - if(keycode == KEY_ENTER) { - unicode = '\r'; - } - if(keycode == KEY_BACKSPACE) { - unicode = '\b'; - } - if(keycode == KEY_TAB) { - unicode = '\t'; - } - - release_check_list[release_check_ptr].keycode = keycode; - release_check_list[release_check_ptr].charcode = unicode; - release_check_list[release_check_ptr].repeat = false; - release_check_ptr++; - } - - nn_value values[5]; - - values[0] = nn_values_cstring("key_down"); - values[1] = nn_values_cstring("shitty keyboard"); - values[2] = nn_values_integer(unicode); - values[3] = nn_values_integer(keycode_to_oc(keycode)); - values[4] = nn_values_cstring("USER"); - - const char* error = nn_pushSignal(computer, values, 5); - - if (error != NULL) { - // well fuck - printf("error happened when eventing the keyboarding: %s\n", error);;;;;; - } - } - - for (int i = 0; i < 256; i++) { - ne_pressedKey *key = release_check_list + i; - if (key->keycode != 0) { - key->repeat = IsKeyPressedRepeat(key->keycode); - if (IsKeyReleased(key->keycode)) { - // omg - nn_value values[5]; - values[0] = nn_values_cstring("key_up"); - values[1] = nn_values_cstring("shitty keyboard"); - values[2] = nn_values_integer(key->charcode); - values[3] = nn_values_integer(keycode_to_oc(key->keycode)); - values[4] = nn_values_cstring("USER"); - - const char* error = nn_pushSignal(computer, values, 5); - - if (error != NULL) { - // well fuck - printf("error happened when eventing the keyboarding: %s\n", error);;;;;; - } - key->keycode = 0; - } - } - } - - idleTime += dt; - - if(idleTime >= interval) { - idleTime -= interval; - - for (int i = 0; i < 256; i++) { - ne_pressedKey *key = release_check_list + i; - if (key->keycode != 0) { - if (key->repeat) { - // omg - nn_value values[5]; - values[0] = nn_values_cstring("key_down"); - values[1] = nn_values_cstring("shitty keyboard"); - values[2] = nn_values_integer(key->charcode); - values[3] = nn_values_integer(keycode_to_oc(key->keycode)); - values[4] = nn_values_cstring("USER"); - - const char* error = nn_pushSignal(computer, values, 5); - - if (error != NULL) { - // well fuck - printf("error happened when eventing the keyboarding: %s\n", error);;;;;; - } - } - } - } - - int state = nn_tickComputer(computer); - if(state == NN_STATE_SWITCH) { - nn_architecture *nextArch = nn_getNextArchitecture(computer); - printf("Next architecture: %s\n", nextArch->archName); - break; - } else if(state == NN_STATE_CLOSING || state == NN_STATE_REPEAT) { - break; - } else if(state == NN_STATE_BLACKOUT) { - printf("blackout\n"); - break; - } - const char *e = nn_getError(computer); - if(e != NULL) { - printf("Error: %s\n", e); - break; - } - } - -render: - if(IsKeyPressed(KEY_F1)) { - nn_setDepth(s, 1); - } - if(IsKeyPressed(KEY_F2)) { - nn_setDepth(s, 4); - } - if(IsKeyPressed(KEY_F3)) { - nn_setDepth(s, 8); - } - if(IsKeyPressed(KEY_F4)) { - ne_legacyColors = !ne_legacyColors; - } - - BeginDrawing(); - - ClearBackground(BLACK); - - if(nn_isOn(s)) { - int scrW = 1, scrH = 1; - nn_getResolution(s, &scrW, &scrH); - int pixelHeight = GetScreenHeight() / scrH; - float spacing = (float)pixelHeight/10; - int pixelWidth = MeasureTextEx(unscii, "A", pixelHeight, spacing).x; - - int depth = nn_getDepth(s); - - int offX = (GetScreenWidth() - scrW * pixelWidth) / 2; - int offY = (GetScreenHeight() - scrH * pixelHeight) / 2; - - for(size_t x = 0; x < scrW; x++) { - for(size_t y = 0; y < scrH; y++) { - ne_premappedPixel p = ne_getPremap(premap, s, x, y); - - // fuck palettes - Color fgColor = ne_processColor(p.mappedFgRes); - Color bgColor = ne_processColor(p.mappedBgRes); - DrawRectangle(x * pixelWidth + offX, y * pixelHeight + offY, pixelWidth, pixelHeight, bgColor); - DrawTextCodepoint(unscii, p.codepoint, (Vector2) {x * pixelWidth + offX, y * pixelHeight + offY}, pixelHeight - 5, fgColor); - } - } - } - - EndDrawing(); - } - - // destroy - nn_deleteComputer(computer); - nn_unsafeDeleteUniverse(universe); - CloseWindow(); - free(premap); - return 0; -} diff --git a/src/emulator/.gitkeep b/src/emulator/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/lock.c b/src/lock.c deleted file mode 100644 index 0790c1c..0000000 --- a/src/lock.c +++ /dev/null @@ -1,86 +0,0 @@ -#include "neonucleus.h" - -#ifndef NN_BAREMETAL -#include "tinycthread.h" - -static nn_bool_t nni_libcLock(void *_, mtx_t *lock, int action, int flags) { - if(action == NN_LOCK_INIT) { - mtx_init(lock, mtx_recursive); - } else if(action == NN_LOCK_DEINIT) { - mtx_destroy(lock); - } else if(action == NN_LOCK_RETAIN) { - if(flags & NN_LOCK_IMMEDIATE) { - return mtx_trylock(lock) == thrd_success; - } - return mtx_lock(lock); - } else if(action == NN_LOCK_RELEASE) { - mtx_unlock(lock); - } - return NN_TRUE; -} - -nn_LockManager nn_libcMutex(void) { - nn_LockManager mgr = {}; - mgr.lockSize = sizeof(mtx_t); - mgr.userdata = NULL; - mgr.proc = (nn_LockProc *)nni_libcLock; - return mgr; -} - -#endif - -static nn_bool_t nni_noLock(void *_, void *__, int action, int flags) { - return NN_TRUE; -} - -nn_LockManager nn_noMutex(void) { - return (nn_LockManager) { - .userdata = NULL, - .lockSize = 0, - .proc = (nn_LockProc *)nni_noLock, - }; -} - -nn_guard *nn_newGuard(nn_Context *ctx) { - nn_guard *g = nn_alloc(&ctx->allocator, ctx->lockManager.lockSize); - if(g == NULL) return NULL; - ctx->lockManager.proc(ctx->lockManager.userdata, g, NN_LOCK_INIT, NN_LOCK_DEFAULT); - return g; -} - -void nn_lock(nn_Context *context, nn_guard *guard) { - if(guard == NULL) return; - context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_RETAIN, NN_LOCK_DEFAULT); -} - -nn_bool_t nn_tryLock(nn_Context *context, nn_guard *guard) { - if(guard == NULL) return NN_TRUE; - return context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_RETAIN, NN_LOCK_IMMEDIATE); -} - -void nn_unlock(nn_Context *context, nn_guard *guard) { - if(guard == NULL) return; - context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_RELEASE, NN_LOCK_DEFAULT); -} - -void nn_deleteGuard(nn_Context *context, nn_guard *guard) { - if(guard == NULL) return; - context->lockManager.proc(context->lockManager.userdata, guard, NN_LOCK_DEINIT, NN_LOCK_DEFAULT); - nn_dealloc(&context->allocator, guard, context->lockManager.lockSize); -} - -void nn_addRef(nn_refc *refc, nn_size_t count) { - (*refc) += count; -} - -void nn_incRef(nn_refc *refc) { - nn_addRef(refc, 1); -} - -nn_bool_t nn_removeRef(nn_refc *refc, nn_size_t count) { - return ((*refc) -= count) == 0; -} - -nn_bool_t nn_decRef(nn_refc *refc) { - return nn_removeRef(refc, 1); -} diff --git a/rewrite/luaarch.c b/src/luaarch.c similarity index 100% rename from rewrite/luaarch.c rename to src/luaarch.c diff --git a/rewrite/machine.lua b/src/machine.lua similarity index 100% rename from rewrite/machine.lua rename to src/machine.lua diff --git a/rewrite/main.c b/src/main.c similarity index 99% rename from rewrite/main.c rename to src/main.c index 07a8c99..d563b6d 100644 --- a/rewrite/main.c +++ b/src/main.c @@ -281,6 +281,7 @@ ne_FsState *ne_newFS(const char *path, bool readonly) { } sprintf(fs->path, "data%c%s", NE_PATHSEP, path); fs->isReadonly = readonly; + fs->dir = NULL; return fs; } @@ -1236,9 +1237,9 @@ cleanup:; nn_destroyComputer(c); nn_destroyComponentState(sandstate); nn_destroyComponentState(etype); + nn_destroyComponentState(gputype); nn_destroyComponentState(scrtype); nn_destroyComponentState(keytype); - nn_destroyComponentState(gputype); nn_destroyComponentState(vdriveState); for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]); ne_dropScreenBuf(scrbuf); diff --git a/rewrite/minBIOS.lua b/src/minBIOS.lua similarity index 100% rename from rewrite/minBIOS.lua rename to src/minBIOS.lua diff --git a/rewrite/neonucleus.c b/src/neonucleus.c similarity index 99% rename from rewrite/neonucleus.c rename to src/neonucleus.c index 1198998..eb04bec 100644 --- a/rewrite/neonucleus.c +++ b/src/neonucleus.c @@ -1012,7 +1012,7 @@ size_t nn_getTotalMemory(nn_Computer *computer) { } size_t nn_getFreeMemory(nn_Computer *computer) { - if(computer->state == NN_BOOTUP) return 0; + if(computer->state == NN_BOOTUP) return computer->totalMemory; nn_ArchitectureRequest req; req.computer = computer; req.action = NN_ARCH_FREEMEM; @@ -1031,6 +1031,45 @@ double nn_getUptime(nn_Computer *computer) { return nn_currentTime(&computer->universe->ctx) - computer->creationTimestamp; } +nn_Exit nn_deserializeComputer(nn_Computer *computer, const char *buf, size_t buflen) { + nn_ArchitectureRequest req; + req.computer = computer; + req.action = NN_ARCH_DESERIALIZE; + req.globalState = computer->arch.state; + req.localState = computer->archState; + req.memIn = buf; + req.memLen = buflen; + + return computer->arch.handler(&req); +} + +nn_Exit nn_serializeComputer(nn_Computer *computer, char **buf, size_t *buflen) { + nn_ArchitectureRequest req; + req.computer = computer; + req.action = NN_ARCH_SERIALIZE; + req.globalState = computer->arch.state; + req.localState = computer->archState; + + nn_Exit e = computer->arch.handler(&req); + if(e) return e; + + *buf = req.memOut; + *buflen = req.memLen; + + return NN_OK; +} + +nn_Exit nn_freeSerializedComputer(nn_Computer *computer, char *buf, size_t buflen) { + nn_ArchitectureRequest req; + req.computer = computer; + req.action = NN_ARCH_DROPSERIALIZED; + req.globalState = computer->arch.state; + req.localState = computer->archState; + req.memOut = buf; + req.memLen = buflen; + + return computer->arch.handler(&req); +} void nn_setError(nn_Computer *computer, const char *s) { nn_setLError(computer, s, nn_strlen(s)); diff --git a/src/neonucleus.h b/src/neonucleus.h index c32414f..ddb31ec 100644 --- a/src/neonucleus.h +++ b/src/neonucleus.h @@ -1,85 +1,18 @@ #ifndef NEONUCLEUS_H #define NEONUCLEUS_H -#ifndef NULL #ifdef __cplusplus -#define NULL nullptr -#else -#define NULL ((void *)0) -#endif +extern "C" { #endif -#ifdef NN_BAREMETAL -#ifdef NN_BIT32 - typedef int nn_intptr_t; - typedef unsigned int nn_size_t; -#else - typedef __INTPTR_TYPE__ nn_intptr_t; - typedef __SIZE_TYPE__ nn_size_t; -#endif -#else -#include - -#include -#include - -typedef intptr_t nn_intptr_t; -typedef size_t nn_size_t; -#endif - -#ifdef bool -typedef bool nn_bool_t; -#else -typedef unsigned char nn_bool_t; -#define bool nn_bool_t -#endif - -#ifdef true - -#define NN_TRUE true - -#else - -#define NN_TRUE 1 -#define true NN_TRUE - -#endif - -#ifdef false -#define NN_FALSE false -#else - -#define NN_FALSE 0 -#define false NN_FALSE - -#endif - -typedef long long nn_integer_t; - +// 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) - #ifdef _WIN64 - #define NN_WINDOWS - #else - #error "Windows 32-bit is not supported" - #endif + #define NN_WINDOWS #elif __APPLE__ - #include - #if TARGET_IPHONE_SIMULATOR - #error "iPhone Emulators are not supported" - #elif TARGET_OS_MACCATALYST - // I guess? - #define NN_MACOS - #elif TARGET_OS_IPHONE - #error "iPhone are not supported" - #elif TARGET_OS_MAC - #define NN_MACOS - #else - #error "Unknown Apple platform" - #endif -#elif __ANDROID__ - #error "Android is not supported" + #define NN_MACOS #elif __linux__ #define NN_LINUX #endif @@ -93,1069 +26,1680 @@ typedef long long nn_integer_t; #define NN_POSIX #endif +// every C standard header we depend on, conveniently put here +#include // for NULL, +#include // for intptr_t +#include // for true, false and bool -#ifdef __cplusplus -extern "C" { -#endif +// Internally we need stdatomic.h and, if NN_BAREMETAL is not defined, stdlib.h and time.h -// The entire C API, in one header +// The entire NeoNucleus API, in one header file -// Magic limits -// If your component needs more than these, redesign your API. +// Internal limits or constants -#define NN_MAX_ARGS 32 -#define NN_MAX_RETS 32 -#define NN_MAX_METHODS 32 -#define NN_MAX_USERS 128 -#define NN_MAX_ARCHITECTURES 16 -#define NN_MAX_SIGNALS 128 -#define NN_MAX_SIGNAL_VALS 32 -#define NN_MAX_USERDATA 1024 -#define NN_MAX_USER_SIZE 128 -#define NN_MAX_SIGNAL_SIZE 8192 -#define NN_MAX_OPEN_FILES 128 -#define NN_MAX_SCREEN_KEYBOARDS 64 +#define NN_KiB (1024) +#define NN_MiB (1024 * NN_KiB) +#define NN_GiB (1024 * NN_MiB) +#define NN_TiB (1024 * NN_TiB) + +// the alignment an allocation should have +#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, including the NULL terminator! #define NN_MAX_PATH 256 -#define NN_PORT_MAX 65535 +// 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. +#define NN_MAX_READ 65536 +// the maximum size of a label +#define NN_MAX_LABEL 256 +// maximum size of a wakeup message #define NN_MAX_WAKEUPMSG 2048 -#define NN_MAX_CHANNEL_SIZE 256 +// the maximum amount of file descriptors that can be open simultaneously +#define NN_MAX_OPENFILES 128 +// the maximum amount of userdata that can be sent simultaneously. +#define NN_MAX_USERDATA 64 +// maximum size of a signal, computed the same as modem packet costs. +#define NN_MAX_SIGNALSIZE 8192 +// maximum amount of signals. +#define NN_MAX_SIGNALS 128 +// the maximum value of a port. Ports start at 1. +#define NN_MAX_PORT 65535 +// the magic port number to close all ports +#define NN_CLOSEPORTS 0 +// maximum amount of architectures one machine can support. +#define NN_MAX_ARCHITECTURES 32 +// maximum size of the architecture name EEPROMs can store +#define NN_MAX_ARCHNAME 64 +// maximum size of an address. +// This only matters in places where an address is returned through a component, as it is the amount of space to allocate for the response. +// Past this there would be a truncation which would invalidate the address. +// However, 256 is unrealistically long, as UUIDv4 only needs 36. +// Please, do not go above this. +#define NN_MAX_ADDRESS 256 +// the port used by tunnel cards. This port is invalid for modems. #define NN_TUNNEL_PORT 0 -#define NN_PORT_CLOSEALL 0 -#define NN_MAX_CONCURRENT_RESOURCES 64 -#define NN_NULL_RESOURCE 0 +// maximum amount of users a computer can have +#define NN_MAX_USERS 64 +// maximum length of a username +#define NN_MAX_USERNAME 128 -#define NN_OVERHEAT_MIN 100 -#define NN_CALL_HEAT 0.05 -#define NN_CALL_COST 1 -#define NN_LABEL_SIZE 128 +// the maximum size of a UTF-8 character +#define NN_MAX_UNICODE_BUFFER 4 -#define NN_MAXIMUM_UNICODE_BUFFER 4 -#define NN_MAX_ERROR_BUFFER 128 +// the maximum size of a component error message. If the error is bigger than this, +// it is truncated. +#define NN_MAX_ERROR_SIZE 1024 -typedef struct nn_guard nn_guard; -#ifdef __STDC_NO_ATOMICS__ -typedef size_t nn_refc; -#else -typedef _Atomic(nn_size_t) nn_refc; -#endif -typedef struct nn_universe nn_universe; -typedef struct nn_computer nn_computer; -typedef struct nn_component nn_component; -typedef struct nn_componentTable nn_componentTable; +// unicode (UTF-8) support library -typedef unsigned long long nn_timestamp_t; +typedef unsigned int nn_codepoint; -// A non-zero malloc is a null ptr, with a 0 oldSize, but a non-0 newSize. -// A zero malloc is never called, the proc address itself is returned, which is ignored when freeing. -// A free is a non-null ptr, with a non-zero oldSize, but a newSize of 0. -// A realloc is a non-null ptr, with a non-zero oldSize, and a non-zero newSize. -typedef void *nn_AllocProc(void *userdata, void *ptr, nn_size_t oldSize, nn_size_t newSize, void *extra); +bool nn_unicode_validate(const char *s, size_t len); +// validates only the *first* codepoint in the NULL-terminated string. +// This returns the length in bytes of the codepoint, with 0 meaning +// invalid. +size_t nn_unicode_validateFirstChar(const char *s, size_t len); -typedef struct nn_Alloc { - void *userdata; - nn_AllocProc *proc; -} nn_Alloc; +// returns the amount of unicode codepoints in the UTF-8 string. +// Undefined behavior for invalid UTF-8, make sure to validate it if needed. +size_t nn_unicode_len(const char *s, size_t len); +// returns the amount of unicode codepoints in the UTF-8 string. +// If s is invalid UTF-8, all invalid bytes are considered a 1-byte codepoint. +size_t nn_unicode_lenPermissive(const char *s, size_t len); -typedef struct nn_architecture { - void *userdata; - const char *archName; - void *(*setup)(nn_computer *computer, void *userdata); - void (*teardown)(nn_computer *computer, void *state, void *userdata); - nn_size_t (*getMemoryUsage)(nn_computer *computer, void *state, void *userdata); - void (*tick)(nn_computer *computer, void *state, void *userdata); - /* Pointer returned should be allocated with nn_malloc or nn_realloc, so it can be freed with nn_free */ - char *(*serialize)(nn_computer *computer, nn_Alloc *alloc, void *state, void *userdata, nn_size_t *len); - void (*deserialize)(nn_computer *computer, const char *data, nn_size_t len, void *state, void *userdata); -} nn_architecture; -typedef char *nn_address; +// Writes the codepoints of s into codepoints. +// Undefined behavior for invalid UTF-8, make sure to validate it if needed. +// The codepoints buffer must be big enough to store the string, use nn_unicode_len() +// to get the required buffer length. +void nn_unicode_codepoints(const char *s, size_t len, nn_codepoint *codepoints); +// Writes the codepoints of s into codepoints. +// If s is invalid UTF-8, all invalid bytes are considered a 1-byte codepoint. +// The codepoints buffer must be big enough to store the string, use nn_unicode_lenPermissive() +// to get the required buffer length. +void nn_unicode_codepointsPermissive(const char *s, size_t len, nn_codepoint *codepoints); -#define NN_LOCK_DEFAULT 0 -#define NN_LOCK_IMMEDIATE 1 +// Returns the first codepoint from a UTF-8 string. +// If s is invalid UTF-8, the behavior is undefined. +nn_codepoint nn_unicode_firstCodepoint(const char *s); +// Returns the size, in bytes, required by UTF-8 for a codepoint. +size_t nn_unicode_codepointSize(nn_codepoint codepoint); +// Writes the UTF-8 bytes for a given codepoint into buffer. +// It does NOT write a NULL terminator, but it does return the length. +size_t nn_unicode_codepointToChar(char buffer[NN_MAX_UNICODE_BUFFER], nn_codepoint codepoint); +// the width, on a screen, for a codepoint. +// This matters for emojies. +size_t nn_unicode_charWidth(nn_codepoint codepoint); +// The width, on a screen, for an entire string. +// The behavior is undefined for +size_t nn_unicode_wlen(const char *s, size_t len); +size_t nn_unicode_wlenPermissive(const char *s, size_t len); -#define NN_LOCK_INIT 0 -#define NN_LOCK_DEINIT 1 -#define NN_LOCK_RETAIN 2 -#define NN_LOCK_RELEASE 3 +// Returns the amount of bytes needed to store the UTF-8 encoded text. +// The behavior on invalid codepoints is undefined. +size_t nn_unicode_countBytes(nn_codepoint *codepoints, size_t len); +// Writes the UTF-8 encoded text. +// DOES NOT WRITE A NULL TERMINATOR. +// s must be big enough to store the string, use nn_unicode_bytelen() +// to allocate the correct amount of space. +// The behavior on invalid codepoints is undefined. +void nn_unicode_writeBytes(char *s, nn_codepoint *codepoints, size_t len); -typedef nn_bool_t nn_LockProc(void *userdata, void *lock, int action, int flags); +// Returns the uppercase version of the codepoint +nn_codepoint nn_unicode_upper(nn_codepoint codepoint); +// Returns the lowercase version of the codepoint +nn_codepoint nn_unicode_lower(nn_codepoint codepoint); -typedef struct nn_LockManager { - void *userdata; - nn_size_t lockSize; - nn_LockProc *proc; -} nn_LockManager; +// The type of a the function used as the allocator. +// The expected behavior is as follows: +// alloc(state, NULL, 0, newSize) -> malloc(newSize) +// alloc(state, memory, oldSize, 0) -> free(memory) +// alloc(state, memory, oldSize, newSize) -> realloc(memory, newSize) +// +// NeoNucleus will ensure oldSize is what the application requested on the last allocation. +// This is useful for allocators which may not do extensive bookkeeping. +// In the case of Out Of Memory, you are expected to return NULL. +typedef void *nn_AllocProc(void *state, void *memory, size_t oldSize, size_t newSize); -typedef double nn_ClockProc(void *userdata); +// Meant to return the time, in seconds, since some epoch. +typedef double nn_TimeProc(void *state); -typedef struct nn_Clock { - void *userdata; - nn_ClockProc *proc; -} nn_Clock; +typedef size_t nn_RngProc(void *state); -typedef nn_size_t nn_RngProc(void *userdata); +typedef enum nn_LockAction { + // create the mutex + NN_LOCK_CREATE, + // destroy the mutex + NN_LOCK_DESTROY, + // lock the mutex + NN_LOCK_LOCK, + // unlock the mutex + NN_LOCK_UNLOCK, +} nn_LockAction; -typedef struct nn_Rng { - void *userdata; - nn_size_t maximum; - nn_RngProc *proc; -} nn_Rng; +typedef struct nn_LockRequest { + // mutate it for NN_LOCK_INIT + void *lock; + nn_LockAction action; +} nn_LockRequest; -nn_size_t nn_rand(nn_Rng *rng); -// returns from 0 to 1 (inclusive) -double nn_randf(nn_Rng *rng); -// returns from 0 to 1 (exclusive) -double nn_randfe(nn_Rng *rng); +// Intended for a plain mutex. +// This is used for synchronization. OpenComputers achieves synchronization +// between the worker threads by sending them as requests to a central thread (indirect methods). +// In NeoNucleus, the function pointer is invoked on the calling thead. This technically makes all methods direct, +// however methods which are meant to be slow may become indirect, as indirect methods consume the entire call budget. +// Do note that locks are only used in "full" component implementations, such as the volatile storage devices. +// The interfaces do not do any automatic synchronization via locks, all synchronization is assumed +// to be handled in the implementer of the interface, because only you know how to best synchronize +// it with the outside world. +typedef void nn_LockProc(void *state, nn_LockRequest *req); +// The *context* NeoNucleus is operating in. +// This determines: +// - How memory is allocated +// - How random numbers are generated and what the range is +// - What the current time is +// - How locks work typedef struct nn_Context { - nn_Alloc allocator; - nn_LockManager lockManager; - nn_Clock clock; - nn_Rng rng; + void *state; + nn_AllocProc *alloc; + nn_TimeProc *time; + // the maximum value the RNG can produce. + // The RNG is assumed to generate [0, rngMaximum]. INCLUSIVE. + // When a [0, 1) range is needed, the value is divided by (rngMaximum+1), + // so rngMaximum+1 MUST NOT OVERFLOW. + size_t rngMaximum; + nn_RngProc *rng; + nn_LockProc *lock; } nn_Context; -// libc-like utils - -void nn_memset(void *buf, unsigned char byte, nn_size_t len); -void nn_memcpy(void *dest, const void *src, nn_size_t len); -char *nn_strcpy(char *dest, const char *src); -const char *nn_strchr(const char *str, int ch); -int nn_strcmp(const char *a, const char *b); -nn_size_t nn_strlen(const char *a); -nn_bool_t nn_strbegin(const char *s, const char *prefix); - -#ifndef NN_BAREMETAL -nn_Alloc nn_libcAllocator(void); -nn_Clock nn_libcRealTime(void); -nn_LockManager nn_libcMutex(void); -nn_Rng nn_libcRng(void); -nn_Context nn_libcContext(void); -#endif - -nn_LockManager nn_noMutex(void); - -// Error buffers!!! -typedef char nn_errorbuf_t[NN_MAX_ERROR_BUFFER]; - -nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf); -void nn_error_write(nn_errorbuf_t buf, const char *s); -void nn_error_clear(nn_errorbuf_t buf); - -// Values for architectures - -#define NN_VALUE_INT 0 -#define NN_VALUE_NUMBER 1 -#define NN_VALUE_BOOL 2 -#define NN_VALUE_CSTR 3 -#define NN_VALUE_STR 4 -#define NN_VALUE_ARRAY 5 -#define NN_VALUE_TABLE 6 -#define NN_VALUE_NIL 7 -#define NN_VALUE_RESOURCE 8 - -typedef struct nn_string { - char *data; - nn_size_t len; - nn_size_t refc; - nn_Alloc alloc; -} nn_string; - -typedef struct nn_array { - struct nn_value *values; - nn_size_t len; - nn_size_t refc; - nn_Alloc alloc; -} nn_array; - -typedef struct nn_object { - struct nn_pair *pairs; - nn_size_t len; - nn_size_t refc; - nn_Alloc alloc; -} nn_table; - -typedef struct nn_value { - nn_size_t tag; - union { - nn_integer_t integer; - double number; - nn_bool_t boolean; - const char *cstring; - nn_string *string; - nn_array *array; - nn_table *table; - nn_size_t resourceID; - }; -} nn_value; - -typedef struct nn_pair { - nn_value key; - nn_value val; -} nn_pair; - -// we expose the allocator because of some utilities -void *nn_alloc(nn_Alloc *alloc, nn_size_t size); -void *nn_resize(nn_Alloc *alloc, void *memory, nn_size_t oldSize, nn_size_t newSize); -void nn_dealloc(nn_Alloc *alloc, void *memory, nn_size_t size); - -// Utilities, both internal and external -char *nn_strdup(nn_Alloc *alloc, const char *s); -void *nn_memdup(nn_Alloc *alloc, const void *buf, nn_size_t len); -void nn_deallocStr(nn_Alloc *alloc, char *s); -nn_address nn_randomUUID(nn_Context *ctx); - -nn_bool_t nn_path_hasSlash(const char *path); -nn_size_t nn_path_firstSlash(const char *path); -nn_size_t nn_path_lastSlash(const char *path); -// returns whether it is the last name -nn_bool_t nn_path_firstName(const char path[NN_MAX_PATH], char firstDirectory[NN_MAX_PATH], char subpath[NN_MAX_PATH]); -// returns whether it is the only name -nn_bool_t nn_path_lastName(const char path[NN_MAX_PATH], char name[NN_MAX_PATH], char parent[NN_MAX_PATH]); - -// returns whether the path is valid -nn_bool_t nn_path_isValid(const char *path); -// writes to canonical the standard form of the path -// returns whether the path is so horribly bad it cannot be converted in canonical form. -nn_bool_t nn_path_canonical(const char path[NN_MAX_PATH], char canonical[NN_MAX_PATH]); - -nn_guard *nn_newGuard(nn_Context *context); -void nn_lock(nn_Context *context, nn_guard *guard); -nn_bool_t nn_tryLock(nn_Context *context, nn_guard *guard); -void nn_unlock(nn_Context *context, nn_guard *guard); -void nn_deleteGuard(nn_Context *context, nn_guard *guard); - -void nn_addRef(nn_refc *refc, nn_size_t count); -void nn_incRef(nn_refc *refc); -/* Returns true if the object should be freed */ -nn_bool_t nn_removeRef(nn_refc *refc, nn_size_t count); -/* Returns true if the object should be freed */ -nn_bool_t nn_decRef(nn_refc *refc); - -// Unicode (more specifically, UTF-8) stuff - -nn_bool_t nn_unicode_validate(const char *s); -// expects NULL terminator -nn_bool_t nn_unicode_isValidCodepoint(const char *s); -// returned string must be nn_deallocStr()'d -char *nn_unicode_char(nn_Alloc *alloc, unsigned int *codepoints, nn_size_t codepointCount); -// returned array must be nn_dealloc()'d -unsigned int *nn_unicode_codepoints(nn_Alloc *alloc, const char *s, nn_size_t *len); -nn_size_t nn_unicode_len(const char *s); -unsigned int nn_unicode_codepointAt(const char *s, nn_size_t byteOffset); -nn_size_t nn_unicode_codepointSize(unsigned int codepoint); -void nn_unicode_codepointToChar(char buffer[NN_MAXIMUM_UNICODE_BUFFER], unsigned int codepoint, nn_size_t *len); -nn_size_t nn_unicode_charWidth(unsigned int codepoint); -nn_size_t nn_unicode_wlen(const char *s); -unsigned int nn_unicode_upperCodepoint(unsigned int codepoint); -// returned string must be nn_deallocStr()'d -char *nn_unicode_upper(nn_Alloc *alloc, const char *s); -unsigned int nn_unicode_lowerCodepoint(unsigned int codepoint); -// returned string must be nn_deallocStr()'d -char *nn_unicode_lower(nn_Alloc *alloc, const char *s); - -// permissive means it allows invalid UTF-8, in which case each byte is treated as a codepoint - -// it will return the codepoint starting at byte *index, but will also set *index to the byte afterward it -// since it is permissive, it supports invalid UTF-8 -unsigned int nn_unicode_nextCodepointPermissive(const char *s, nn_size_t *index); -nn_size_t nn_unicode_lenPermissive(const char *s); -nn_size_t nn_unicode_wlenPermissive(const char *s); -// if not found, it will return -1. This is why it is an nn_intptr_t -nn_intptr_t nn_unicode_indexPermissive(const char *s, nn_size_t codepointIndex); - -// Data card stuff - -// Hashing -void nn_data_crc32(const char *inBuf, nn_size_t buflen, char outBuf[4]); -void nn_data_md5(const char *inBuf, nn_size_t buflen, char outBuf[16]); -void nn_data_sha256(const char *inBuf, nn_size_t buflen, char outBuf[32]); - -// Base64 - -// The initial value of *len is the size of buf, with the new value being the length of the returned buffer. -char *nn_data_decode64(nn_Alloc *alloc, const char *buf, nn_size_t *len); -char *nn_data_encode64(nn_Alloc *alloc, const char *buf, nn_size_t *len); - -// Deflate/inflate - -char *nn_data_deflate(nn_Alloc *alloc, const char *buf, nn_size_t *len); -char *nn_data_inflate(nn_Alloc *alloc, const char *buf, nn_size_t *len); - -// AES -char *nn_data_aes_encrypt(nn_Alloc *alloc, const char *buf, nn_size_t *len, const char key[16], const char iv[16]); -char *nn_data_aes_decrypt(nn_Alloc *alloc, const char *buf, nn_size_t *len, const char key[16], const char iv[16]); - -// ECDH - -// if longKeys is on, instead of taking 32 bytes, the keys take up 48 bytes. -nn_size_t nn_data_ecdh_keylen(nn_bool_t longKeys); -// use nn_data_ecdh_keylen to figure out the expected length for the buffers -void nn_data_ecdh_generateKeyPair(nn_Context *context, nn_bool_t longKeys, char *publicKey, char *privateKey); - -nn_bool_t nn_data_ecdsa_check(nn_bool_t longKeys, const char *buf, nn_size_t buflen, const char *sig, nn_size_t siglen); -char *nn_data_ecdsa_sign(nn_Alloc *alloc, const char *buf, nn_size_t *buflen, const char *key, nn_bool_t longKeys); - -char *nn_data_ecdh_getSharedKey(nn_Alloc *alloc, nn_size_t *len, const char *privateKey, const char *publicKey, nn_bool_t longKeys); - -// ECC -char *nn_data_hamming_encode(nn_Alloc *alloc, const char *buf, nn_size_t *len); -char *nn_data_hamming_decode(nn_Alloc *alloc, const char *buf, nn_size_t *len); - -// Universe stuff - -nn_universe *nn_newUniverse(nn_Context context); -nn_Context *nn_getContext(nn_universe *universe); -nn_Alloc *nn_getAllocator(nn_universe *universe); -nn_Clock *nn_getClock(nn_universe *universe); -nn_LockManager *nn_getLockManager(nn_universe *universe); -nn_Rng *nn_getRng(nn_universe *universe); -void nn_unsafeDeleteUniverse(nn_universe *universe); -void *nn_queryUserdata(nn_universe *universe, const char *name); -void nn_storeUserdata(nn_universe *universe, const char *name, void *data); -double nn_getTime(nn_universe *universe); - -// Device info - -typedef struct nn_deviceInfoList_t nn_deviceInfoList_t; -typedef struct nn_deviceInfo_t nn_deviceInfo_t; - -// Common / standard keys -#define NN_DEVICEINFO_KEY_CLASS "class" -#define NN_DEVICEINFO_KEY_VENDOR "vendor" -#define NN_DEVICEINFO_KEY_PRODUCT "product" -#define NN_DEVICEINFO_KEY_CAPACITY "capacity" -#define NN_DEVICEINFO_KEY_CLOCK "clock" -#define NN_DEVICEINFO_KEY_DESCRIPTION "description" - -// Common / standard values -#define NN_DEVICEINFO_CLASS_INPUT "input" -#define NN_DEVICEINFO_CLASS_RAM "memory" -#define NN_DEVICEINFO_CLASS_ROM "memory" // not a mistake, they both use memory -#define NN_DEVICEINFO_CLASS_CPU "processor" // also used by data card -#define NN_DEVICEINFO_CLASS_DATA "processor" // also used by data card -#define NN_DEVICEINFO_CLASS_GPU "display" // not a mistake, it and screen have the same class -#define NN_DEVICEINFO_CLASS_SCREEN "display" -#define NN_DEVICEINFO_CLASS_COMPUTER "system" -#define NN_DEVICEINFO_CLASS_STORAGE "volume" -#define NN_DEVICEINFO_CLASS_INTERNET "communication" -#define NN_DEVICEINFO_CLASS_REDSTONE "communication" // why do they use the same one? idfk -#define NN_DEVICEINFO_CLASS_MODEM "network" -#define NN_DEVICEINFO_CLASS_TUNNEL "network" -#define NN_DEVICEINFO_CLASS_GENERIC "generic" - -nn_deviceInfoList_t *nn_newDeviceInfoList(nn_Context *ctx, nn_size_t preallocate); -void nn_deleteDeviceInfoList(nn_deviceInfoList_t *deviceInfoList); -nn_deviceInfo_t *nn_addDeviceInfo(nn_deviceInfoList_t *list, nn_address address, nn_size_t maxKeys); -void nn_removeDeviceInfo(nn_deviceInfoList_t *list, const char *address); -nn_bool_t nn_registerDeviceKey(nn_deviceInfo_t *deviceInfo, const char *key, const char *value); -nn_deviceInfo_t *nn_getDeviceInfoAt(nn_deviceInfoList_t *list, nn_size_t idx); -nn_size_t nn_getDeviceCount(nn_deviceInfoList_t *list); -const char *nn_getDeviceInfoAddress(nn_deviceInfo_t *deviceInfo); -const char *nn_iterateDeviceInfoKeys(nn_deviceInfo_t *deviceInfo, nn_size_t idx, const char **value); -nn_size_t nn_getDeviceKeyCount(nn_deviceInfo_t *deviceInfo); - -// Computer running states - -nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_architecture *arch, void *userdata, nn_size_t memoryLimit, nn_size_t componentLimit); -nn_universe *nn_getUniverse(nn_computer *computer); -int nn_tickComputer(nn_computer *computer); -double nn_getUptime(nn_computer *computer); -nn_size_t nn_getComputerMemoryUsed(nn_computer *computer); -nn_size_t nn_getComputerMemoryTotal(nn_computer *computer); -void *nn_getComputerUserData(nn_computer *computer); -void nn_addSupportedArchitecture(nn_computer *computer, nn_architecture *arch); -nn_architecture *nn_getSupportedArchitecture(nn_computer *computer, nn_size_t idx); -nn_architecture *nn_getArchitecture(nn_computer *computer); -nn_architecture *nn_getNextArchitecture(nn_computer *computer); -void nn_setNextArchitecture(nn_computer *computer, nn_architecture *arch); -void nn_deleteComputer(nn_computer *computer); -const char *nn_pushSignal(nn_computer *computer, nn_value *values, nn_size_t len); -nn_value nn_fetchSignalValue(nn_computer *computer, nn_size_t index); -nn_size_t nn_signalSize(nn_computer *computer); -void nn_popSignal(nn_computer *computer); -const char *nn_addUser(nn_computer *computer, const char *name); -void nn_deleteUser(nn_computer *computer, const char *name); -const char *nn_indexUser(nn_computer *computer, nn_size_t idx); -nn_bool_t nn_isUser(nn_computer *computer, const char *name); -void nn_setCallBudget(nn_computer *computer, double callBudget); -double nn_getCallBudget(nn_computer *computer); -void nn_callCost(nn_computer *computer, double cost); -double nn_getCallCost(nn_computer *computer); -nn_bool_t nn_isOverworked(nn_computer *computer); -void nn_triggerIndirect(nn_computer *computer); -nn_deviceInfoList_t *nn_getComputerDeviceInfoList(nn_computer *computer); - -/* The memory returned can be freed with nn_dealloc() */ -char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len); -void nn_deserializeProgram(nn_computer *computer, const char *memory, nn_size_t len); - -nn_Context *nn_getComputerContext(nn_computer *computer); -nn_guard *nn_getComputerLock(nn_computer *computer); - -/// This means the computer has not yet started. -#define NN_STATE_SETUP 0 - -/// This means the computer is running. There is no matching off-state, as the computer is -/// only off when it is deleted. -#define NN_STATE_RUNNING 1 - -/// This means a component's invocation could not be done due to a crucial resource being busy. -/// The sandbox should yield, then *invoke the component method again.* -#define NN_STATE_BUSY 2 - -/// This state occurs when a call to removeEnergy has consumed all the energy left. -/// The sandbox should yield, and the runner should shut down the computer. -/// No error is set, the sandbox can set it if it wanted to. -#define NN_STATE_BLACKOUT 3 - -/// This state only indicates that the runner should turn off the computer, but not due to a blackout. -/// The runner need not bring it back. -#define NN_STATE_CLOSING 4 - -/// This state indicates that the runner should turn off the computer, but not due to a blackout. -/// The runner should bring it back. -/// By "bring it back", we mean delete the computer, then recreate the entire state. -#define NN_STATE_REPEAT 5 - -/// This state indciates that the runner should turn off the computer, to switch architectures. -/// The architecture is returned by getNextArchitecture. -#define NN_STATE_SWITCH 6 - -/// The machine is overworked. -#define NN_STATE_OVERWORKED 7 - -int nn_getState(nn_computer *computer); -void nn_setState(nn_computer *computer, int state); - -void nn_computer_clearBeep(nn_computer *computer); -void nn_computer_setBeep(nn_computer *computer, double frequency, double duration, double volume); -nn_bool_t nn_computer_getBeep(nn_computer *computer, double *frequency, double *duration, double *volume); - -void nn_setEnergyInfo(nn_computer *computer, double energy, double capacity); -double nn_getEnergy(nn_computer *computer); -double nn_getMaxEnergy(nn_computer *computer); -void nn_removeEnergy(nn_computer *computer, double energy); -void nn_addEnergy(nn_computer *computer, double amount); - -double nn_getTemperature(nn_computer *computer); -double nn_getThermalCoefficient(nn_computer *computer); -double nn_getRoomTemperature(nn_computer *computer); -void nn_setTemperature(nn_computer *computer, double temperature); -void nn_setTemperatureCoefficient(nn_computer *computer, double coefficient); -void nn_setRoomTemperature(nn_computer *computer, double roomTemperature); -void nn_addHeat(nn_computer *computer, double heat); -void nn_removeHeat(nn_computer *computer, double heat); -/* Checks against NN_OVERHEAT_MIN */ -nn_bool_t nn_isOverheating(nn_computer *computer); - -// NULL if there is no error. -const char *nn_getError(nn_computer *computer); -void nn_clearError(nn_computer *computer); -void nn_setError(nn_computer *computer, const char *err); -// this version does NOT allocate a copy of err, thus err should come from the data -// segment or memory with the same lifetime as the computer. This may not be possible -// in garbage-collected languages using this API, and thus should be avoided. -// This can be used by low-level implementations of architectures such that any -// internal out-of-memory errors can be reported. The normal setError would report -// no error if allocating the copy failed, and would clear any previous error. -void nn_setCError(nn_computer *computer, const char *err); - -// Component stuff - -nn_component *nn_newComponent(nn_computer *computer, nn_address address, int slot, nn_componentTable *table, void *userdata); -void nn_setTmpAddress(nn_computer *computer, nn_address tmp); -nn_address nn_getComputerAddress(nn_computer *computer); -nn_address nn_getTmpAddress(nn_computer *computer); -void nn_removeComponent(nn_computer *computer, nn_address address); -void nn_destroyComponent(nn_component *component); -nn_computer *nn_getComputerOfComponent(nn_component *component); -nn_address nn_getComponentAddress(nn_component *component); -int nn_getComponentSlot(nn_component *component); -nn_componentTable *nn_getComponentTable(nn_component *component); -const char *nn_getComponentType(nn_componentTable *table); -void *nn_getComponentUserdata(nn_component *component); -nn_component *nn_findComponent(nn_computer *computer, nn_address address); -// the internal index is not the array index, but rather an index into -// an internal structure. YOU SHOULD NOT ADD OR REMOVE COMPONENTS WHILE ITERATING. -// the internalIndex SHOULD BE INITIALIZED TO 0. -// Returns NULL at the end -nn_component *nn_iterComponent(nn_computer *computer, nn_size_t *internalIndex); - -// Component VTable stuff - -typedef void *nn_componentConstructor(void *tableUserdata, void *componentUserdata); -typedef void *nn_componentDestructor(void *tableUserdata, nn_component *component, void *componentUserdata); -typedef void nn_componentMethod(void *componentUserdata, void *methodUserdata, nn_component *component, nn_computer *computer); -typedef nn_bool_t nn_componentMethodCondition_t(void *componentUserdata, void *methodUserdata); -typedef struct nn_method_t nn_method_t; - -nn_componentTable *nn_newComponentTable(nn_Alloc *alloc, const char *typeName, void *userdata, nn_componentConstructor *constructor, nn_componentDestructor *destructor); -void nn_destroyComponentTable(nn_componentTable *table); -nn_method_t *nn_defineMethod(nn_componentTable *table, const char *methodName, nn_componentMethod *methodFunc, const char *methodDoc); -void nn_method_setDirect(nn_method_t *method, nn_bool_t direct); -void nn_method_setUserdata(nn_method_t *method, void *userdata); -void nn_method_setCondition(nn_method_t *method, nn_componentMethodCondition_t *condition); -const char *nn_getTableMethod(nn_componentTable *table, nn_size_t idx, nn_bool_t *outDirect); -const char *nn_methodDoc(nn_componentTable *table, const char *methodName); -nn_bool_t nn_isMethodEnabled(nn_component *component, const char *methodName); - -// Resource stuff - -typedef struct nn_resourceTable_t nn_resourceTable_t; -typedef struct nn_resourceMethod_t nn_resourceMethod_t; - -typedef void nn_resourceDestructor_t(void *userdata); -typedef void nn_resourceMethodCallback_t(void *userdata, void *methodUserdata, nn_computer *computer); -typedef nn_bool_t nn_resourceMethodCondition_t(void *userdata, void *methodUserdata); - -nn_resourceTable_t *nn_resource_newTable(nn_Context *ctx, nn_resourceDestructor_t *dtor); -nn_resourceMethod_t *nn_resource_addMethod(nn_resourceTable_t *table, const char *methodName, nn_resourceMethodCallback_t *method, const char *doc); -void nn_resource_setUserdata(nn_resourceMethod_t *method, void *methodUserdata); -void nn_resource_setCondition(nn_resourceMethod_t *method, nn_resourceMethodCondition_t *methodCondition); -nn_bool_t nn_resource_invoke(nn_computer *computer, nn_size_t resourceID, const char *method); -// returns the name, and NULL for out of bounds -const char *nn_resource_nextMethodInfo(nn_computer *computer, nn_size_t id, const char **doc, nn_size_t *idx); - -nn_resourceTable_t *nn_resource_fetchTable(nn_computer *computer, nn_size_t resourceID); -nn_size_t nn_resource_allocate(nn_computer *computer, void *userdata, nn_resourceTable_t *table); -void nn_resource_release(nn_computer *computer, nn_size_t id); - -// Component calling - -/* Returns false if the method does not exist */ -nn_bool_t nn_invokeComponentMethod(nn_component *component, const char *name); -void nn_simulateBufferedIndirect(nn_component *component, double amount, double amountPerTick); -void nn_resetCall(nn_computer *computer); -void nn_addArgument(nn_computer *computer, nn_value arg); -void nn_return(nn_computer *computer, nn_value val); -nn_value nn_getArgument(nn_computer *computer, nn_size_t idx); -nn_value nn_getReturn(nn_computer *computer, nn_size_t idx); -nn_size_t nn_getArgumentCount(nn_computer *computer); -nn_size_t nn_getReturnCount(nn_computer *computer); - -// Value stuff - -nn_value nn_values_nil(void); -nn_value nn_values_integer(nn_integer_t integer); -nn_value nn_values_number(double num); -nn_value nn_values_boolean(nn_bool_t boolean); -nn_value nn_values_cstring(const char *string); -nn_value nn_values_string(nn_Alloc *alloc, const char *string, nn_size_t len); -nn_value nn_values_array(nn_Alloc *alloc, nn_size_t len); -nn_value nn_values_table(nn_Alloc *alloc, nn_size_t pairCount); -nn_value nn_values_resource(nn_size_t id); - -void nn_return_nil(nn_computer *computer); -void nn_return_integer(nn_computer *computer, nn_integer_t integer); -void nn_return_number(nn_computer *computer, double number); -void nn_return_boolean(nn_computer *computer, nn_bool_t boolean); -void nn_return_cstring(nn_computer *computer, const char *cstr); -void nn_return_string(nn_computer *computer, const char *str, nn_size_t len); -nn_value nn_return_array(nn_computer *computer, nn_size_t len); -nn_value nn_return_table(nn_computer *computer, nn_size_t len); -void nn_return_resource(nn_computer *computer, nn_size_t userdata); - -nn_size_t nn_values_getType(nn_value val); -nn_value nn_values_retain(nn_value val); -void nn_values_drop(nn_value val); -void nn_values_dropAll(nn_value *values, nn_size_t len); - -void nn_values_set(nn_value arr, nn_size_t idx, nn_value val); -nn_value nn_values_get(nn_value arr, nn_size_t idx); - -void nn_values_setPair(nn_value obj, nn_size_t idx, nn_value key, nn_value val); -nn_pair nn_values_getPair(nn_value obj, nn_size_t idx); - -nn_integer_t nn_toInt(nn_value val); -double nn_toNumber(nn_value val); -nn_bool_t nn_toBoolean(nn_value val); -const char *nn_toCString(nn_value val); -const char *nn_toString(nn_value val, nn_size_t *len); - -nn_integer_t nn_toIntOr(nn_value val, nn_integer_t defaultVal); -double nn_toNumberOr(nn_value val, double defaultVal); -nn_bool_t nn_toBooleanOr(nn_value val, nn_bool_t defaultVal); - -/* - * Computes the "packet size" of the values, using the same algorithm as OC. - * This is used by pushSignal to check the size - */ -nn_size_t nn_measurePacketSize(nn_value *vals, nn_size_t len); - -// COMPONENTS - -/* Loads the vtables for the default implementations of those components */ -void nn_loadCoreComponentTables(nn_universe *universe); - -// loading each component -void nn_loadEepromTable(nn_universe *universe); -void nn_loadFilesystemTable(nn_universe *universe); -void nn_loadDriveTable(nn_universe *universe); -void nn_loadScreenTable(nn_universe *universe); -void nn_loadGraphicsCardTable(nn_universe *universe); -void nn_loadKeyboardTable(nn_universe *universe); -void nn_loadModemTable(nn_universe *universe); -void nn_loadTunnelTable(nn_universe *universe); -void nn_loadDiskDriveTable(nn_universe *universe); -void nn_loadExternalComputerTable(nn_universe *universe); - -nn_component *nn_mountKeyboard(nn_computer *computer, nn_address address, int slot); - -// the helpers - -// EEPROM -typedef struct nn_eepromControl { - double readHeatPerByte; - double writeHeatPerByte; - - double readEnergyCostPerByte; - double writeEnergyCostPerByte; - - double bytesReadPerTick; - double bytesWrittenPerTick; -} nn_eepromControl; - -typedef struct nn_eepromTable { - void *userdata; - void (*deinit)(void *userdata); - - // methods - nn_size_t size; - nn_size_t dataSize; - void (*getLabel)(void *userdata, char *buf, nn_size_t *buflen, nn_errorbuf_t error); - nn_size_t (*setLabel)(void *userdata, const char *buf, nn_size_t buflen, nn_errorbuf_t error); - nn_size_t (*get)(void *userdata, char *buf, nn_errorbuf_t error); - nn_bool_t (*set)(void *userdata, const char *buf, nn_size_t len, nn_errorbuf_t error); - nn_size_t (*getData)(void *userdata, char *buf, nn_errorbuf_t error); - nn_bool_t (*setData)(void *userdata, const char *buf, nn_size_t len, nn_errorbuf_t error); - // allocate the string with alloc. We recommend using nn_strdup() - char *(*getArchitecture)(nn_Alloc *alloc, void *userdata, nn_errorbuf_t error); - void (*setArchitecture)(void *userdata, const char *buf, nn_errorbuf_t error); - nn_bool_t (*isReadonly)(void *userdata, nn_errorbuf_t error); - nn_bool_t (*makeReadonly)(void *userdata, nn_errorbuf_t error); -} nn_eepromTable; - -typedef struct nn_eeprom nn_eeprom; - -typedef struct nn_veepromOptions { - const char *code; - nn_size_t len; - nn_size_t size; - const char *data; - nn_size_t dataLen; - nn_size_t dataSize; - char label[NN_LABEL_SIZE]; - nn_size_t labelLen; - nn_bool_t isReadOnly; -} nn_veepromOptions; - -nn_eeprom *nn_newEEPROM(nn_Context *context, nn_eepromTable table, nn_eepromControl control); -nn_eeprom *nn_volatileEEPROM(nn_Context *context, nn_veepromOptions opts, nn_eepromControl control); -nn_guard *nn_getEEPROMLock(nn_eeprom *eeprom); -void nn_retainEEPROM(nn_eeprom *eeprom); -nn_bool_t nn_destroyEEPROM(nn_eeprom *eeprom); -nn_component *nn_addEEPROM(nn_computer *computer, nn_address address, int slot, nn_eeprom *eeprom); - -// FileSystem -typedef struct nn_filesystemControl { - double readBytesPerTick; - double writeBytesPerTick; - double removeFilesPerTick; - double createFilesPerTick; - - double readHeatPerByte; - double writeHeatPerByte; - double removeHeat; - double createHeat; - - double readEnergyPerByte; - double writeEnergyPerByte; - double removeEnergy; - double createEnergy; -} nn_filesystemControl; - -typedef struct nn_filesystemTable { - void *userdata; - void (*deinit)(void *userdata); - - void (*getLabel)(void *userdata, char *buf, nn_size_t *buflen, nn_errorbuf_t err); - nn_size_t (*setLabel)(void *userdata, const char *buf, nn_size_t buflen, nn_errorbuf_t err); - - nn_size_t (*spaceUsed)(void *userdata); - nn_size_t spaceTotal; - nn_bool_t (*isReadOnly)(void *userdata, nn_errorbuf_t err); - - // general operations - nn_size_t (*size)(void *userdata, const char *path, nn_errorbuf_t err); - nn_size_t (*remove)(void *userdata, const char *path, nn_errorbuf_t err); - nn_timestamp_t (*lastModified)(void *userdata, const char *path, nn_errorbuf_t err); - nn_size_t (*rename)(void *userdata, const char *from, const char *to, nn_errorbuf_t err); - nn_bool_t (*exists)(void *userdata, const char *path, nn_errorbuf_t err); - - // directory operations - nn_bool_t (*isDirectory)(void *userdata, const char *path, nn_errorbuf_t err); - nn_bool_t (*makeDirectory)(void *userdata, const char *path, nn_errorbuf_t err); - // The returned array should be allocated with the supplied allocator. - // The strings should be null terminated. Use nn_strdup for the allocation to guarantee nn_deallocStr deallocates it correctly. - // For the array, the *exact* size of the allocation should be sizeof(char *) * (*len), - // If it is not, the behavior is undefined. - // We recommend first computing len then allocating, though if that is not doable or practical, - // consider nn_resize()ing it to the correct size to guarantee a correct deallocation. - char **(*list)(nn_Alloc *alloc, void *userdata, const char *path, nn_size_t *len, nn_errorbuf_t err); - - // file operations - void *(*open)(void *userdata, const char *path, const char *mode, nn_errorbuf_t err); - nn_bool_t (*close)(void *userdata, void *fd, nn_errorbuf_t err); - nn_bool_t (*write)(void *userdata, void *fd, const char *buf, nn_size_t len, nn_errorbuf_t err); - nn_size_t (*read)(void *userdata, void *fd, char *buf, nn_size_t required, nn_errorbuf_t err); - nn_size_t (*seek)(void *userdata, void *fd, const char *whence, int off, nn_errorbuf_t err); -} nn_filesystemTable; - -typedef struct nn_filesystem nn_filesystem; - -typedef struct nn_vfilesystemImageNode { +// if NN_BAREMETAL is defined when NeoNucleus is compiled, +// this function will fill in a bogus context that does nothing. +// Otherwise, it fills in a context which uses the C standard library. +// This function is only meant to be called once, to get the initial context. +// It does seed the RNG when using the C standard library, so do not +// call it in loops. +void nn_initContext(nn_Context *ctx); + +// Memory allocation!!! + +void *nn_alloc(nn_Context *ctx, 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); + +typedef enum nn_Exit { + // no error + NN_OK = 0, + // out of memory. + NN_ENOMEM, + // over the limit. For example, adding too many architectures to a machine. + NN_ELIMIT, + // internal stack underflow when managing the stack. + NN_EBELOWSTACK, + // internal stack overflow when carrying values. + NN_ENOSTACK, + // bad invocation, error message stored in computer state + NN_EBADCALL, + // bad state, the function was called at the wrong time + NN_EBADSTATE, +} nn_Exit; + +// This stores necessary data between computers +typedef struct nn_Universe nn_Universe; + +nn_Universe *nn_createUniverse(nn_Context *ctx); +void nn_destroyUniverse(nn_Universe *universe); + +// The actual computer +typedef struct nn_Computer nn_Computer; + +typedef enum nn_ComputerState { + // the machine is running just fine + NN_RUNNING = 0, + // machine is initializing. This is the state after createComputer but before the first nn_tick. + // It is a required state of various initialization functions. + NN_BOOTUP, + // the machine has powered off. + NN_POWEROFF, + // the machine demands being restarted. + NN_RESTART, + // the machine has crashed. RIP + NN_CRASHED, + // the machine ran out of energy. + NN_BLACKOUT, + // change architecture. + NN_CHARCH, +} nn_ComputerState; + +typedef enum nn_ArchitectureAction { + // create the local state + NN_ARCH_INIT, + // destroy the local state + NN_ARCH_DEINIT, + // run 1 tick + NN_ARCH_TICK, + // get the free memory + NN_ARCH_FREEMEM, + // deserialize from an encoded state + NN_ARCH_DESERIALIZE, + // serialize to an encoded state + NN_ARCH_SERIALIZE, + // drop the encoded buffer + NN_ARCH_DROPSERIALIZED, +} nn_ArchitectureAction; + +typedef struct nn_ArchitectureRequest { + // the state pointer passed through + void *globalState; + // the computer which made the request + nn_Computer *computer; + // the local state bound to this computer. + // In NN_ARCH_INIT, this is NULL, and must be set to the new state, or an appropriate exit code returned. + void *localState; + // the action requested + nn_ArchitectureAction action; + union { + // in the case of NN_ARCH_FREEMEM, the free memory + size_t freeMemory; + // in the case of NN_ARCH_DESERIALIZE, NN_ARCH_SERIALIZE and NN_ARCH_DROPSERIALIZED, the buffer. + struct { + union { + char *memOut; + const char *memIn; + }; + size_t memLen; + }; + }; +} nn_ArchitectureRequest; + +typedef nn_Exit nn_ArchitectureHandler(nn_ArchitectureRequest *req); + +typedef struct nn_Architecture { const char *name; - // if NULL, the node is a directory + void *state; + nn_ArchitectureHandler *handler; +} nn_Architecture; + +// Standard RAM sizes. +// Standard OC goes from tier 1 to tier 6, +// NN adds 2 more tiers. +extern size_t nn_ramSizes[8]; + +// The state of a *RUNNING* computer. +// Powered off computers shall not have a state, and as far as NeoNucleus is aware, +// not exist. +// The computer API *is not thread-safe*, so it is recommended that you use an external lock to manage it if you are running +// it in a multi-threaded environment. +// The userdata pointer is meant to store external data required by the environment. +// totalMemory is a hint to the architecture as to how much memory to allow the runner to use. It is in bytes. +// maxComponents and maxDevices determine how many components can be connected simultaneously to this computer, and how much device info can be +// registered on this computer. +nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices); +// Destroys the state, effectively shutting down the computer. +void nn_destroyComputer(nn_Computer *computer); +// get the userdata pointer +void *nn_getComputerUserdata(nn_Computer *computer); +const char *nn_getComputerAddress(nn_Computer *computer); +nn_Universe *nn_getComputerUniverse(nn_Computer *computer); +nn_Context *nn_getUniverseContext(nn_Universe *universe); +nn_Context *nn_getComputerContext(nn_Computer *computer); +// Sets the memory scale, which defaults to 1. +// For context, OC will set the memory scale to 1.8 on 64-bit systems by default. +// This scale affects how much real-world memory an amount of VM actually takes up. +// This means if the total memory is 4 MiB, and the scale is set to 2, the computer can take up to 8MiB of actual RAM. +// However, nn_getTotalMemory() will still return 4 MiB. +// The architecture is meant to ensure that the reported free memory is also scaled. As in, the real-world free memory +// is divided by this scale to ensure it is within the correct range. +// It is undefined behavior to change the memory scale *after* the first call to nn_tick(). +// Some architectures may ignore this, if they are very low-level and thus +// do not have any implicit changes of sizes between 32-bit and 64-bit. +void nn_setMemoryScale(nn_Computer *computer, double scale); +double nn_getMemoryScale(nn_Computer *computer); + +// Returns the memory usage limit of the computer. +size_t nn_getTotalMemory(nn_Computer *computer); +// Gets the total amount of free memory the computer has available. The total memory - this is the amount of memory used. +size_t nn_getFreeMemory(nn_Computer *computer); +// Gets the total amount of used memory the computer has allocated. +// This is just the total minus the free, and does not take into +// account the overhead of storing the computer instance. +size_t nn_getUsedMemory(nn_Computer *computer); +// gets the current uptime of a computer. When the computer is not running, this value can be anything and loses all meaning. +double nn_getUptime(nn_Computer *computer); + +// Deserialize an encoded computer state. +// Encoding depends on architecture. +nn_Exit nn_deserializeComputer(nn_Computer *computer, const char *buf, size_t buflen); + +// Serialize the computer state. +// Encoding depends on architecture. +nn_Exit nn_serializeComputer(nn_Computer *computer, char **buf, size_t *buflen); + +// Free the serialized buffer. +nn_Exit nn_freeSerializedComputer(nn_Computer *computer, char *buf, size_t buflen); + +// address is copied. +// It can be NULL if you wish to have no tmp address. +// It can fail due to out-of-memory errors. +nn_Exit nn_setTmpAddress(nn_Computer *computer, const char *address); +// can return NULL if none was set +const char *nn_getTmpAddress(nn_Computer *computer); + +// Registers a user to the computer. +nn_Exit nn_addUser(nn_Computer *computer, const char *user); +// Unregisters a user from the computer. +// If they were never there, nothing is removed and all is fine. +// It returns if the user was originally there. +bool nn_removeUser(nn_Computer *computer, const char *user); +// NULL for out-of-bound users +// Can be used to iterate all users. +const char *nn_getUser(nn_Computer *computer, size_t idx); +// Helper function. +// Always returns true if 0 users are registered. +// If users are registered, it will only return true if the specified +// user is registered. +// This can be used for checking signals. +bool nn_hasUser(nn_Computer *computer, const char *user); + +// Sets the computer's architecture. +// The architecture determines everything from how the computer runs, to how it turns off. +// Everything is limited by the architecture. +// The architecture is copied, it can be freed after this is called. +void nn_setArchitecture(nn_Computer *computer, const nn_Architecture *arch); +// Gets the current architecture. +nn_Architecture nn_getArchitecture(nn_Computer *computer); +// Sets the computer's desired architecture. +// The desired architecture indicates, when the computer state is CHARCH, what the new architecture should be. +// This is set even if it is not in the supported architecture list, *you must check if it is in that list first.* +// The architecture is copied, it can be freed after this is called. +void nn_setDesiredArchitecture(nn_Computer *computer, const nn_Architecture *arch); +// Gets the desired architecture. This is the architecture the computer should use after changing architectures. +nn_Architecture nn_getDesiredArchitecture(nn_Computer *computer); +// Adds a new supported architecture, which indicates to the code running on this computer that it is possible to switch to that architecture. +// The architecture is copied, it can be freed after this is called. +nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture *arch); +// Returns the array of supported architectures, as well as the length. +const nn_Architecture *nn_getSupportedArchitectures(nn_Computer *computer, size_t *len); +// Helper function for searching for an architecture using a computer which supports it and the architecture name. +// If the architecture is not found, it returns one with a NULL name. +nn_Architecture nn_findSupportedArchitecture(nn_Computer *computer, const char *name); + +// sets the energy capacity of the computer. +void nn_setTotalEnergy(nn_Computer *computer, double maxEnergy); +// gets the energy capacity of the computer +double nn_getTotalEnergy(nn_Computer *computer); +// gets the current amount of energy +double nn_getEnergy(nn_Computer *computer); +// Returns true if there is no more energy left, and a blackout has occured. +bool nn_removeEnergy(nn_Computer *computer, double energy); + +// the handler of energy costs. +// The default handler just returns the total energy. +// Computers do not keep track of their current energy, they just call this function. +// getEnergy() calls this with amountToRemove set to 0. It is recommended to handle this as a fastpath. +// This should return the new amount of energy after the removal. +// A negative amount can be returned, it will be clamped to 0. +// If an error occurs, it is recommended to just return 0 and trigger a blackout. +// TODO: evaluate if API should be reworked to handle errors in energy handler. +typedef double nn_EnergyHandler(void *energyState, nn_Computer *computer, double amountToRemove); + +void nn_setEnergyHandler(nn_Computer *computer, void *energyState, nn_EnergyHandler *handler); + +// copies the string into the local error buffer. The error is NULL terminated, but also capped by NN_MAX_ERROR_SIZE +void nn_setError(nn_Computer *computer, const char *s); +// set a default error message from an exit. +// Does nothing for EBADCALL. +void nn_setErrorFromExit(nn_Computer *computer, nn_Exit exit); +// copies the string into the local error buffer. The error is capped by NN_MAX_ERROR_SIZE-1. The -1 is there because the NULL terminator is still inserted at the end. +// Do note that nn_getError() still returns a NULL-terminated string, thus NULL terminators in this error will lead to a shortened error. +void nn_setLError(nn_Computer *computer, const char *s, size_t len); +// Gets a pointer to the local error buffer. This is only meaningful when NN_EBADCALL is returned. +const char *nn_getError(nn_Computer *computer); +// clears the computer's error buffer, making nn_getError return a simple empty string. +void nn_clearError(nn_Computer *computer); + +// sets the computer state to the desired state. This is meant for architectures to report things like reboots or shutdowns, DO NOT ABUSE THIS. +void nn_setComputerState(nn_Computer *computer, nn_ComputerState state); +// gets the current computer state +nn_ComputerState nn_getComputerState(nn_Computer *computer); + +// Checks if the uptime is below the idle timestamp. +bool nn_isComputerIdle(nn_Computer *computer); +// Shifts over the idle timestamp. +void nn_addIdleTime(nn_Computer *computer, double time); +// runs a tick of the computer. Make sure to check the state as well! +// This automatically resets the component budgets and call budget. +// It also sets the idle timestamp to the current uptime. +nn_Exit nn_tick(nn_Computer *computer); + +typedef struct nn_DeviceInfoEntry { + const char *name; + const char *value; +} nn_DeviceInfoEntry; + +typedef struct nn_DeviceInfo { + const char *address; + const nn_DeviceInfoEntry *entries; +} nn_DeviceInfo; + +// adds some device information to the computer. This can also be removed. +// Entries is terminated by a NULL name, and preferrably also NULL value. +// It is perfectly fine to free entries after the call, it is copied. +nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *address, const nn_DeviceInfoEntry entries[]); +// Removes info assicated with a device +void nn_removeDeviceInfo(nn_Computer *computer, const char *address); +// gets the device info array. +const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len); + +typedef enum nn_MethodFlags { + // calling will consume the entire call budget + NN_INDIRECT = 0, + // calling will only consume 1 call from the call budget + NN_DIRECT = (1<<0), + // this indicates this method wraps a *field* + // getter means calling it with no arguments will return the current value, + NN_GETTER = (1<<1), + // this indicates this method wraps a *field* + // setter means calling it with 1 argument will try to set the value. + NN_SETTER = (1<<2), +} nn_MethodFlags; + +#define NN_FIELD_MASK (NN_GETTER | NN_SETTER) + +typedef struct nn_Method { + const char *name; + const char *docString; + nn_MethodFlags flags; +} nn_Method; + +typedef struct nn_ComponentState nn_ComponentState; + +typedef enum nn_ComponentAction { + // create the local state + NN_COMP_INIT, + // delete the local state + NN_COMP_DEINIT, + // perform a method call + NN_COMP_CALL, + // check if a method is enabled + NN_COMP_ENABLED, + // delete the type userdata + NN_COMP_FREETYPE, +} nn_ComponentAction; + +typedef struct nn_ComponentRequest { + // the userdata of the component type. This may be an associated VM, for example. + void *typeUserdata; + // the userdata of the component, passed in addComponent. This may be an associated resource, for example. + void *compUserdata; + // the local state of the component. NN_COMP_INIT should initialize this pointer. + void *state; + nn_Computer *computer; + // address of the component + const char *compAddress; + // the action requested + nn_ComponentAction action; + // for NN_COMP_CALL, it is the method called. + // for NN_COMP_ENABLED, it is the method being checked. + const char *methodCalled; + union { + // for NN_COMP_CALL, it is the amount of return values. + size_t returnCount; + // for NN_COMP_ENABLED, it is whether the method is enabled. + bool methodEnabled; + }; +} nn_ComponentRequest; + +typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req); + +// Creates a new component type. It is safe to free name and methods afterwards. +nn_ComponentState *nn_createComponentState(nn_Universe *universe, const char *name, void *userdata, const nn_Method methods[], nn_ComponentHandler *handler); +// NOTE: do not destroy this before destroying any components using it, or any computers with components using it. +// The component type is still used one last time for the destructor of the components. +void nn_destroyComponentState(nn_ComponentState *cstate); + +// adds a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component added. +// You MUST NOT destroy the component type while a component using that type still exists. +// You can free the address after the call just fine. +nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentState *cstate, const char *address, int slot, void *userdata); +// Checks if a component of that address exists. +bool nn_hasComponent(nn_Computer *computer, const char *address); +// Checks if the component has that method. +// This not only checks if the method exists in the component type, +// but also checks if the method is enabled for the component instance. +bool nn_hasMethod(nn_Computer *computer, const char *address, const char *method); +// removes a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component removed. +nn_Exit nn_removeComponent(nn_Computer *computer, const char *address); +// Gets the name of a type of a component. +const char *nn_getComponentType(nn_Computer *computer, const char *address); +// Gets the slot of a component. +int nn_getComponentSlot(nn_Computer *computer, const char *address); +// Returns the array of component methods. This can be used for doc strings or just listing methods. +const nn_Method *nn_getComponentMethods(nn_Computer *computer, const char *address, size_t *len); +// get the address at a certain index. +// It'll return NULL for out of bounds indexes. +// This can be used to iterate over all components. +const char *nn_getComponentAddress(nn_Computer *computer, size_t idx); +// Returns the doc-string associated with a method. +const char *nn_getComponentDoc(nn_Computer *computer, const char *address, const char *method); +void *nn_getComponentUserdata(nn_Computer *computer, const char *address); + +// this uses the call stack. +// Component calls must not call other components, it just doesn't work. +// The lack of an argument count is because the entire call stack is assumed to be the arguments. +// In the case of NN_EBUSY, you should call it again with the same arguments later. +nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method); + +// Sets the call budget. +// The default is 1,000. +void nn_setCallBudget(nn_Computer *computer, size_t budget); + +// gets the total call budget +size_t nn_getCallBudget(nn_Computer *computer); + +// returns the remaining call budget +size_t nn_callBudgetRemaining(nn_Computer *computer); + +// automatically called by nn_tick() +void nn_resetCallBudget(nn_Computer *computer); + +// returns whether there is no more call budget left. +// 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. +// Internally, reference counting is used to manage the memory automatically. The API is designed such that strong reference cycles +// cannot occur. + +// returns if there is enough space for [amount] values +bool nn_checkstack(nn_Computer *computer, size_t amount); + +// pushes a null on the call stack +nn_Exit nn_pushnull(nn_Computer *computer); +// pushes a boolean on the call stack +nn_Exit nn_pushbool(nn_Computer *computer, bool truthy); +// pushes a number on the call stack +nn_Exit nn_pushnumber(nn_Computer *computer, double num); +// casts [num] to a double and pushes it on the call stack +nn_Exit nn_pushinteger(nn_Computer *computer, intptr_t num); +// pushes a NULL-terminated string on the call stack. The string is copied, so you can free it afterwards without worry. +nn_Exit nn_pushstring(nn_Computer *computer, const char *str); +// pushes a string on the call stack. The string is copied, so you can free it afterwards without worry. The copy will have a NULL terminator inserted +// at the end for APIs which need it, but the length is also stored. +nn_Exit nn_pushlstring(nn_Computer *computer, const char *str, size_t len); +// pushes a computer userdata to the stack. This is indicative of a resource, such as an HTTP request. +nn_Exit nn_pushuserdata(nn_Computer *computer, size_t userdataIdx); +// pushes a table meant to be an array. [len] is the length of the array. The keys are numbers and 1-indexed, just like in Lua. +// The values are popped, then the array is pushed. +nn_Exit nn_pusharraytable(nn_Computer *computer, size_t len); +// pushes a table. [len] is the amount of pairs. Keys should not be duplicated, as they are not de-duplicated. +// The stack should have a sequence of K1,V1,K2,V2,etc., len pairs long. +// The pairs are popped, then the array is pushed. +nn_Exit nn_pushtable(nn_Computer *computer, size_t len); + +// stack management + +// pops the top value off the stack +nn_Exit nn_pop(nn_Computer *computer); +// pops the top N values off the stack +nn_Exit nn_popn(nn_Computer *computer, size_t n); +// pushes the top value onto the stack, effectively duplicating the top value. +nn_Exit nn_dupe(nn_Computer *computer); +// pushes the top N values onto the stack, effectively duplicating the top N values. +nn_Exit nn_dupen(nn_Computer *computer, size_t n); + +// pushes the value at idx. +nn_Exit nn_dupeat(nn_Computer *computer, size_t idx); + +// get the current amount of values on the call stack. +// For component calls, calling this at the start effectively gives you the argument count. +size_t nn_getstacksize(nn_Computer *computer); +// Removes all values from the stack. +// It is recommended to do this when initiating a component call or +// after returning from errors, as the call stack may have +// random junk on it. +void nn_clearstack(nn_Computer *computer); + +// type check! The API may misbehave if types do not match, so always type-check! + +// Returns whether the value at [idx] is a null. +// [idx] starts at 0. +bool nn_isnull(nn_Computer *computer, size_t idx); +// Returns whether the value at [idx] is a boolean. +// [idx] starts at 0. +bool nn_isboolean(nn_Computer *computer, size_t idx); +// Returns whether the value at [idx] is a number. +// [idx] starts at 0. +bool nn_isnumber(nn_Computer *computer, size_t idx); +// Returns whether the value at [idx] is a number AND +// the number can safely be cast to an intptr_t. +bool nn_isinteger(nn_Computer *computer, size_t idx); +// Returns whether the value at [idx] is a string. +// [idx] starts at 0. +bool nn_isstring(nn_Computer *computer, size_t idx); +// Returns whether the value at [idx] is a userdata. +// [idx] starts at 0. +bool nn_isuserdata(nn_Computer *computer, size_t idx); +// Returns whether the value at [idx] is a table. +// [idx] starts at 0. +bool nn_istable(nn_Computer *computer, size_t idx); +// Returns the name of the type of the value at that index. +// For out of bounds indexes, "none" is returned. +const char *nn_typenameof(nn_Computer *computer, size_t idx); + +// Argument helpers + +// Returns true if the argument at that index is not null. +bool nn_checknull(nn_Computer *computer, size_t idx, const char *errMsg); +// Returns true if the argument at that index is not a boolean. +bool nn_checkboolean(nn_Computer *computer, size_t idx, const char *errMsg); +// Returns true if the argument at that index is not a number. +bool nn_checknumber(nn_Computer *computer, size_t idx, const char *errMsg); +// Returns true if the argument at that index is not an integer. +bool nn_checkinteger(nn_Computer *computer, size_t idx, const char *errMsg); +// Returns true if the argument at that index is not a string. +bool nn_checkstring(nn_Computer *computer, size_t idx, const char *errMsg); +// Returns true if the argument at that index is not userdata. +bool nn_checkuserdata(nn_Computer *computer, size_t idx, const char *errMsg); +// Returns true if the argument at that index is a table. +bool nn_checktable(nn_Computer *computer, size_t idx, const char *errMsg); + +// Checks if idx is equal to the stack size. +// If it is, it will push a null. +nn_Exit nn_defaultnull(nn_Computer *computer, size_t idx); +// Checks if idx is equal to the stack size. +// If it is, it will push a boolean [value]. +nn_Exit nn_defaultboolean(nn_Computer *computer, size_t idx, bool value); +// Checks if idx is equal to the stack size. +// If it is, it will push a number [num]. +nn_Exit nn_defaultnumber(nn_Computer *computer, size_t idx, double num); +// Checks if idx is equal to the stack size. +// If it is, it will push an integer [num]. +nn_Exit nn_defaultinteger(nn_Computer *computer, size_t idx, intptr_t num); +// Checks if idx is equal to the stack size. +// If it is, it will push a string [str]. +nn_Exit nn_defaultstring(nn_Computer *computer, size_t idx, const char *str); +// Checks if idx is equal to the stack size. +// If it is, it will push a string [str]. +nn_Exit nn_defaultlstring(nn_Computer *computer, size_t idx, const char *str, size_t len); +// Checks if idx is equal to the stack size. +// If it is, it will push the userdata [userdataIdx]. +nn_Exit nn_defaultuserdata(nn_Computer *computer, size_t idx, size_t userdataIdx); +// Checks if idx is equal to the stack size. +// If it is, it will push an empty table. +nn_Exit nn_defaulttable(nn_Computer *computer, size_t idx); + +// NOTE: behavior of the nn_to*() functions and nn_dumptable() when the values have the wrong types or at out of bounds indexes is undefined. + +// Returns the boolean value at [idx]. +bool nn_toboolean(nn_Computer *computer, size_t idx); +// Returns the number value at [idx]. +double nn_tonumber(nn_Computer *computer, size_t idx); +// Returns the number value at [idx] cast to an intptr_t. +// NOTE: for numbers where nn_isinteger() returns false, +// the cast is undefined. +// This includes values such as infinity and NaN, where +// the behavior is platform, ABI and compiler-specific. +intptr_t nn_tointeger(nn_Computer *computer, size_t idx); +// Returns the string value at [idx]. +const char *nn_tostring(nn_Computer *computer, size_t idx); +// Returns the string value and its length at [idx]. +const char *nn_tolstring(nn_Computer *computer, size_t idx, size_t *len); +// Returns the userdata index at [idx]. +size_t nn_touserdata(nn_Computer *computer, size_t idx); +// Takes a table value and pushes onto the stack the key-value pairs, as well as writes how many there were in [len]. +// It pushes them as K1,V1,K2,V2,K3,V3,etc., just like went in to pushtable. And yes, the keys are not de-duplicated. +nn_Exit nn_dumptable(nn_Computer *computer, size_t idx, size_t *len); + +// computes the cost of the top [values] values using the same algorithm as +// the modem. +// It will return -1 if the values are invalid. +// The algorithm is as mentioned in https://ocdoc.cil.li/component:modem +// and is as follows: +// - Every value adds a 2 byte overhead +// - Numbers add another 8 bytes, true/false/null another 4 bytes, strings as +// many bytes as they contain, except empty strings count as 1 byte. +int nn_countValueCost(nn_Computer *computer, size_t values); + +// computes the signal cost. +// This is a slightly modified version of value cost, except it allows +// tables and userdata. +// All values are always valid. +// For userdata and tables: +// - Userdata adds another 8 bytes overhead like numbers do. +// - Tables add yet another 2 byte overhead for their terminator, and the sum of all of the size of the keys and values they contain as per this algorithm. +size_t nn_countSignalCost(nn_Computer *computer, size_t values); + +// Returns the amount of signals stored +size_t nn_countSignals(nn_Computer *computer); +// Pops [valueCount] values from the call stack and pushes them as a signal. +nn_Exit nn_pushSignal(nn_Computer *computer, size_t valueCount); +// Removes the first signal and pushes the values onto the call stack, while setting valueCount to the amount of values in the signal. +// If there is no signal, it returns EBADSTATE +nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount); + +// The high-level API of the built-in components. +// These components still make no assumptions about the OS, and still require handlers to connect them to the outside work. + +// TODO: screen, gpu, filesystem, eeprom and the rest of the universe + +typedef enum nn_EEPROMAction { + // the eeprom instance has been dropped + NN_EEPROM_DROP, + // the eeprom state has been dropped + NN_EEPROM_FREE, + NN_EEPROM_GET, + NN_EEPROM_SET, + NN_EEPROM_GETDATA, + NN_EEPROM_SETDATA, + NN_EEPROM_GETLABEL, + NN_EEPROM_SETLABEL, + NN_EEPROM_GETARCH, + NN_EEPROM_SETARCH, + NN_EEPROM_ISREADONLY, + NN_EEPROM_MAKEREADONLY, +} nn_EEPROMAction; + +typedef struct nn_EEPROMRequest { + // associated userdata + void *userdata; + // associated component userdata + void *instance; + // the computer making the request + nn_Computer *computer; + const struct nn_EEPROM *eepromConf; + nn_EEPROMAction action; + // all the get* options should set this to the length, + // and its initial value is the capacity of [buf]. + // For ISREADONLY, this should be set to 0 if false and 1 if true. + unsigned int buflen; + // this may be the buffer length + 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 energy cost of reading an EEPROM + double readEnergyCost; + // the energy cost of reading an EEPROM's associated data + double readDataEnergyCost; + // the energy cost of writing to an EEPROM + double writeEnergyCost; + // the energy cost of writing to an EEPROM's associated data + double writeDataEnergyCost; + // idle time added when writing code + double writeDelay; + // idle time added when writing data + double writeDataDelay; +} nn_EEPROM; + +// Tier 1 - The normal EEPROM equivalent +// Tier 2 - A better EEPROM +// Tier 3 - An even better EEPROM +// Tier 4- The best EEPROM +extern nn_EEPROM nn_defaultEEPROMs[4]; + +typedef struct nn_VEEPROM { + const char *code; + size_t codelen; const char *data; - // if it is a directory, this is the amount of entries encoded afterwards - nn_size_t len; -} nn_vfilesystemImageNode; + size_t datalen; + const char *label; + size_t labellen; + const char *arch; + bool isReadonly; +} nn_VEEPROM; -typedef struct nn_vfilesystemOptions { - // used to compute lastModified - nn_timestamp_t creationTime; - nn_size_t maxDirEntries; - nn_size_t capacity; - nn_bool_t isReadOnly; - char label[NN_LABEL_SIZE]; - nn_size_t labelLen; - // loading the files into the tmpfs - nn_vfilesystemImageNode *image; - nn_size_t rootEntriesInImage; -} nn_vfilesystemOptions; +typedef nn_Exit nn_EEPROMHandler(nn_EEPROMRequest *request); -nn_filesystem *nn_newFilesystem(nn_Context *context, nn_filesystemTable table, nn_filesystemControl control); -nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control); -nn_guard *nn_getFilesystemLock(nn_filesystem *fs); -void nn_retainFilesystem(nn_filesystem *fs); -nn_bool_t nn_destroyFilesystem(nn_filesystem *fs); +// the userdata passed to the component is the userdata +// in the handler +nn_ComponentState *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata); +nn_ComponentState *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem); -nn_component *nn_addFileSystem(nn_computer *computer, nn_address address, int slot, nn_filesystem *filesystem); +// Note on paths: +// - Paths given always have their length stored, but also have a NULL terminator. +// - Paths are validated. They check for illegal characters as per OC's definition. +// - Logical paradoxes such as rename("a", "a/b") are automatically checked and handled. +// - \ are automatically replaced with / +// - .. and leading / is handled automatically. This also improves sandboxing, as ../a.txt would become just a.txt +// - For rename, it automatically checks if the destination exists and if so, errors out. +typedef enum nn_FilesystemAction { + // the filesystem instance has been dropped. + // This is just for computer-local state, make sure to free it. + NN_FS_DROP, + // the filesystem state has been dropped. + // Make sure to close all file descriptors which are still open. + NN_FS_FREE, + // open a file. strarg1 stores the path, and strarg2 stores the mode. + // strarg1len and strarg2len are their respective lengths. + // The output should be in fd. + NN_FS_OPEN, + // read a file. + // The file descriptor is stored in fd, + // make sure to ensure it is valid. + // strarg1len is the capacity of strarg1. + // Write the result of reading into strarg1. + // Update strarg1len to reflect the amount of data read. + // Set strarg1 to NULL to indicate EOF. + NN_FS_READ, + // write to a file. + // The file descriptor is stored in fd, + // make sure to ensure it is valid. + // strarg1len is the amount of data to write. + // strarg1 is the contents of the buffer to write. + NN_FS_WRITE, + // seek a file. + // The file descriptor is stored in fd, + // make sure to ensure it is valid. + // The offset is stored in off. + // The seek mode is stored in whence. + // It should set off to the new position. + NN_FS_SEEK, + // close a file. + // The file descriptor is stored in fd, + // make sure to ensure it is valid. + NN_FS_CLOSE, + // open a directory file descriptor. + // The result should be in fd. + NN_FS_OPENDIR, + // read a directory file descriptor, stored in fd. + // 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 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 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, + // Create a directory at a given path stored in strarg1. + // strarg1len is the length of the path. + // It is meant to also create parent directories recursively + // as needed. + NN_FS_MKDIR, + // Return the lastmodified timestamp. + // 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. + // If it is not, size should be set to 0. + NN_FS_ISDIRECTORY, + // Checks if the filesystem is read-only. + // If it is, size should be set to 1. + // If it is not, size should be set to 0. + NN_FS_ISREADONLY, + // 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. + NN_FS_EXISTS, + // Returns the label. + // The label should be written into strarg1, with strarg1len as the capacity. + // Set strarg1len to the label length. + NN_FS_GETLABEL, + // Sets the label. + // The label is stored in strarg1, with strarg1len as the length. + NN_FS_SETLABEL, + // Gets the space used, it should be stored in size. + NN_FS_SPACEUSED, + // Gets 2 paths, strarg1 and strarg2, with their lengths. + // It should try to rename strarg1 to strarg2, as in, + // it should move strarg1 to be at strarg2, potentially + // using recursive directory copies. + NN_FS_RENAME, + // Removes the path stored in strarg1. + NN_FS_REMOVE, + // Returns the size of the entry at strarg1. + // The size of a directory is typically 0. + // The size of a file is typically the amount of bytes in its contents. + // Using other measures of size will rarely break code, + // but may confuse users. + NN_FS_SIZE, +} nn_FilesystemAction; -// Drive -typedef struct nn_driveControl { - double readSectorsPerTick; - double writeSectorsPerTick; - // Set it to 0 to disable seek latency. - double seekSectorsPerTick; +typedef enum nn_FilesystemWhence { + // relative to start + NN_SEEK_SET, + // relative to the current position + NN_SEEK_CUR, + // relative to the EOF position. + NN_SEEK_END, +} nn_FilesystemWhence; - double readHeatPerSector; - double writeHeatPerSector; - double motorHeatPerSector; - - double readEnergyPerSector; - double writeEnergyPerSector; - double motorEnergyPerSector; +typedef struct nn_FilesystemRequest { + void *userdata; + void *instance; + nn_Computer *computer; + struct nn_Filesystem *fsConf; + nn_FilesystemAction action; + int fd; + nn_FilesystemWhence whence; + int off; + char *strarg1; + size_t strarg1len; + char *strarg2; + size_t strarg2len; + size_t size; +} nn_FilesystemRequest; - // if not, seeking *backwards* will cost as much as a full spin. - nn_bool_t reversable; -} nn_driveControl; +typedef struct nn_Filesystem { + // the maximum capacity of the filesystem + size_t spaceTotal; + // how many read calls can be done per tick + // list, exists, size, lastModified, 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; -typedef struct nn_driveTable { - void *userdata; - void (*deinit)(void *userdata); - - void (*getLabel)(void *userdata, char *buf, nn_size_t *buflen); - nn_size_t (*setLabel)(void *userdata, const char *buf, nn_size_t buflen); +// 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; +// a generic tmpfs +extern nn_Filesystem nn_defaultTmpFS; - nn_size_t platterCount; - nn_size_t capacity; - nn_size_t sectorSize; +typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request); - // sectors start at 1 as per OC. - void (*readSector)(void *userdata, int sector, char *buf); - void (*writeSector)(void *userdata, int sector, const char *buf); +typedef struct nn_VFileNode { + // the name of the node. + // This is the raw name, do not append / to directories. + const char *name; + // if NULL, the node is a directory. + const char *data; + union { + // for files, how much of data to read. + size_t dataLen; + // for directories, the amount of entries encoded afterwards. + // Do note that entry encoding is recursive, so for example + // a(1) b(2) c("hi") d("there"), means directory a/ has a directory b/ which has 2 files, c and d, + // even though a's entry count is 1. + size_t entryCount; + }; +} nn_VFileNode; - // readByte and writeByte will internally use readSector and writeSector. This is to ensure they are handled *consistently.* - // Also makes the interface less redundant -} nn_driveTable; +typedef struct nn_VFilesystem { + const char *label; + size_t labellen; + bool isReadOnly; + // The maximum amount of directory entries. This is used to pre-allocate an array. + // It also helps against memory hogging attacks. + size_t maxDirEntries; + // the maximum amount of nodes the filesystem can have. This is also used to pre-allocate an array. + size_t maxNodeCount; + // used to compute lastModified. This, together with the context's time procedure, is used to compute the timestamp. + // It must be a UNIX timestamp, else you'll get weird results. + size_t creationTime; + size_t rootNodeCount; + // the flat array of the filesystem. See nn_VFileNode for details. + nn_VFileNode *image; +} nn_VFilesystem; -typedef struct nn_vdriveOptions { - nn_size_t sectorSize; - nn_size_t platterCount; - nn_size_t capacity; - const char *data; - char label[NN_LABEL_SIZE]; - nn_size_t labelLen; -} nn_vdriveOptions; +nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata); +nn_ComponentState *nn_createVFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, const nn_VFilesystem *vfs); -typedef struct nn_drive nn_drive; +typedef enum nn_DriveAction { + // instance dropped + NN_DRIVE_DROP, + // free screen state + NN_DRIVE_FREE, + // Gets the current label. + // [index] is set to the capacity of [buf]. + // You must write the label into [buf], then set [index] to the length of the label. + // Empty label means no label. + NN_DRIVE_GETLABEL, + // Sets the current label. + // [index] is set to the length of [buf]. + // Empty label means no label. + // Set [index] to the new length of the label, if it has been truncated. + NN_DRIVE_SETLABEL, + // gets the current read head, or more accurately, the last sector used + // in order to compute seeking penalties. + // You must output the current read head in [index]. + NN_DRIVE_GETCURSECTOR, + // Reads a sector. + // The sector index is in [index], and the contents are in [buf]. + NN_DRIVE_READSECTOR, + // Writes a sector. + // The sector index is in [index]. + // Output the contents of that sector in [buf]. + NN_DRIVE_WRITESECTOR, + // Reads a byte + // The byte index is in [index]. + // You must output the byte in [byte]. + NN_DRIVE_READBYTE, + // Writes a byte. + // The byte index is in [index], the byte is in [byte]. + NN_DRIVE_WRITEBYTE, +} nn_DriveAction; -nn_drive *nn_newDrive(nn_Context *context, nn_driveTable table, nn_driveControl control); -nn_drive *nn_volatileDrive(nn_Context *context, nn_vdriveOptions opts, nn_driveControl control); -nn_guard *nn_getDriveLock(nn_drive *drive); -void nn_retainDrive(nn_drive *drive); -nn_bool_t nn_destroyDrive(nn_drive *drive); +// Note that sectors and bytes are 1-indexed. +// Bounds checking is done automatically by the interface. +typedef struct nn_DriveRequest { + void *userdata; + void *instance; + nn_Computer *computer; + struct nn_Drive *driveConf; + nn_DriveAction action; + size_t index; + union { + char *buf; + // OC explicitly uses *signed* chars. + // Helper methods for reading unsigned bytes cast it to an unsigned byte first. + // Just, do not ask. + signed char byte; + }; +} nn_DriveRequest; -nn_component *nn_addDrive(nn_computer *computer, nn_address address, int slot, nn_drive *drive); +typedef nn_Exit nn_DriveHandler(nn_DriveRequest *req); -// Screens and GPUs -typedef struct nn_screen nn_screen; +typedef struct nn_Drive { + // The capacity of the drive. + // It is in bytes, but it MUST be a multiple of the sector size. + // The total amount of sectors, as in capacity / sectorSize, must also be divisible by the platter count. + // If it is not, it is UB. + size_t capacity; + // the sector size, typically 512 + size_t sectorSize; + // the amount of platters the drive has. This contributes to how many "rotations" are needed. + // A drive with 8 sectors but 1 platter, when seeking from sector 1 to 8, would mean 7 rotations. + // However, if it has 2 platters, it'd be seen as 1 to 4 being at the same angle as 5 to 8, which + // would mean only 3 rotations. + size_t platterCount; + // how many reads can be issued per tick. + // Reading either a sector or a byte counts as 1 read. + size_t readsPerTick; + // how many writes can be issued per tick. + // Writing a sector counts as 1 write. + // Writing a byte counts as 1 read and 1 write, + // you can imagine it as reading the sector, editing the byte, + // then writing the sector back. + size_t writesPerTick; + // Set to 0 for *infinite*, effectively an SSD. + // This would mean there is 0 penalty for seeking (technically unreliastic even for an SSD). + // This is simply used to compute idle time. It is in literal full rotations per minute. + size_t rpm; + // If false, it behaves like a normal OC drive, where the drive can spin backwards to seek. + // However, this is unrealistic, as doing so may crack the sensitive platter and make the + // reader lose lift. + // For fans of physics, this option only allows the seeks to go forwards. + // This is super punishing at a slow RPM, so it is recommended to bump up + // the RPM to something like 7200 RPM. + bool onlySpinForwards; + // 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_Drive; -typedef struct nn_scrchr_t { - unsigned int codepoint; - int fg; - int bg; - nn_bool_t isFgPalette; - nn_bool_t isBgPalette; -} nn_scrchr_t; +typedef struct nn_VDrive { + // initial label + const char *label; + size_t labellen; + // initial data + const char *data; + size_t datalen; +} nn_VDrive; -nn_screen *nn_newScreen(nn_Context *context, int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors); -nn_componentTable *nn_getScreenTable(nn_universe *universe); +extern nn_Drive nn_defaultDrives[4]; -void nn_retainScreen(nn_screen *screen); -void nn_destroyScreen(nn_screen *screen); +nn_ComponentState *nn_createDrive(nn_Universe *universe, const nn_Drive *drive, nn_DriveHandler *handler, void *userdata); +nn_ComponentState *nn_createVDrive(nn_Universe *universe, const nn_Drive *drive, const nn_VDrive *vdrive); -void nn_lockScreen(nn_screen *screen); -void nn_unlockScreen(nn_screen *screen); +typedef enum nn_ScreenAction { + // instance dropped + NN_SCR_DROP, + // free screen state + NN_SCR_FREE, -void nn_getResolution(nn_screen *screen, int *width, int *height); -void nn_maxResolution(nn_screen *screen, int *width, int *height); -void nn_setResolution(nn_screen *screen, int width, int height); -// changes the maximum resolution -// DOES NOT USE THE LOCK AND THUS MAY CAUSE RACE CONDITIONS AND SEGFAULTS!!!!! -nn_bool_t nn_unsafeReallocateScreenBuffer(nn_screen *screen, int maxWidth, int maxHeight); + // set w to 1 if it is on, or 0 if it is off. + NN_SCR_ISON, + // attempt to turn the screen on. + // set w to 1 if it was on, or 0 if it was off. + // set h to 1 if it is now on, or 0 if it is now off. + NN_SCR_TURNON, + // attempt to turn the screen off. + // set w to 1 if it was on, or 0 if it was off. + // set h to 1 if it is now on, or 0 if it is now off. + NN_SCR_TURNOFF, + // get a keyboard. The index requested is stored in h. + // If the index is out of bounds, set keyboard to NULL. + // Else, write the keyboard address into the buffer in keyboard. + // The capacity of the buffer is stored in w. + NN_SCR_GETKEYBOARD, + // change the screen to/from precise mode. + // Precise mode means mouse events will have real-number coordinates, as opposed to integer-based ones. + // NeoNucleus does not automatically round this, you are meant to round it. + // The new precision value is stored in w, where it is a 1 to enable it and 0 to disable it. + // Set w to 1 if precise mode is now enabled, or 0 if it isn't. + NN_SCR_SETPRECISE, + // Set w to 1 if precise mode is enabled, or 0 if it isn't. + NN_SCR_ISPRECISE, + // change the screen to/from inverted touch mode. + // Inverted touch mode normally provides an alternative way to interact with the touchscreen. + // For example, in OC, it makes the GUI only open with shift+rightclick, and normal rightclick + // triggers a touch event instead. It is best to give it an equivalent meaning to OC's to prevent + // unexpected program behavior. + // The new inverted touch mode state is stored in w, where it is a 1 to enable it and 0 to disable it. + // Set w to 1 if inverted touch mode is now enabled, or 0 if it isn't. + NN_SCR_SETTOUCHINVERTED, + // Set w to 1 if inverted touch mode is enabled, or 0 if it isn't. + NN_SCR_ISTOUCHINVERTED, + // Gets the aspect ratio (amount of screen blocks joined together). + // Outside of MC, this may not make much sense, in which case you can just set it to 1x1. + // Store the width in w and the height in h. + NN_SCR_GETASPECTRATIO, +} nn_ScreenAction; -void nn_getViewport(nn_screen *screen, int *width, int *height); -void nn_setViewport(nn_screen *screen, int width, int height); +typedef struct nn_ScreenRequest { + void *userdata; + void *instance; + nn_Computer *computer; + nn_ScreenAction action; + int w; + int h; + char *keyboard; +} nn_ScreenRequest; -void nn_getAspectRatio(nn_screen *screen, int *width, int *height); -void nn_setAspectRatio(nn_screen *screen, int width, int height); +typedef enum nn_ScreenFeatures { + NN_SCRF_NONE = 0, + // whether it supports mouse input. + // If it doesn't, it should not emit + // touch, drag or other mouse events. + // Walk events should also not be emitted. + NN_SCRF_MOUSE = 1<<0, + // Whether precise mode is supported. + NN_SCRF_PRECISE = 1<<1, + // Whether touch inverted is supported. + NN_SCRF_TOUCHINVERTED = 1<<2, + // it indicates that the palette can be edited. + NN_SCRF_EDITABLECOLORS = 1<<3, +} nn_ScreenFeatures; -void nn_addKeyboard(nn_screen *screen, nn_address address); -void nn_removeKeyboard(nn_screen *screen, nn_address address); -nn_address nn_getKeyboard(nn_screen *screen, nn_size_t idx); -nn_size_t nn_getKeyboardCount(nn_screen *screen); +// A struct for the reference screen configurations +// This does not influence the interface at all, +// however it exists as a runtime reference of what +// the conventional screen tiers are. +typedef struct nn_ScreenConfig { + int maxWidth; + int maxHeight; + nn_ScreenFeatures features; + int paletteColors; + char maxDepth; +} nn_ScreenConfig; -void nn_setEditableColors(nn_screen *screen, int count); -int nn_getEditableColors(nn_screen *screen); -void nn_setPaletteColor(nn_screen *screen, int idx, int color); -int nn_getPaletteColor(nn_screen *screen, int idx); -int nn_getPaletteCount(nn_screen *screen); +// OC has 3 tiers, NN adds a 4th one as well. +extern nn_ScreenConfig nn_defaultScreens[4]; -int nn_maxDepth(nn_screen *screen); -int nn_getDepth(nn_screen *screen); -void nn_setDepth(nn_screen *screen, int depth); +typedef nn_Exit nn_ScreenHandler(nn_ScreenRequest *req); + +nn_ComponentState *nn_createScreen(nn_Universe *universe, nn_ScreenHandler *handler, void *userdata); +// a useless component which does nothing +nn_ComponentState *nn_createKeyboard(nn_Universe *universe); + +// Remember: +// - Colors are in 0xRRGGBB format. +// - Screen coordinates and palettes are 1-indexed. +// - If NN_GPU_SETRESOLUTION returns NN_OK, a screen_resized signal is queued automatically. +// - VRAM is always fast +typedef enum nn_GPUAction { + // instance dropped + NN_GPU_DROP, + // component state dropped + NN_GPU_FREE, + + // Conventional GPU functions + + // requests to bind to a screen connected to the computer. + // The address is stored in text, with the length in width. + // The interface does check that the computer does have the screen, but do look out + // for time-of-check/time-of-use issues which may occur in multi-threaded environments. + // If x is set to 1, the reset flag is enabled. This means the GPU should "reset" the state + // of the screen. + NN_GPU_BIND, + // requests to unbind the GPU from its screen. + // If there is no screen, it just does nothing. + NN_GPU_UNBIND, + // Ask for the screen the GPU is currently bound to. + // If it is not bound to any, text should be set to NULL. + // If it is, you must write to text the address of the screen. + // width stores the capacity of text, so if needed, truncate it to that many bytes. + // The length of this address must be stored in width. + NN_GPU_GETSCREEN, + // Gets the current background. + // x should store either the color in 0xRRGGBB format or the palette index. + // y should be 1 if x is a palette index and 0 if it is a color. + NN_GPU_GETBACKGROUND, + // Sets the current background. + // x should store either the color in 0xRRGGBB format or the palette index. + // y should be 1 if x is a palette index and 0 if it is a color. + // The values x and y should be updated to reflect the old state. + NN_GPU_SETBACKGROUND, + // Gets the current foreground. + // x should store either the color in 0xRRGGBB format or the palette index. + // y should be 1 if x is a palette index and 0 if it is a color. + NN_GPU_GETFOREGROUND, + // Sets the current foreground. + // x should store either the color in 0xRRGGBB format or the palette index. + // y should be 1 if x is a palette index and 0 if it is a color. + // The values x and y should be updated to reflect the old state. + NN_GPU_SETFOREGROUND, + // Gets the palette color. + // x is the index. + // y should be set to the color. + NN_GPU_GETPALETTECOLOR, + // Gets the palette color. + // x is the index. + // y is the color. + NN_GPU_SETPALETTECOLOR, + // Gets the maximum depth supported by the GPU and screen. + // Valid depth values in OC are 1, 4 and 8, however NN also recognizes 2, 3, 16 and 24. + // The result should be stored in x. + NN_GPU_MAXDEPTH, + // Gets the current depth the screen is displaying at. + // The result should be stored in x. + NN_GPU_GETDEPTH, + // Sets the current depth the screen is displaying at. + // The new depth is in x. + // This should not change the stored color values of neither the palette nor the characters, + // but simply change what their color is translated to graphically. + // The old depth should be stored in x. + NN_GPU_SETDEPTH, + // Gets the maximum resolution supported by the GPU and screen. + // Result should be in width and height. + NN_GPU_MAXRESOLUTION, + // Gets the resolution of the screen. + // Result should be in width and height. + NN_GPU_GETRESOLUTION, + // Sets the resolution of the screen. + // The new resolution should be stored in width and height. + // If successful, a screen_resized event is implicitly queued. + NN_GPU_SETRESOLUTION, + // Gets the current screen viewport. + // The result should be in width and height. + NN_GPU_GETVIEWPORT, + // Sets the screen viewport. + // The new viewport dimensions are stored in width and height. + NN_GPU_SETVIEWPORT, + // Gets a character. + // The position requested is given in x and y. + // The codepoint of the character should be set in [codepoint]. + // The foreground and background color should be set in [width] and [height]. + // The palette indexes of the foreground and background should be set + // in [dest] and [src] respectively. If the pixel color was not from + // the palette, the imaginary -1 palette index can be used. + NN_GPU_GET, + // Sets a horizontal line of text at a given x, y. + // The position is stored in x, y, and is the position of the first character. + // The text goes left-to-right on the horizontal line. Anything off-screen is discared. + // There is no wrapping. + // The text is stored in text, with the size of the text, in bytes, being stored in width. + NN_GPU_SET, + // like NN_GPU_SET, but the text is set vertically. + // This means instead of going from left-to-right on the screen on a horizontal line, + // it is up-to-down on a vertical line. + NN_GPU_SETVERTICAL, + // Copies a portion of the screen to another location. + // The rectangle being copied is width x height, and has the top-left corner at x, y. + // The destination rectangle is also width x height, but has the top-left corner at x + tx, y + ty. + // The copy happens as if it is using an intermediary buffer, thus even if the source and destination + // intersect, the order in which characters are copied must not change the result. + NN_GPU_COPY, + // Fills a rectangle + // The rectangle's top-left corner is at x, y, and its dimensions are width x height. + // The character it should be filled with has its unicode codepoint stored in codepoint. + NN_GPU_FILL, + + // VRAM buffers (always blazing fast) + + // Should return the current active buffer. + // 0 for the screen, or if there is no screen. + // The result should be stored in x. + NN_GPU_GETACTIVEBUFFER, + // Switches the active buffer to a new one, stored in x. + NN_GPU_SETACTIVEBUFFER, + // Gets a buffer by index in an imaginary list containing all of them. + // The index is in x, the buffer is output in y. + // If y is 0, the sequence is assumed to end. + NN_GPU_BUFFERS, + // Allocates a buffer. + // The buffer sizes are in width and height, with 0 x 0 meaning max resolution (default). + // This consumes exactly width * height VRAM. + // The new buffer should be put in x. + // If there was not enough VRAM for this, x can be set to 0. + NN_GPU_ALLOCBUFFER, + // Frees a buffer. + // The buffer is stored in x. + // This releases the same VRAM that the buffer consumed when allocated. + NN_GPU_FREEBUFFER, + // Frees all buffers. The free VRAM should be equal to the total VRAM after this. + NN_GPU_FREEBUFFERS, + // Gets memory info about the GPU. + // x should be set to the amount of free VRAM available. + NN_GPU_FREEMEM, + // Gets the size of a buffer, stored in x. + // The size should be stored in width and height. + NN_GPU_GETBUFFERSIZE, + // Copy a region between buffers or between the screen and buffers. + // The destination buffer is stored in dest. If 0, it refers to the screen. + // The source buffer is stored in src. If 0, it refers to the screen. + // x, y, width and height define the source rectangle, in the same way as in fill, to copy from the source buffer. + // tx, ty refer to the top-left corner for the destination rectangle, in the destination buffer. It has the same width + // and height as the source rectangle. + // Screen-to-screen copies are illegal and checked, no need to worry about handling them. + NN_GPU_BITBLT, +} nn_GPUAction; + +typedef struct nn_GPURequest { + void *userdata; + void *instance; + nn_Computer *computer; + struct nn_GPU *gpuConf; + nn_GPUAction action; + int x; + int y; + int width; + int height; + union { + struct { + int tx; + int ty; + }; + nn_codepoint codepoint; + char *text; + }; + int dest; + int src; +} nn_GPURequest; + +typedef struct nn_GPU { + // the minimum between these and the screen's + // are the maximum width/height/depth supported. + int maxWidth; + int maxHeight; + char maxDepth; + // this is in pixels. + size_t totalVRAM; + // amount of times copy can be called before running out of budget. + int copyPerTick; + // amount of times fill can be called before running out of budget. + int fillPerTick; + // amount of times set can be called before running out of budget. + int setPerTick; + // amount of times setForeground can be called before running out of budget. + int setForegroundPerTick; + // amount of times setBackground can be called before running out of budget. + int setBackgroundPerTick; + // energy per non-space set. + double energyPerWrite; + // energy per space set. + double energyPerClear; +} nn_GPU; + +typedef nn_Exit nn_GPUHandler(nn_GPURequest *req); + +// 1 GPU tier for every screen. +extern nn_GPU nn_defaultGPUs[4]; + +nn_ComponentState *nn_createGPU(nn_Universe *universe, const nn_GPU *gpu, nn_GPUHandler *handler, void *userdata); + +// Colors and palettes. +// Do note that the + +// The NeoNucleus 2-bit palette +extern int nn_palette2[4]; + +// The NeoNucleus 3-bit palette +extern int nn_palette3[8]; + +// The OC 4-bit palette. +extern int nn_ocpalette4[16]; + +// The Minecraft 4-bit palette, using dye colors. +extern int nn_mcpalette4[16]; + +// The OC 8-bit palette. +extern int nn_ocpalette8[256]; + +// initializes the contents of the palettes. +void nn_initPalettes(); + +// Expensive. +// Maps a color to the closest match in a palette. +int nn_mapColor(int color, int *palette, size_t len); +// Expensive. +// Maps a color within a given depth. +// ocCompatible only matters for 4-bit, and determines whether to use the OC palette or the MC palette. +// Invalid depths behave identically to 24-bit, in which case the color is left unchanged. +int nn_mapDepth(int color, int depth, bool ocCompatible); + +// the name of a depth, if valid. +// If invalid, NULL is returned, thus this can be used to check +// if a depth is valid as well. +// Valid depths are 1, 2, 3, 4, 8, 16 and 24. const char *nn_depthName(int depth); -double nn_colorDistance(int colorA, int colorB); -int nn_mapColor(int color, int *palette, int paletteSize); +// Signal helpers -int nn_mapDepth(int color, int depth, nn_bool_t legacy); -void nn_getStd4BitPalette(int color[16]); -void nn_getStd8BitPalette(int color[256]); +// common mouse buttons, not an exhaustive list +#define NN_BUTTON_LEFT 0 +#define NN_BUTTON_RIGHT 1 +#define NN_BUTTON_MIDDLE 2 -// Std4bit uses actual MC dye colors, except for white and black -// Legacy uses OC's versions that were brightened -void nn_getLegacy4BitPalette(int color[16]); +// OC keycodes +// taken from https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/resources/assets/opencomputers/loot/openos/lib/core/full_keyboard.lua +#define NN_KEY_UNKNOWN 0 +#define NN_KEY_1 0x02 +#define NN_KEY_2 0x03 +#define NN_KEY_3 0x04 +#define NN_KEY_4 0x05 +#define NN_KEY_5 0x06 +#define NN_KEY_6 0x07 +#define NN_KEY_7 0x08 +#define NN_KEY_8 0x09 +#define NN_KEY_9 0x0A +#define NN_KEY_0 0x0B +#define NN_KEY_A 0x1E +#define NN_KEY_B 0x30 +#define NN_KEY_C 0x2E +#define NN_KEY_D 0x20 +#define NN_KEY_E 0x12 +#define NN_KEY_F 0x21 +#define NN_KEY_G 0x22 +#define NN_KEY_H 0x23 +#define NN_KEY_I 0x17 +#define NN_KEY_J 0x24 +#define NN_KEY_K 0x25 +#define NN_KEY_L 0x26 +#define NN_KEY_M 0x32 +#define NN_KEY_N 0x31 +#define NN_KEY_O 0x18 +#define NN_KEY_P 0x19 +#define NN_KEY_Q 0x10 +#define NN_KEY_R 0x13 +#define NN_KEY_S 0x1F +#define NN_KEY_T 0x14 +#define NN_KEY_U 0x16 +#define NN_KEY_V 0x2F +#define NN_KEY_W 0x11 +#define NN_KEY_X 0x2D +#define NN_KEY_Y 0x15 +#define NN_KEY_Z 0x2C -void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel); -nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y); +#define NN_KEY_APOSTROPHE 0x28 +#define NN_KEY_AT 0x91 +#define NN_KEY_BACK 0x0E +#define NN_KEY_BACKSLASH 0x2B +// caps-lock +#define NN_KEY_CAPITAL 0x3A +#define NN_KEY_COLON 0x92 +#define NN_KEY_COMMA 0x33 +#define NN_KEY_ENTER 0x1C +#define NN_KEY_EQUALS 0x0D +// accent grave +#define NN_KEY_GRAVE 0x29 +#define NN_KEY_LBRACKET 0x1A +#define NN_KEY_LCONTROL 0x1D +// left alt +#define NN_KEY_LMENU 0x38 +#define NN_KEY_LSHIFT 0x2A +#define NN_KEY_MINUS 0x0C +#define NN_KEY_NUMLOCK 0x45 +#define NN_KEY_PAUSE 0xC5 +#define NN_KEY_PERIOD 0x34 +#define NN_KEY_RBRACKET 0x1B +#define NN_KEY_RCONTROL 0x9D +// right alt +#define NN_KEY_RMENU 0xB8 +#define NN_KEY_RSHIFT 0x36 +// scroll lock +#define NN_KEY_SCROLL 0x46 +#define NN_KEY_SEMICOLON 0x27 +#define NN_KEY_SLASH 0x35 +#define NN_KEY_SPACE 0x39 +#define NN_KEY_STOP 0x95 +#define NN_KEY_TAB 0x0F +#define NN_KEY_UNDERLINE 0x93 -nn_bool_t nn_isDirty(nn_screen *screen); -void nn_setDirty(nn_screen *screen, nn_bool_t dirty); -nn_bool_t nn_isPrecise(nn_screen *screen); -void nn_setPrecise(nn_screen *screen, nn_bool_t precise); -nn_bool_t nn_isTouchModeInverted(nn_screen *screen); -void nn_setTouchModeInverted(nn_screen *screen, nn_bool_t touchModeInverted); -nn_bool_t nn_isOn(nn_screen *buffer); -void nn_setOn(nn_screen *buffer, nn_bool_t on); +#define NN_KEY_UP 0xC8 +#define NN_KEY_DOWN 0xD0 +#define NN_KEY_LEFT 0xCB +#define NN_KEY_RIGHT 0xCD +#define NN_KEY_HOME 0xC7 +#define NN_KEY_END 0xCF +#define NN_KEY_PAGEUP 0xC9 +#define NN_KEY_PAGEDOWN 0xD1 +#define NN_KEY_INSERT 0xD2 +#define NN_KEY_DELETE 0xD3 -nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen); +#define NN_KEY_F1 0x3B +#define NN_KEY_F2 0x3C +#define NN_KEY_F3 0x3D +#define NN_KEY_F4 0x3E +#define NN_KEY_F5 0x3F +#define NN_KEY_F6 0x40 +#define NN_KEY_F7 0x41 +#define NN_KEY_F8 0x42 +#define NN_KEY_F9 0x43 +#define NN_KEY_F10 0x44 +#define NN_KEY_F11 0x57 +#define NN_KEY_F12 0x58 +#define NN_KEY_F13 0x64 +#define NN_KEY_F14 0x65 +#define NN_KEY_F15 0x66 +#define NN_KEY_F16 0x67 +#define NN_KEY_F17 0x68 +#define NN_KEY_F18 0x69 +#define NN_KEY_F19 0x71 -typedef struct nn_gpuControl { - // VRAM Buffers - int totalVRAM; - int maximumBufferCount; - int defaultBufferWidth; - int defaultBufferHeight; +#define NN_KEY_KANA 0x70 +#define NN_KEY_KANJI 0x94 +#define NN_KEY_CONVERT 0x79 +#define NN_KEY_NOCONVERT 0x7B +#define NN_KEY_YEN 0x7D +#define NN_KEY_CIRCUMFLEX 0x90 +#define NN_KEY_AX 0x96 - // Calls per tick, only applicable to screens - double screenCopyPerTick; - double screenFillPerTick; - double screenSetsPerTick; - double bitbltPerTick; // for bitblit +#define NN_KEY_NUMPAD0 0x52 +#define NN_KEY_NUMPAD1 0x4F +#define NN_KEY_NUMPAD2 0x50 +#define NN_KEY_NUMPAD3 0x51 +#define NN_KEY_NUMPAD4 0x4B +#define NN_KEY_NUMPAD5 0x4C +#define NN_KEY_NUMPAD6 0x4D +#define NN_KEY_NUMPAD7 0x47 +#define NN_KEY_NUMPAD8 0x48 +#define NN_KEY_NUMPAD9 0x49 +#define NN_KEY_NUMPADMUL 0x37 +#define NN_KEY_NUMPADDIV 0xB5 +#define NN_KEY_NUMPADSUB 0x4A +#define NN_KEY_NUMPADADD 0x4E +#define NN_KEY_NUMPADDECIMAL 0x53 +#define NN_KEY_NUMPADCOMMA 0xB3 +#define NN_KEY_NUMPADENTER 0x9C +#define NN_KEY_NUMPADEQUALS 0x8D - // Heat - double heatPerPixelChange; - double heatPerPixelReset; - double heatPerVRAMChange; +// pushes a screen_resized signal +nn_Exit nn_pushScreenResized(nn_Computer *computer, const char *screenAddress, int newWidth, int newHeight); +// pushes a touch signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushTouch(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); +// pushes a drag signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushDrag(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); +// pushes a drop signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player); +// pushes a scroll signal +// A positive direction usually means up, a negative one usually means down. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player); +// pushes a walk signal +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player); - // Energy - double energyPerPixelChange; - double energyPerPixelReset; - double energyPerVRAMChange; -} nn_gpuControl; +// pushes a key_down event +// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. +// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. +// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushKeyDown(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); +// pushes a key_up event +// charcode is the unicode code-point of the typed character. It should be uppercase/lowercase depending on shift or capslock. +// keycode is an OC-specific keycode, and should be from the NN_KEY_* constants. +// player is the name of the player which used the keyboard. Some programs use it for splitscreen games. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushKeyUp(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player); +// pushes a clipboard event +// clipboard should be a NULL-terminated string. +// NN does no truncation of the contents, but it is best to limit it. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, const char *player); +// pushes a clipboard event +// len is the length of the clipboard. +// NN does no truncation of the contents, but it is best to limit it. +// The signal is checked, as in, the player must be a user of the computer if users are defined. +nn_Exit nn_pushLClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, size_t len, const char *player); -// the control is COPIED. -nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control); +// TODO: the remaining vanilla ones in https://ocdoc.cil.li/component:signals -typedef struct nn_networkControl { - double packetBytesPerTick; - double heatPerFullPacket; - double energyPerFullPacket; -} nn_networkControl; - -nn_bool_t nn_wakeupMatches(nn_value *values, nn_size_t valueLen, const char *wakeUp, nn_bool_t fuzzy); - -// NULL on success, error string on failure -// this *retains* all of those values, meaning you must drop them after call this function -const char *nn_pushNetworkMessage(nn_computer *computer, nn_address receiver, nn_address sender, nn_size_t port, double distance, nn_value *values, nn_size_t valueLen); - -typedef struct nn_modemTable { - void *userdata; - void (*deinit)(void *userdata); - - // basic limits - - nn_bool_t wireless; - nn_size_t maxValues; - nn_size_t maxPacketSize; - nn_size_t maxOpenPorts; - - // ports - - nn_bool_t (*isOpen)(void *userdata, nn_size_t port, nn_errorbuf_t err); - nn_bool_t (*open)(void *userdata, nn_size_t port, nn_errorbuf_t err); - // port NN_PORT_CLOSEALL means close all - nn_bool_t (*close)(void *userdata, nn_size_t port, nn_errorbuf_t err); - nn_size_t (*getPorts)(void *userdata, nn_size_t *ports, nn_errorbuf_t err); - - // messages - - // Address is NULL if broadcasting - nn_bool_t (*send)(void *userdata, nn_address address, nn_size_t port, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err); - - // signal strength - double maxStrength; - double (*getStrength)(void *userdata, nn_errorbuf_t err); - double (*setStrength)(void *userdata, double strength, nn_errorbuf_t err); - - // wake message - nn_size_t (*getWakeMessage)(void *userdata, char *buf, nn_errorbuf_t err); - nn_size_t (*setWakeMessage)(void *userdata, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err); -} nn_modemTable; - -typedef struct nn_modem nn_modem; - -typedef struct nn_debugLoopbackNetworkOpts { - nn_computer *computer; - nn_address address; - nn_size_t maxValues; - nn_size_t maxPacketSize; - nn_size_t maxOpenPorts; - double maxStrength; - nn_bool_t isWireless; -} nn_debugLoopbackNetworkOpts; - -nn_modem *nn_newModem(nn_Context *context, nn_modemTable table, nn_networkControl control); -nn_modem *nn_debugLoopbackModem(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control); -nn_guard *nn_getModemLock(nn_modem *modem); -void nn_retainModem(nn_modem *modem); -nn_bool_t nn_destroyModem(nn_modem *modem); - -nn_component *nn_addModem(nn_computer *computer, nn_address address, int slot, nn_modem *modem); - -typedef struct nn_tunnelTable { - void *userdata; - void (*deinit)(void *userdata); - - nn_size_t maxValues; - nn_size_t maxPacketSize; - - void (*send)(void *userdata, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err); - nn_size_t (*getChannel)(void *userdata, char *buf, nn_errorbuf_t err); - nn_size_t (*getWakeMessage)(void *userdata, char *buf, nn_errorbuf_t err); - nn_size_t (*setWakeMessage)(void *userdata, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err); -} nn_tunnelTable; - -typedef struct nn_tunnel nn_tunnel; - -nn_tunnel *nn_newTunnel(nn_Context *context, nn_tunnelTable table, nn_networkControl control); -nn_tunnel *nn_debugLoopbackTunnel(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control); -nn_guard *nn_getTunnelLock(nn_tunnel *tunnel); -void nn_retainTunnel(nn_tunnel *tunnel); -nn_bool_t nn_destroyTunnel(nn_tunnel *tunnel); - -nn_component *nn_addTunnel(nn_computer *computer, nn_address address, int slot, nn_tunnel *tunnel); - -typedef struct nn_diskDriveTable { - void *userdata; - void (*deinit)(void *userdata); - - // velocity is 0 or less for "default" - void (*eject)(void *userdata, double velocity, nn_errorbuf_t err); - nn_bool_t (*isEmpty)(void *userdata); - nn_address (*media)(void *userdata, nn_Alloc *alloc, nn_errorbuf_t err); -} nn_diskDriveTable; - -typedef struct nn_diskDrive nn_diskDrive; - -nn_diskDrive *nn_newDiskDrive(nn_Context *context, nn_diskDriveTable table); -nn_guard *nn_getDiskDriveLock(nn_diskDrive *diskDrive); -void nn_retainDiskDrive(nn_diskDrive *diskDrive); -nn_bool_t nn_destroyDiskDrive(nn_diskDrive *diskDrive); - -nn_component *nn_addDiskDrive(nn_computer *computer, nn_address address, int slot, nn_diskDrive *diskDrive); - -typedef struct nn_hologram nn_hologram; - -nn_hologram *nn_newHologram(nn_Context *context, int pallette_len, int width_x, int width_z, int height, int depth); -nn_guard *nn_getHologramLock(nn_hologram *hologram); -void nn_retainHologram(nn_hologram *hologram); -nn_bool_t nn_destroyHologram(nn_hologram *hologram); - -nn_component *nn_addHologram(nn_computer *computer, nn_address address, int slot, nn_hologram *hologram); - -void nn_hologram_clear(nn_hologram *hologram); -int nn_hologram_get(nn_hologram *hologram, int x, int y, int z); -void nn_hologram_set(nn_hologram *hologram, int x, int y, int z, int value); -void nn_hologram_fill(nn_hologram *hologram, int x, int z, int minY, int maxY, int value); -void nn_hologram_copy(nn_hologram *hologram, int x, int z, int sx, int sz, int tx, int tz); -float nn_hologram_getScale(nn_hologram *hologram); -void nn_hologram_setScale(nn_hologram *hologram, float value); -void nn_hologram_getTranslation(nn_hologram *hologram, double *x, double *y, double *z); -void nn_hologram_setTranslation(nn_hologram *hologram, double x, double y, double z); -int nn_hologram_maxDepth(nn_hologram *hologram); -int nn_hologram_getPaletteColor(nn_hologram *hologram, int index); -int nn_hologram_setPaletteColor(nn_hologram *hologram, int index, int value); - -typedef struct nn_externalComputerTable_t { - void *userdata; - void (*deinit)(void *userdata); - - nn_bool_t (*start)(void *userdata, nn_computer *requester, nn_errorbuf_t err); - nn_bool_t (*stop)(void *userdata, nn_computer *requester, nn_errorbuf_t err); - nn_bool_t (*isRunning)(void *userdata, nn_computer *requester, nn_errorbuf_t err); - void (*beep)(void *userdata, nn_computer *requester, double freq, double duration, double volume, nn_errorbuf_t err); - void (*crash)(void *userdata, nn_computer *requester, nn_errorbuf_t err); - nn_architecture *(*getArchitecture)(void *userdata, nn_computer *requester, nn_errorbuf_t err); - void (*getDeviceInfo)(void *userdata, nn_deviceInfoList_t *list, nn_computer *requester, nn_errorbuf_t err); - nn_bool_t (*isRobot)(void *userdata, nn_computer *requester, nn_errorbuf_t err); -} nn_externalComputerTable_t; - -typedef struct nn_externalComputer_t nn_externalComputer_t; - -// An external computer is a computer component. -// It may refer to the current computer (counter-intuitively) -// It may exist when the computer it is refering too has no running state (aka is powered off) -nn_externalComputer_t *nn_newExternalComputer(nn_Context *ctx, nn_externalComputerTable_t table); -nn_guard *nn_externalComputer_getLock(nn_externalComputer_t *external); -void nn_externalComputer_retain(nn_externalComputer_t *external); -nn_bool_t nn_externalComputer_destroy(nn_externalComputer_t *external); - -nn_component *nn_externalComputer_addTo(nn_computer *computer, nn_address address, int slot, nn_externalComputer_t *external); - -#ifdef __cplusplus // c++ sucks +#ifdef __cplusplus } #endif diff --git a/src/resource.c b/src/resource.c deleted file mode 100644 index f499b18..0000000 --- a/src/resource.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "resource.h" - -nn_resourceTable_t *nn_resource_newTable(nn_Context *ctx, nn_resourceDestructor_t *dtor) { - nn_resourceTable_t *t = nn_alloc(&ctx->allocator, sizeof(nn_resourceTable_t)); - if(t == NULL) return NULL; - t->dtor = dtor; - t->methodCount = 0; - return t; -} - -nn_resourceMethod_t *nn_resource_addMethod(nn_resourceTable_t *table, const char *methodName, nn_resourceMethodCallback_t *method, const char *doc) { - if(table->methodCount == NN_MAX_METHODS) return NULL; - nn_resourceMethod_t *m = &table->methods[table->methodCount]; - table->methodCount++; - nn_Alloc *a = &table->ctx.allocator; - m->name = nn_strdup(a, methodName); - m->doc = nn_strdup(a, doc); - m->callback = method; - m->condition = NULL; - return m; -} - -void nn_resource_setUserdata(nn_resourceMethod_t *method, void *methodUserdata) { - method->userdata = methodUserdata; -} - -void nn_resource_setCondition(nn_resourceMethod_t *method, nn_resourceMethodCondition_t *methodCondition) { - method->condition = methodCondition; -} diff --git a/src/resource.h b/src/resource.h deleted file mode 100644 index 04bf41a..0000000 --- a/src/resource.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef NN_RESOURCE -#define NN_RESOURCE - -#include "neonucleus.h" - -typedef struct nn_resourceMethod_t { - const char *name; - const char *doc; - void *userdata; - nn_resourceMethodCallback_t *callback; - nn_resourceMethodCondition_t *condition; -} nn_resourceMethod_t; - -typedef struct nn_resourceTable_t { - nn_Context ctx; - nn_resourceDestructor_t *dtor; - nn_size_t methodCount; - nn_resourceMethod_t methods[NN_MAX_METHODS]; -} nn_resourceTable_t; - -#endif diff --git a/src/sandbox.lua b/src/sandbox.lua deleted file mode 100644 index edb05de..0000000 --- a/src/sandbox.lua +++ /dev/null @@ -1,455 +0,0 @@ --- sandbox stuff - -local function copy(v, p) - if type(v) == "table" then - local t = {} - for key, val in pairs(v) do - t[key] = copy(val) - end - if p then - for key, val in pairs(p) do - t[key] = copy(val) - end - end - return t - else - return v - end -end - -local function spcall(f, ...) - local t = {pcall(f, ...)} - if t[1] then - return table.unpack(t, 2) - end - return nil, t[2] -end - -local function nextDeadline() - return computer.uptime() + 5 -end - -local bubbleYield = false -local timeout = nextDeadline() - -local function tooLong() - return computer.uptime() >= timeout -end - -local tooLongWithoutYielding = "too long without yielding" - -local function nextHeatUp() - return computer.uptime() + math.random() * 2 + 0.1 -end - -local heatInc = nextHeatUp() - -debug.sethook(function() - if computer.uptime() >= heatInc then - heatInc = nextHeatUp() - computer.addHeat(math.random() * 3) - end - - if tooLong() and not bubbleYield then - bubbleYield = true - error(tooLongWithoutYielding) -- here it is an actual string - end -end, "c", 100000) -- no bogo mips, the check is cheap anyways - -local function resume(co, val1, ...) - while true do - local t = {coroutine.resume(co, val1, ...)} - if bubbleYield then -- yield was meaningless - coroutine.yield() -- carry through - else - return table.unpack(t) -- yield the user cares about - end - end -end - -local function yield() - bubbleYield = true - coroutine.yield() -end - -local function ensureYields() - if bubbleYield then - coroutine.yield() - end -end - -local function checkArg(idx, v, ...) - local bad = true - local n = select("#", ...) - for i=1,n do - local t = select(i, ...) - if type(v) == t then bad = false break end - end - if not bad then return end - local msg = string.format("bad argument #%d (%s expected, got %s)", idx, table.concat({...}, " or "), type(v)) - error(msg, 3) -end - -local libcomponent - -local componentProxy = { - __pairs = function(self) - local method - return function() - method = next(self, method) - if method then - return method, self[method] - end - end - end, -} - -local componentCallback = { - __call = function(self, ...) - return libcomponent.invoke(self.address, self.name, ...) - end, - __tostring = function(self) - return libcomponent.doc(self.address, self.name) or "undocumented" - end -} - -libcomponent = { - invoke = function(addr, method, ...) - checkArg(1, addr, "string") - checkArg(2, method, "string") - - while true do - local r = {pcall(component.invoke, addr, method, ...)} - computer.clearError() - - -- in this situation, either the temperature is above 100 C and we throttle - -- or the call budget has been filled and we dont care - if computer.isOverheating() or computer.isOverworked() then - local ok = pcall(yield) - assert(ok, "component explicitly requested to be suspended") - end - - if computer.getState() == states.blackout then - -- oops, powerout - local ok = pcall(yield) - assert(ok, "blackout") - end - - if computer.getState() == states.busy then - -- busy gets to try again - computer.setState(states.running) - else - if r[1] then - return table.unpack(r, 2) - end - return nil, r[2] - end - end - end, - list = function(filter, exact) - checkArg(1, filter, "string", "nil") - local t = component.list() - local list = t - if filter then - list = {} - for addr, kind in pairs(t) do - if type(exact) == "boolean" and exact then - if kind == filter then - list[addr] = kind - end - elseif rawequal(exact, "pattern") then - if string.match(kind, filter) then - list[addr] = kind - end - else - if string.find(kind, filter, nil, true) then - list[addr] = kind - end - end - end - end - - local key = nil - return setmetatable(list, { - __call = function() - key = next(list, key) - if key then - return key, list[key] - end - end, - }) - end, - methods = component.methods, - fields = component.fields, - doc = component.doc, - slot = component.slot, - type = component.type, - proxy = function(addr) - checkArg(1, addr, "string") - if not component.type(addr) then return nil, "no such component" end - local proxy = setmetatable({ - address = addr, - type = component.type(addr), - slot = component.slot(addr), - fields = {}, - }, componentProxy) - local methods = component.methods(addr) - for method in pairs(methods) do - proxy[method] = setmetatable({address = addr, name = method}, componentCallback) - end - return proxy - end, -} - -local libcomputer = { - isRobot = function() - return libcomponent.list("robot", true) ~= nil - end, - address = computer.address, - tmpAddress = computer.tmpAddress, - usedMemory = computer.usedMemory, - freeMemory = computer.freeMemory, - totalMemory = computer.totalMemory, - uptime = computer.uptime, - energy = computer.energy, - maxEnergy = computer.maxEnergy, - users = computer.users, - -- these 2 are not actually implemented - -- TODO: implement them - addUser = computer.addUser, - removeUser = computer.removeUser, - - shutdown = function(reboot) - computer.setState(reboot and states.REPEAT or states.closing) - yield() - end, - pushSignal = computer.pushSignal, - pullSignal = function(timeout) - local deadline = computer.uptime() + (type(timeout) == "number" and timeout or math.huge) - - repeat - yield() -- give executor a chance to give us stuff - local s = table.pack(computer.popSignal()) - if s.n > 0 then - return table.unpack(s) - end - until computer.uptime() >= deadline - end, - beep = computer.beep, - getDeviceInfo = computer.getDeviceInfo, - getProgramLocations = function() - return {} -- yup - end, - - getArchitectures = computer.getArchitectures, - getArchitecture = computer.getArchitecture, - setArchitecture = function(...) - computer.setArchitecture(...) -- also sets state to SWITCH - yield() - end, - getTemperature = computer.getTemperature, -} - -local sandbox -sandbox = { - assert = assert, - error = error, - getmetatable = function(t) - if type(t) == "string" then -- HUGE security problem - return nil -- fixed - end - return getmetatable(t) - end, - ipairs = ipairs, - load = function(ld, source, _, env) -- mode is ignored as bytecode is just fully illegal for now - return load(ld, source, "t", env or sandbox) - end, - next = next, - pairs = pairs, - pcall = function(...) - if tooLong() then - yield() - return false, tooLongWithoutYielding - end - local t = {pcall(...)} - ensureYields() -- if it took too long, this will make it yield - return table.unpack(t) - end, - rawequal = rawequal, - rawget = rawget, - rawlen = rawlen, - rawset = rawset, - select = select, - setmetatable = function(t, mt) - if type(mt) ~= "table" then - return setmetatable(t, mt) - end - -- we do mutate the metatable but this field shouldn't exist anyways - mt.__gc = nil - return setmetatable(t, mt) - end, - tonumber = tonumber, - tostring = tostring, - type = type, - _VERSION = _VERSION, - xpcall = function(f, msgh, ...) - checkArg(1, f, "function") - checkArg(2, msgh, "function") - - -- to prevent infinite loops we simply terminate the error handler if it took too long. - local function errorCapture(ff, ...) - --ensureYields() -- you can't yield in xpcall... - -- Immediately dont care - if tooLong() then - return nil, tooLongWithoutYielding - end - -- This would mean you shutdown in the errorCapture. - -- In vanilla OC, that does nothing. - -- In here, it returns a suspended error and then yields eventually. - if bubbleYield then - return nil, "suspended" - end - - return xpcall(ff, function(...) - if tooLong() then - return tooLongWithoutYielding - else - return select(2, errorCapture(msgh, ...)) - end - end, ...) - end - - local t = {errorCapture(f, ...)} - pcall(ensureYields) -- it can fail if we are doing xpcall in xpcall. - return table.unpack(t) - end, - - coroutine = { - create = coroutine.create, - resume = resume, - running = coroutine.running, - status = coroutine.status, - yield = coroutine.yield, - wrap = function(f) - -- uses the correct resume - local co = coroutine.create(f) - return function(...) - local result = {resume(co, ...)} - if result[1] then - return table.unpack(result, 2) - else - error(result[2], 0) - end - end - end, - isyieldable = coroutine.isyieldable, - }, - - string = copy(string), - table = copy(table), - math = copy(math, { - -- patch table - atan2 = math.atan, - ldexp = function(a, e) return a * (2.0 ^ e) end, - pow = function(a, b) return a ^ b end, - }), - - os = { - clock = os.clock, - date = os.date, - difftime = function(t2, t1) return t2 - t1 end, -- thanks UNIX - time = function(t) - checkArg(1, t, "table", "nil") - return os.time(t) - end, - }, - - debug = { - getinfo = function(...) - local result = debug.getinfo(...) - if result then - return { - source = result.source, - short_src = result.short_src, - linedefined = result.linedefined, - lastlinedefined = result.lastlinedefined, - what = result.what, - currentline = result.currentline, - nups = result.nups, - nparams = result.nparams, - isvararg = result.isvararg, - name = result.name, - namewhat = result.namewhat, - istailcall = result.istailcall, - -- believe it or not, this IS NOT safe. - -- They may use this to re-call machine.lua which would reset the hook and timeout. - -- TODO: make this safe. - --func = result.func, - } - end - end, - traceback = debug.traceback, - -- we only allow the first return, aka not the value. - -- Otherwise, some dumb shmuck could do nasty stuff. - -- TODO: make them not need this. - getlocal = function(...) return (debug.getlocal(...)) end, - getupvalue = function(...) return (debug.getupvalue(...)) end, - }, - - utf8 = copy(utf8), - unicode = copy(unicode, { - isWide = function(s) - local c = unicode.sub(s, 1, 1) - return unicode.wlen(c) > unicode.len(c) - end, - upper = string.upper, - lower = string.lower, - wtrunc = function (str,space) - space = space - 1 - return unicode.sub(str, 1, space) - end, - }), - checkArg = checkArg, - component = libcomponent, - computer = libcomputer, - debugprint = print, -} -sandbox._G = sandbox - -local function bootstrap() - local eeprom = libcomponent.list("eeprom")() - assert(eeprom, "no eeprom") - - local code = libcomponent.invoke(eeprom, "get") - assert(code and #code > 0, "empty eeprom") - - return assert(load(code, "=bios", "t", sandbox)) -end - -coroutine.yield() -- startup delay - -local f = bootstrap() -local co = coroutine.create(f) - -local gcInterval = 0.25 -local lastGC = computer.uptime() - -while true do - timeout = nextDeadline() - bubbleYield = false - - if computer.uptime() - lastGC >= gcInterval then - collectgarbage("collect") - lastGC = computer.uptime() - end - - local ok, err = coroutine.resume(co) - - if not ok then - error(debug.traceback(co, err), 0) - elseif coroutine.status(co) == "dead" then - error("computer halted", 0) - else - coroutine.yield() - end -end diff --git a/src/testLuaArch.c b/src/testLuaArch.c deleted file mode 100644 index 4c2618a..0000000 --- a/src/testLuaArch.c +++ /dev/null @@ -1,789 +0,0 @@ -#include "lua.h" -#include "lualib.h" -#include "lauxlib.h" -#include -#include -#include -#include "neonucleus.h" - -char *testLuaSandbox = NULL; - -#if LUA_VERSION_NUM == 502 - -#include - -// monkey patching - -bool lua_isinteger(lua_State *L, int i) { - if(lua_type(L, i) != LUA_TNUMBER) return false; - double x = lua_tonumber(L, i); - if(isinf(x)) return false; - if(isnan(x)) return false; - return trunc(x) == x; -} - -void lua_seti(lua_State *L, int arr, int i) { - lua_rawseti(L, arr, i); -} - -#endif - -typedef struct testLuaArch { - lua_State *L; - nn_computer *computer; - size_t memoryUsed; -} testLuaArch; - -testLuaArch *testLuaArch_get(lua_State *L) { - lua_getfield(L, LUA_REGISTRYINDEX, "archPtr"); - testLuaArch *arch = lua_touserdata(L, -1); - lua_pop(L, 1); - return arch; -} - -nn_Alloc *testLuaArch_getAlloc(lua_State *L) { - lua_getfield(L, LUA_REGISTRYINDEX, "archPtr"); - testLuaArch *arch = lua_touserdata(L, -1); - lua_pop(L, 1); - return nn_getAllocator(nn_getUniverse(arch->computer)); -} - -const char *testLuaArch_pushlstring(lua_State *L, const char *s, size_t len) { - if (lua_checkstack(L, 1) == 0) { - return NULL; - } - testLuaArch* arch = testLuaArch_get(L); - size_t freeSpace = nn_getComputerMemoryTotal(arch->computer) - arch->memoryUsed; - if ((len * 2 + 64) > freeSpace) { // dk how much space this really needs and its unstable so :/ - return NULL; - } - return lua_pushlstring(L, s, len); -} - -const char *testLuaArch_pushstring(lua_State *L, const char *s) { - size_t len = strlen(s); - return testLuaArch_pushlstring(L, s, len); -} - -void *testLuaArch_alloc(testLuaArch *arch, void *ptr, size_t osize, size_t nsize) { - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(arch->computer)); - if(nsize == 0) { - arch->memoryUsed -= osize; - nn_dealloc(alloc, ptr, osize); - return NULL; - } else { - size_t actualOldSize = osize; - if(ptr == NULL) actualOldSize = 0; - if(arch->memoryUsed - actualOldSize + nsize > nn_getComputerMemoryTotal(arch->computer)) { - return NULL; // OOM condition - } - arch->memoryUsed -= actualOldSize; - arch->memoryUsed += nsize; - return nn_resize(alloc, ptr, actualOldSize, nsize); - } -} - -nn_computer *testLuaArch_getComputer(lua_State *L) { - return testLuaArch_get(L)->computer; -} - -static nn_value testLuaArch_getValue(lua_State *L, int index) { - int type = lua_type(L, index); - nn_Alloc *alloc = testLuaArch_getAlloc(L); - - if(type == LUA_TBOOLEAN) { - return nn_values_boolean(lua_toboolean(L, index)); - } - if(lua_isnoneornil(L, index)) { - return nn_values_nil(); - } - if(type == LUA_TSTRING) { - size_t l = 0; - const char *s = lua_tolstring(L, index, &l); - return nn_values_string(alloc, s, l); - } - if(type == LUA_TNUMBER && lua_isinteger(L, index)) { - return nn_values_integer(lua_tointeger(L, index)); - } - if(type == LUA_TNUMBER && lua_isnumber(L, index)) { - return nn_values_number(lua_tonumber(L, index)); - } - //TODO: bring it back once I make everything else not leak memory - //luaL_argcheck(L, false, index, luaL_typename(L, index)); - return nn_values_nil(); -} - -static void testLuaArch_pushValue(lua_State *L, nn_value val) { - int t = nn_values_getType(val); - if(t == NN_VALUE_NIL) { - lua_pushnil(L); - return; - } - if(t == NN_VALUE_INT) { - lua_pushinteger(L, val.integer); - return; - } - if(t == NN_VALUE_NUMBER) { - lua_pushnumber(L, val.number); - return; - } - if(t == NN_VALUE_BOOL) { - lua_pushboolean(L, val.boolean); - return; - } - if(t == NN_VALUE_STR) { - lua_pushlstring(L, val.string->data, val.string->len); - return; - } - if(t == NN_VALUE_CSTR) { - lua_pushstring(L, val.cstring); - return; - } - if(t == NN_VALUE_ARRAY) { - nn_array *arr = val.array; - lua_createtable(L, arr->len, 0); - int luaVal = lua_gettop(L); - for(size_t i = 0; i < arr->len; i++) { - testLuaArch_pushValue(L, arr->values[i]); - lua_seti(L, luaVal, i+1); - } - return; - } - if(t == NN_VALUE_TABLE) { - nn_table *tbl = val.table; - lua_createtable(L, 0, tbl->len); - int luaVal = lua_gettop(L); - for(size_t i = 0; i < tbl->len; i++) { - testLuaArch_pushValue(L, tbl->pairs[i].key); - testLuaArch_pushValue(L, tbl->pairs[i].val); - lua_settable(L, luaVal); - } - return; - } - luaL_error(L, "invalid return type: %d", t); -} - -static int testLuaArch_computer_clearError(lua_State *L) { - testLuaArch *s = testLuaArch_get(L); - nn_clearError(s->computer); - return 0; -} - -static int testLuaArch_computer_usedMemory(lua_State *L) { - testLuaArch *s = testLuaArch_get(L); - lua_pushinteger(L, s->memoryUsed); - return 1; -} - -static int testLuaArch_computer_freeMemory(lua_State *L) { - testLuaArch *s = testLuaArch_get(L); - lua_pushinteger(L, nn_getComputerMemoryTotal(s->computer) - s->memoryUsed); - return 1; -} - -static int testLuaArch_computer_totalMemory(lua_State *L) { - testLuaArch *s = testLuaArch_get(L); - lua_pushinteger(L, nn_getComputerMemoryTotal(s->computer)); - return 1; -} - -static int testLuaArch_computer_address(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushstring(L, nn_getComputerAddress(c)); - return 1; -} - -static int testLuaArch_computer_tmpAddress(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushstring(L, nn_getTmpAddress(c)); - return 1; -} - -static int testLuaArch_computer_uptime(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushnumber(L, nn_getUptime(c)); - return 1; -} - -// TODO: beep -static int testLuaArch_computer_beep(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - // defaults - double frequency = 200; - double duration = 0.25; - double volume = 1; - - if(lua_type(L, 1) == LUA_TNUMBER) { - frequency = lua_tonumber(L, 1); - } - if(lua_type(L, 2) == LUA_TNUMBER) { - duration = lua_tonumber(L, 2); - } - if(lua_type(L, 3) == LUA_TNUMBER) { - volume = lua_tonumber(L, 3); - } - - nn_computer_setBeep(c, frequency, duration, volume); - return 0; -} - -static int testLuaArch_computer_energy(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushnumber(L, nn_getEnergy(c)); - return 1; -} - -static int testLuaArch_computer_maxEnergy(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushnumber(L, nn_getEnergy(c)); - return 1; -} - -static int testLuaArch_computer_getArchitecture(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushstring(L, nn_getArchitecture(c)->archName); - return 1; -} - -static int testLuaArch_computer_getArchitectures(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_createtable(L, 3, 0); - int arr = lua_gettop(L); - size_t i = 0; - while(true) { - nn_architecture *arch = nn_getSupportedArchitecture(c, i); - if(arch == NULL) break; - i++; - lua_pushstring(L, arch->archName); - lua_seti(L, arr, i); - } - return 1; -} - -static int testLuaArch_computer_setArchitecture(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - const char *requested = luaL_checkstring(L, 1); - for(size_t i = 0;; i++) { - nn_architecture *arch = nn_getSupportedArchitecture(c, i); - if(arch == NULL) break; - if(strcmp(arch->archName, requested) == 0) { - nn_setState(c, NN_STATE_SWITCH); - nn_setNextArchitecture(c, arch); - return 0; - } - } - luaL_error(L, "unsupported architecture: %s", requested); - return 0; -} - -static int testLuaArch_computer_isOverworked(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushboolean(L, nn_isOverworked(c)); - return 1; -} - -static int testLuaArch_computer_isOverheating(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushboolean(L, nn_isOverheating(c)); - return 1; -} - -static int testLuaArch_computer_getTemperature(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushnumber(L, nn_getTemperature(c)); - return 1; -} - -static int testLuaArch_computer_addHeat(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - double n = luaL_checknumber(L, 1); - nn_addHeat(c, n); - return 0; -} - -static int testLuaArch_computer_pushSignal(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - luaL_checkstring(L, 1); - int argc = lua_gettop(L); - if(argc > NN_MAX_ARGS) luaL_error(L, "too many arguments"); - nn_value args[argc]; - for(size_t i = 0; i < argc; i++) { - args[i] = testLuaArch_getValue(L, i+1); - } - const char *err = nn_pushSignal(c, args, argc); - if(err != NULL) { - for(size_t i = 0; i < argc; i++) { - nn_values_drop(args[i]); - } - luaL_error(L, "%s", err); - return 0; - } - return 0; -} - -static int testLuaArch_computer_popSignal(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - size_t retc = nn_signalSize(c); - for(size_t i = 0; i < retc; i++) { - testLuaArch_pushValue(L, nn_fetchSignalValue(c, i)); - } - nn_popSignal(c); - return retc; -} - -static int testLuaArch_computer_users(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - size_t i = 0; - while(true) { - const char *name = nn_indexUser(c, i); - if(name == NULL) break; - lua_pushstring(L, name); - i++; - } - return i; -} - -static int testLuaArch_computer_getState(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_pushinteger(L, nn_getState(c)); - return 1; -} - -static int testLuaArch_computer_setState(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - int s = luaL_checkinteger(L, 1); - nn_setState(c, s); - return 1; -} - -static int testLuaArch_computer_getDeviceInfo(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - - nn_deviceInfoList_t *list = nn_getComputerDeviceInfoList(c); - nn_size_t deviceCount = nn_getDeviceCount(list); - - lua_createtable(L, 0, deviceCount); - int infoTable = lua_gettop(L); - - for(nn_size_t i = 0; i < deviceCount; i++) { - nn_deviceInfo_t *info = nn_getDeviceInfoAt(list, i); - lua_createtable(L, 0, 16); - int deviceTable = lua_gettop(L); - - nn_size_t j = 0; - while(true) { - const char *value = NULL; - const char *key = nn_iterateDeviceInfoKeys(info, j, &value); - j++; - if(key == NULL) break; - lua_pushstring(L, value); - lua_setfield(L, deviceTable, key); - } - - lua_setfield(L, infoTable, nn_getDeviceInfoAddress(info)); - } - - return 1; -} - -static int testLuaArch_component_list(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - lua_createtable(L, 0, 10); - size_t iter = 0; - int list = lua_gettop(L); - while(true) { - nn_component *component = nn_iterComponent(c, &iter); - if(component == NULL) break; - nn_componentTable *table = nn_getComponentTable(component); - nn_address addr = nn_getComponentAddress(component); - const char *type = nn_getComponentType(table); - - lua_pushstring(L, type); - lua_setfield(L, list, addr); - } - return 1; -} - -static int testLuaArch_component_doc(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - const char *addr = luaL_checkstring(L, 1); - const char *method = luaL_checkstring(L, 2); - nn_component *component = nn_findComponent(c, (char *)addr); - if(component == NULL) { - lua_pushnil(L); - lua_pushstring(L, "no such component"); - return 2; - } - const char *doc = nn_methodDoc(nn_getComponentTable(component), method); - if(doc == NULL) { - lua_pushnil(L); - } else { - lua_pushstring(L, doc); - } - return 1; -} - -static int testLuaArch_component_fields(lua_State *L) { - lua_createtable(L, 0, 0); - return 1; -} - -static int testLuaArch_component_methods(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - const char *addr = luaL_checkstring(L, 1); - nn_component *component = nn_findComponent(c, (char *)addr); - if(component == NULL) { - lua_pushnil(L); - lua_pushstring(L, "no such component"); - return 2; - } - nn_componentTable *table = nn_getComponentTable(component); - lua_createtable(L, 0, 0); - int methods = lua_gettop(L); - - size_t i = 0; - while(true) { - bool direct = false; - const char *name = nn_getTableMethod(table, i, &direct); - if(name == NULL) break; - i++; - if(!nn_isMethodEnabled(component, name)) continue; - lua_pushboolean(L, direct); - lua_setfield(L, methods, name); - } - - return 1; -} - -static int testLuaArch_component_slot(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - const char *addr = luaL_checkstring(L, 1); - nn_component *component = nn_findComponent(c, (char *)addr); - if(component == NULL) { - lua_pushnil(L); - lua_pushstring(L, "no such component"); - return 2; - } - lua_pushinteger(L, nn_getComponentSlot(component)); - return 1; -} - -static int testLuaArch_component_type(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - const char *addr = luaL_checkstring(L, 1); - nn_component *component = nn_findComponent(c, (char *)addr); - if(component == NULL) { - lua_pushnil(L); - lua_pushstring(L, "no such component"); - return 2; - } - lua_pushstring(L, nn_getComponentType(nn_getComponentTable(component))); - return 1; -} - -static int testLuaArch_component_invoke(lua_State *L) { - nn_computer *c = testLuaArch_getComputer(L); - const char *addr = luaL_checkstring(L, 1); - const char *method = luaL_checkstring(L, 2); - int argc = lua_gettop(L) - 2; - nn_component *component = nn_findComponent(c, (char *)addr); - if(component == NULL) { - lua_pushnil(L); - lua_pushstring(L, "no such component"); - return 2; - } - nn_resetCall(c); - for(size_t i = 0; i < argc; i++) { - nn_addArgument(c, testLuaArch_getValue(L, 3 + i)); - } - if(!nn_invokeComponentMethod(component, method)) { - nn_resetCall(c); - lua_pushnil(L); - lua_pushstring(L, "no such method"); - return 2; - } - if(nn_getError(c) != NULL) { - nn_resetCall(c); - luaL_error(L, "%s", nn_getError(c)); - } - size_t retc = nn_getReturnCount(c); - for(size_t i = 0; i < retc; i++) { - testLuaArch_pushValue(L, nn_getReturn(c, i)); - } - nn_resetCall(c); - return retc; -} - -int testLuaArch_unicode_sub(lua_State *L) { - const char *s = luaL_checkstring(L, 1); - nn_Alloc *alloc = testLuaArch_getAlloc(L); - int start = luaL_checkinteger(L, 2); - int len = nn_unicode_lenPermissive(s); - if(len < 0) { - luaL_error(L, "length overflow"); - } - int stop = len; - if(lua_isinteger(L, 3)) { - stop = luaL_checkinteger(L, 3); - } - // OpenOS does this... - if(len == 0) { - lua_pushstring(L, ""); - return 1; - } - - if(start == 0) start = 1; - if(stop == 0) { - lua_pushstring(L, ""); - return 1; - } - if(start < 0) start = len + start + 1; - if(stop < 0) stop = len + stop + 1; - - if(stop >= len) { - stop = len; - } - - if(start > stop) { - lua_pushstring(L, ""); - return 1; - } - - nn_size_t startByte = nn_unicode_indexPermissive(s, start - 1); - nn_size_t termByte = nn_unicode_indexPermissive(s, stop); - const char *res = testLuaArch_pushlstring(L, s + startByte, termByte - startByte); - if (!res) { - luaL_error(L, "out of memory"); - } - - return 1; -} - -int testLuaArch_unicode_char(lua_State *L) { - int argc = lua_gettop(L); - nn_Alloc *alloc = testLuaArch_getAlloc(L); - unsigned int *codepoints = nn_alloc(alloc, sizeof(unsigned int) * argc); - if(codepoints == NULL) { - luaL_error(L, "out of memory"); - return 0; // tell lsp to shut the fuck up - } - for(int i = 0; i < argc; i++) { - int idx = i + 1; - if(!lua_isinteger(L, idx)) { - nn_dealloc(alloc, codepoints, sizeof(unsigned int) * argc); - luaL_argerror(L, idx, "integer expected"); - return 0; - } - codepoints[i] = lua_tointeger(L, idx); - } - char *s = nn_unicode_char(alloc, codepoints, argc); - const char *res = testLuaArch_pushstring(L, s); - nn_deallocStr(alloc, s); - nn_dealloc(alloc, codepoints, sizeof(unsigned int) * argc); - if (!res) { - luaL_error(L, "out of memory"); - } - return 1; -} - -int testLuaArch_unicode_len(lua_State *L) { - const char *s = luaL_checkstring(L, 1); - lua_pushinteger(L, nn_unicode_lenPermissive(s)); - return 1; -} - -int testLuaArch_unicode_wlen(lua_State *L) { - const char *s = luaL_checkstring(L, 1); - lua_pushinteger(L, nn_unicode_lenPermissive(s)); - return 1; -} - -void testLuaArch_loadEnv(lua_State *L) { - lua_createtable(L, 0, 10); - int computer = lua_gettop(L); - lua_pushcfunction(L, testLuaArch_computer_clearError); - lua_setfield(L, computer, "clearError"); - lua_pushcfunction(L, testLuaArch_computer_usedMemory); - lua_setfield(L, computer, "usedMemory"); - lua_pushcfunction(L, testLuaArch_computer_freeMemory); - lua_setfield(L, computer, "freeMemory"); - lua_pushcfunction(L, testLuaArch_computer_totalMemory); - lua_setfield(L, computer, "totalMemory"); - lua_pushcfunction(L, testLuaArch_computer_address); - lua_setfield(L, computer, "address"); - lua_pushcfunction(L, testLuaArch_computer_tmpAddress); - lua_setfield(L, computer, "tmpAddress"); - lua_pushcfunction(L, testLuaArch_computer_uptime); - lua_setfield(L, computer, "uptime"); - lua_pushcfunction(L, testLuaArch_computer_beep); - lua_setfield(L, computer, "beep"); - lua_pushcfunction(L, testLuaArch_computer_energy); - lua_setfield(L, computer, "energy"); - lua_pushcfunction(L, testLuaArch_computer_maxEnergy); - lua_setfield(L, computer, "maxEnergy"); - lua_pushcfunction(L, testLuaArch_computer_getArchitecture); - lua_setfield(L, computer, "getArchitecture"); - lua_pushcfunction(L, testLuaArch_computer_getArchitectures); - lua_setfield(L, computer, "getArchitectures"); - lua_pushcfunction(L, testLuaArch_computer_setArchitecture); - lua_setfield(L, computer, "setArchitecture"); - lua_pushcfunction(L, testLuaArch_computer_isOverworked); - lua_setfield(L, computer, "isOverworked"); - lua_pushcfunction(L, testLuaArch_computer_isOverheating); - lua_setfield(L, computer, "isOverheating"); - lua_pushcfunction(L, testLuaArch_computer_getTemperature); - lua_setfield(L, computer, "getTemperature"); - lua_pushcfunction(L, testLuaArch_computer_addHeat); - lua_setfield(L, computer, "addHeat"); - lua_pushcfunction(L, testLuaArch_computer_pushSignal); - lua_setfield(L, computer, "pushSignal"); - lua_pushcfunction(L, testLuaArch_computer_popSignal); - lua_setfield(L, computer, "popSignal"); - lua_pushcfunction(L, testLuaArch_computer_users); - lua_setfield(L, computer, "users"); - lua_pushcfunction(L, testLuaArch_computer_getState); - lua_setfield(L, computer, "getState"); - lua_pushcfunction(L, testLuaArch_computer_setState); - lua_setfield(L, computer, "setState"); - lua_pushcfunction(L, testLuaArch_computer_getDeviceInfo); - lua_setfield(L, computer, "getDeviceInfo"); - lua_setglobal(L, "computer"); - - lua_createtable(L, 0, 10); - int component = lua_gettop(L); - lua_pushcfunction(L, testLuaArch_component_list); - lua_setfield(L, component, "list"); - lua_pushcfunction(L, testLuaArch_component_doc); - lua_setfield(L, component, "doc"); - lua_pushcfunction(L, testLuaArch_component_fields); - lua_setfield(L, component, "fields"); - lua_pushcfunction(L, testLuaArch_component_methods); - lua_setfield(L, component, "methods"); - lua_pushcfunction(L, testLuaArch_component_invoke); - lua_setfield(L, component, "invoke"); - lua_pushcfunction(L, testLuaArch_component_slot); - lua_setfield(L, component, "slot"); - lua_pushcfunction(L, testLuaArch_component_type); - lua_setfield(L, component, "type"); - lua_setglobal(L, "component"); - - lua_createtable(L, 0, 7); - int states = lua_gettop(L); - lua_pushinteger(L, NN_STATE_SETUP); - lua_setfield(L, states, "setup"); - lua_pushinteger(L, NN_STATE_RUNNING); - lua_setfield(L, states, "running"); - lua_pushinteger(L, NN_STATE_BUSY); - lua_setfield(L, states, "busy"); - lua_pushinteger(L, NN_STATE_BLACKOUT); - lua_setfield(L, states, "blackout"); - lua_pushinteger(L, NN_STATE_CLOSING); - lua_setfield(L, states, "closing"); - lua_pushinteger(L, NN_STATE_REPEAT); - lua_setfield(L, states, "REPEAT"); - lua_pushinteger(L, NN_STATE_SWITCH); - lua_setfield(L, states, "switch"); - lua_setglobal(L, "states"); - - lua_createtable(L, 0, 20); - int unicode = lua_gettop(L); - lua_pushcfunction(L, testLuaArch_unicode_sub); - lua_setfield(L, unicode, "sub"); - lua_pushcfunction(L, testLuaArch_unicode_len); - lua_setfield(L, unicode, "len"); - lua_pushcfunction(L, testLuaArch_unicode_wlen); - lua_setfield(L, unicode, "wlen"); - lua_pushcfunction(L, testLuaArch_unicode_char); - lua_setfield(L, unicode, "char"); - lua_setglobal(L, "unicode"); -} - -testLuaArch *testLuaArch_setup(nn_computer *computer, void *_) { - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - testLuaArch *s = nn_alloc(alloc, sizeof(testLuaArch)); - if(s == NULL) return NULL; - s->memoryUsed = 0; - s->computer = computer; - lua_State *L = lua_newstate((void *)testLuaArch_alloc, s); - assert(L != NULL); - luaL_openlibs(L); - lua_pushlightuserdata(L, s); - lua_setfield(L, LUA_REGISTRYINDEX, "archPtr"); - s->L = L; - testLuaArch_loadEnv(L); - if(luaL_loadbufferx(L, testLuaSandbox, strlen(testLuaSandbox), "=machine.lua", "t") != LUA_OK) { - lua_close(L); - nn_dealloc(alloc, s, sizeof(testLuaArch)); - return NULL; - } - return s; -} - -void testLuaArch_teardown(nn_computer *computer, testLuaArch *arch, void *_) { - nn_Alloc *alloc = nn_getAllocator(nn_getUniverse(computer)); - lua_close(arch->L); - nn_dealloc(alloc, arch, sizeof(testLuaArch)); -} - -void testLuaArch_tick(nn_computer *computer, testLuaArch *arch, void *_) { - int ret = 0; -#if LUA_VERSION_NUM == 504 - int res = lua_resume(arch->L, NULL, 0, &ret); -#endif -#if LUA_VERSION_NUM == 503 - int res = lua_resume(arch->L, NULL, 0); -#endif -#if LUA_VERSION_NUM == 502 - int res = lua_resume(arch->L, NULL, 0); -#endif - if(res == LUA_OK) { - // machine halted, this is no good - lua_pop(arch->L, ret); - nn_setCError(computer, "machine halted"); - } else if(res == LUA_YIELD) { - lua_pop(arch->L, ret); - } else { - const char *s = lua_tostring(arch->L, -1); - nn_setError(computer, s); - lua_pop(arch->L, ret); - } -} - -size_t testLuaArch_getMemoryUsage(nn_computer *computer, testLuaArch *arch, void *_) { - return arch->memoryUsed; -} - -char *testLuaArch_serialize(nn_computer *computer, nn_Alloc *alloc, testLuaArch *arch, void *_, size_t *len) { - *len = 0; - return NULL; -} - -void testLuaArch_deserialize(nn_computer *computer, const char *data, size_t len, testLuaArch *arch, void *_) {} - -nn_architecture testLuaArchTable = { - .archName = "Lua Test", - .userdata = NULL, - .setup = (void *)testLuaArch_setup, - .teardown = (void *)testLuaArch_teardown, - .tick = (void *)testLuaArch_tick, - .getMemoryUsage = (void*)testLuaArch_getMemoryUsage, -}; - -nn_architecture *testLuaArch_getArchitecture(const char *sandboxPath) { - if(testLuaSandbox == NULL) { - FILE *f = fopen(sandboxPath, "r"); - if(f == NULL) return NULL; - fseek(f, 0, SEEK_END); - size_t l = ftell(f); - testLuaSandbox = malloc(l+1); - if(testLuaSandbox == NULL) { - fclose(f); - return NULL; - } - fseek(f, 0, SEEK_SET); - fread(testLuaSandbox, sizeof(char), l, f); - testLuaSandbox[l] = '\0'; - fclose(f); - } - return &testLuaArchTable; -} diff --git a/src/testLuaArch.h b/src/testLuaArch.h deleted file mode 100644 index 6dd6e75..0000000 --- a/src/testLuaArch.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef TEST_LUA_ARCH -#define TEST_LUA_ARCH - -#include "neonucleus.h" -nn_architecture *testLuaArch_getArchitecture(const char *sandboxPath); - -#endif diff --git a/src/tinycthread.c b/src/tinycthread.c deleted file mode 100644 index 624c179..0000000 --- a/src/tinycthread.c +++ /dev/null @@ -1,934 +0,0 @@ -// Not created by NeoNucleus project authors. -// It was not modified aside from the writing of this comment. - -/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- -Copyright (c) 2012 Marcus Geelnard -Copyright (c) 2013-2016 Evan Nemerson - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ - -#include "tinycthread.h" -#include - -/* Platform specific includes */ -#if defined(_TTHREAD_POSIX_) - #include - #include - #include - #include - #include -#elif defined(_TTHREAD_WIN32_) - #include - #include -#endif - -/* Standard, good-to-have defines */ -#ifndef NULL - #define NULL (void*)0 -#endif -#ifndef TRUE - #define TRUE 1 -#endif -#ifndef FALSE - #define FALSE 0 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - - -int mtx_init(mtx_t *mtx, int type) -{ -#if defined(_TTHREAD_WIN32_) - mtx->mAlreadyLocked = FALSE; - mtx->mRecursive = type & mtx_recursive; - mtx->mTimed = type & mtx_timed; - if (!mtx->mTimed) - { - InitializeCriticalSection(&(mtx->mHandle.cs)); - } - else - { - mtx->mHandle.mut = CreateMutex(NULL, FALSE, NULL); - if (mtx->mHandle.mut == NULL) - { - return thrd_error; - } - } - return thrd_success; -#else - int ret; - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - if (type & mtx_recursive) - { - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); - } - ret = pthread_mutex_init(mtx, &attr); - pthread_mutexattr_destroy(&attr); - return ret == 0 ? thrd_success : thrd_error; -#endif -} - -void mtx_destroy(mtx_t *mtx) -{ -#if defined(_TTHREAD_WIN32_) - if (!mtx->mTimed) - { - DeleteCriticalSection(&(mtx->mHandle.cs)); - } - else - { - CloseHandle(mtx->mHandle.mut); - } -#else - pthread_mutex_destroy(mtx); -#endif -} - -int mtx_lock(mtx_t *mtx) -{ -#if defined(_TTHREAD_WIN32_) - if (!mtx->mTimed) - { - EnterCriticalSection(&(mtx->mHandle.cs)); - } - else - { - switch (WaitForSingleObject(mtx->mHandle.mut, INFINITE)) - { - case WAIT_OBJECT_0: - break; - case WAIT_ABANDONED: - default: - return thrd_error; - } - } - - if (!mtx->mRecursive) - { - while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */ - mtx->mAlreadyLocked = TRUE; - } - return thrd_success; -#else - return pthread_mutex_lock(mtx) == 0 ? thrd_success : thrd_error; -#endif -} - -int mtx_timedlock(mtx_t *mtx, const struct timespec *ts) -{ -#if defined(_TTHREAD_WIN32_) - struct timespec current_ts; - DWORD timeoutMs; - - if (!mtx->mTimed) - { - return thrd_error; - } - - timespec_get(¤t_ts, TIME_UTC); - - if ((current_ts.tv_sec > ts->tv_sec) || ((current_ts.tv_sec == ts->tv_sec) && (current_ts.tv_nsec >= ts->tv_nsec))) - { - timeoutMs = 0; - } - else - { - timeoutMs = (DWORD)(ts->tv_sec - current_ts.tv_sec) * 1000; - timeoutMs += (ts->tv_nsec - current_ts.tv_nsec) / 1000000; - timeoutMs += 1; - } - - /* TODO: the timeout for WaitForSingleObject doesn't include time - while the computer is asleep. */ - switch (WaitForSingleObject(mtx->mHandle.mut, timeoutMs)) - { - case WAIT_OBJECT_0: - break; - case WAIT_TIMEOUT: - return thrd_timedout; - case WAIT_ABANDONED: - default: - return thrd_error; - } - - if (!mtx->mRecursive) - { - while(mtx->mAlreadyLocked) Sleep(1); /* Simulate deadlock... */ - mtx->mAlreadyLocked = TRUE; - } - - return thrd_success; -#elif defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS >= 200112L) && defined(_POSIX_THREADS) && (_POSIX_THREADS >= 200112L) - switch (pthread_mutex_timedlock(mtx, ts)) { - case 0: - return thrd_success; - case ETIMEDOUT: - return thrd_timedout; - default: - return thrd_error; - } -#else - int rc; - struct timespec cur, dur; - - /* Try to acquire the lock and, if we fail, sleep for 5ms. */ - while ((rc = pthread_mutex_trylock (mtx)) == EBUSY) { - timespec_get(&cur, TIME_UTC); - - if ((cur.tv_sec > ts->tv_sec) || ((cur.tv_sec == ts->tv_sec) && (cur.tv_nsec >= ts->tv_nsec))) - { - break; - } - - dur.tv_sec = ts->tv_sec - cur.tv_sec; - dur.tv_nsec = ts->tv_nsec - cur.tv_nsec; - if (dur.tv_nsec < 0) - { - dur.tv_sec--; - dur.tv_nsec += 1000000000; - } - - if ((dur.tv_sec != 0) || (dur.tv_nsec > 5000000)) - { - dur.tv_sec = 0; - dur.tv_nsec = 5000000; - } - - nanosleep(&dur, NULL); - } - - switch (rc) { - case 0: - return thrd_success; - case ETIMEDOUT: - case EBUSY: - return thrd_timedout; - default: - return thrd_error; - } -#endif -} - -int mtx_trylock(mtx_t *mtx) -{ -#if defined(_TTHREAD_WIN32_) - int ret; - - if (!mtx->mTimed) - { - ret = TryEnterCriticalSection(&(mtx->mHandle.cs)) ? thrd_success : thrd_busy; - } - else - { - ret = (WaitForSingleObject(mtx->mHandle.mut, 0) == WAIT_OBJECT_0) ? thrd_success : thrd_busy; - } - - if ((!mtx->mRecursive) && (ret == thrd_success)) - { - if (mtx->mAlreadyLocked) - { - LeaveCriticalSection(&(mtx->mHandle.cs)); - ret = thrd_busy; - } - else - { - mtx->mAlreadyLocked = TRUE; - } - } - return ret; -#else - return (pthread_mutex_trylock(mtx) == 0) ? thrd_success : thrd_busy; -#endif -} - -int mtx_unlock(mtx_t *mtx) -{ -#if defined(_TTHREAD_WIN32_) - mtx->mAlreadyLocked = FALSE; - if (!mtx->mTimed) - { - LeaveCriticalSection(&(mtx->mHandle.cs)); - } - else - { - if (!ReleaseMutex(mtx->mHandle.mut)) - { - return thrd_error; - } - } - return thrd_success; -#else - return pthread_mutex_unlock(mtx) == 0 ? thrd_success : thrd_error;; -#endif -} - -#if defined(_TTHREAD_WIN32_) -#define _CONDITION_EVENT_ONE 0 -#define _CONDITION_EVENT_ALL 1 -#endif - -int cnd_init(cnd_t *cond) -{ -#if defined(_TTHREAD_WIN32_) - cond->mWaitersCount = 0; - - /* Init critical section */ - InitializeCriticalSection(&cond->mWaitersCountLock); - - /* Init events */ - cond->mEvents[_CONDITION_EVENT_ONE] = CreateEvent(NULL, FALSE, FALSE, NULL); - if (cond->mEvents[_CONDITION_EVENT_ONE] == NULL) - { - cond->mEvents[_CONDITION_EVENT_ALL] = NULL; - return thrd_error; - } - cond->mEvents[_CONDITION_EVENT_ALL] = CreateEvent(NULL, TRUE, FALSE, NULL); - if (cond->mEvents[_CONDITION_EVENT_ALL] == NULL) - { - CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); - cond->mEvents[_CONDITION_EVENT_ONE] = NULL; - return thrd_error; - } - - return thrd_success; -#else - return pthread_cond_init(cond, NULL) == 0 ? thrd_success : thrd_error; -#endif -} - -void cnd_destroy(cnd_t *cond) -{ -#if defined(_TTHREAD_WIN32_) - if (cond->mEvents[_CONDITION_EVENT_ONE] != NULL) - { - CloseHandle(cond->mEvents[_CONDITION_EVENT_ONE]); - } - if (cond->mEvents[_CONDITION_EVENT_ALL] != NULL) - { - CloseHandle(cond->mEvents[_CONDITION_EVENT_ALL]); - } - DeleteCriticalSection(&cond->mWaitersCountLock); -#else - pthread_cond_destroy(cond); -#endif -} - -int cnd_signal(cnd_t *cond) -{ -#if defined(_TTHREAD_WIN32_) - int haveWaiters; - - /* Are there any waiters? */ - EnterCriticalSection(&cond->mWaitersCountLock); - haveWaiters = (cond->mWaitersCount > 0); - LeaveCriticalSection(&cond->mWaitersCountLock); - - /* If we have any waiting threads, send them a signal */ - if(haveWaiters) - { - if (SetEvent(cond->mEvents[_CONDITION_EVENT_ONE]) == 0) - { - return thrd_error; - } - } - - return thrd_success; -#else - return pthread_cond_signal(cond) == 0 ? thrd_success : thrd_error; -#endif -} - -int cnd_broadcast(cnd_t *cond) -{ -#if defined(_TTHREAD_WIN32_) - int haveWaiters; - - /* Are there any waiters? */ - EnterCriticalSection(&cond->mWaitersCountLock); - haveWaiters = (cond->mWaitersCount > 0); - LeaveCriticalSection(&cond->mWaitersCountLock); - - /* If we have any waiting threads, send them a signal */ - if(haveWaiters) - { - if (SetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) - { - return thrd_error; - } - } - - return thrd_success; -#else - return pthread_cond_broadcast(cond) == 0 ? thrd_success : thrd_error; -#endif -} - -#if defined(_TTHREAD_WIN32_) -static int _cnd_timedwait_win32(cnd_t *cond, mtx_t *mtx, DWORD timeout) -{ - DWORD result; - int lastWaiter; - - /* Increment number of waiters */ - EnterCriticalSection(&cond->mWaitersCountLock); - ++ cond->mWaitersCount; - LeaveCriticalSection(&cond->mWaitersCountLock); - - /* Release the mutex while waiting for the condition (will decrease - the number of waiters when done)... */ - mtx_unlock(mtx); - - /* Wait for either event to become signaled due to cnd_signal() or - cnd_broadcast() being called */ - result = WaitForMultipleObjects(2, cond->mEvents, FALSE, timeout); - if (result == WAIT_TIMEOUT) - { - /* The mutex is locked again before the function returns, even if an error occurred */ - mtx_lock(mtx); - return thrd_timedout; - } - else if (result == WAIT_FAILED) - { - /* The mutex is locked again before the function returns, even if an error occurred */ - mtx_lock(mtx); - return thrd_error; - } - - /* Check if we are the last waiter */ - EnterCriticalSection(&cond->mWaitersCountLock); - -- cond->mWaitersCount; - lastWaiter = (result == (WAIT_OBJECT_0 + _CONDITION_EVENT_ALL)) && - (cond->mWaitersCount == 0); - LeaveCriticalSection(&cond->mWaitersCountLock); - - /* If we are the last waiter to be notified to stop waiting, reset the event */ - if (lastWaiter) - { - if (ResetEvent(cond->mEvents[_CONDITION_EVENT_ALL]) == 0) - { - /* The mutex is locked again before the function returns, even if an error occurred */ - mtx_lock(mtx); - return thrd_error; - } - } - - /* Re-acquire the mutex */ - mtx_lock(mtx); - - return thrd_success; -} -#endif - -int cnd_wait(cnd_t *cond, mtx_t *mtx) -{ -#if defined(_TTHREAD_WIN32_) - return _cnd_timedwait_win32(cond, mtx, INFINITE); -#else - return pthread_cond_wait(cond, mtx) == 0 ? thrd_success : thrd_error; -#endif -} - -int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts) -{ -#if defined(_TTHREAD_WIN32_) - struct timespec now; - if (timespec_get(&now, TIME_UTC) == TIME_UTC) - { - unsigned long long nowInMilliseconds = now.tv_sec * 1000 + now.tv_nsec / 1000000; - unsigned long long tsInMilliseconds = ts->tv_sec * 1000 + ts->tv_nsec / 1000000; - DWORD delta = (tsInMilliseconds > nowInMilliseconds) ? - (DWORD)(tsInMilliseconds - nowInMilliseconds) : 0; - return _cnd_timedwait_win32(cond, mtx, delta); - } - else - return thrd_error; -#else - int ret; - ret = pthread_cond_timedwait(cond, mtx, ts); - if (ret == ETIMEDOUT) - { - return thrd_timedout; - } - return ret == 0 ? thrd_success : thrd_error; -#endif -} - -#if defined(_TTHREAD_WIN32_) -struct TinyCThreadTSSData { - void* value; - tss_t key; - struct TinyCThreadTSSData* next; -}; - -static tss_dtor_t _tinycthread_tss_dtors[1088] = { NULL, }; - -static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_head = NULL; -static _Thread_local struct TinyCThreadTSSData* _tinycthread_tss_tail = NULL; - -static void _tinycthread_tss_cleanup (void); - -static void _tinycthread_tss_cleanup (void) { - struct TinyCThreadTSSData* data; - int iteration; - unsigned int again = 1; - void* value; - - for (iteration = 0 ; iteration < TSS_DTOR_ITERATIONS && again > 0 ; iteration++) - { - again = 0; - for (data = _tinycthread_tss_head ; data != NULL ; data = data->next) - { - if (data->value != NULL) - { - value = data->value; - data->value = NULL; - - if (_tinycthread_tss_dtors[data->key] != NULL) - { - again = 1; - _tinycthread_tss_dtors[data->key](value); - } - } - } - } - - while (_tinycthread_tss_head != NULL) { - data = _tinycthread_tss_head->next; - free (_tinycthread_tss_head); - _tinycthread_tss_head = data; - } - _tinycthread_tss_head = NULL; - _tinycthread_tss_tail = NULL; -} - -static void NTAPI _tinycthread_tss_callback(PVOID h, DWORD dwReason, PVOID pv) -{ - (void)h; - (void)pv; - - if (_tinycthread_tss_head != NULL && (dwReason == DLL_THREAD_DETACH || dwReason == DLL_PROCESS_DETACH)) - { - _tinycthread_tss_cleanup(); - } -} - -#if defined(_MSC_VER) - #ifdef _M_X64 - #pragma const_seg(".CRT$XLB") - #else - #pragma data_seg(".CRT$XLB") - #endif - PIMAGE_TLS_CALLBACK p_thread_callback = _tinycthread_tss_callback; - #ifdef _M_X64 - #pragma data_seg() - #else - #pragma const_seg() - #endif -#else - PIMAGE_TLS_CALLBACK p_thread_callback __attribute__((section(".CRT$XLB"))) = _tinycthread_tss_callback; -#endif - -#endif /* defined(_TTHREAD_WIN32_) */ - -/** Information to pass to the new thread (what to run). */ -typedef struct { - thrd_start_t mFunction; /**< Pointer to the function to be executed. */ - void * mArg; /**< Function argument for the thread function. */ -} _thread_start_info; - -/* Thread wrapper function. */ -#if defined(_TTHREAD_WIN32_) -static DWORD WINAPI _thrd_wrapper_function(LPVOID aArg) -#elif defined(_TTHREAD_POSIX_) -static void * _thrd_wrapper_function(void * aArg) -#endif -{ - thrd_start_t fun; - void *arg; - int res; - - /* Get thread startup information */ - _thread_start_info *ti = (_thread_start_info *) aArg; - fun = ti->mFunction; - arg = ti->mArg; - - /* The thread is responsible for freeing the startup information */ - free((void *)ti); - - /* Call the actual client thread function */ - res = fun(arg); - -#if defined(_TTHREAD_WIN32_) - if (_tinycthread_tss_head != NULL) - { - _tinycthread_tss_cleanup(); - } - - return (DWORD)res; -#else - return (void*)(intptr_t)res; -#endif -} - -int thrd_create(thrd_t *thr, thrd_start_t func, void *arg) -{ - /* Fill out the thread startup information (passed to the thread wrapper, - which will eventually free it) */ - _thread_start_info* ti = (_thread_start_info*)malloc(sizeof(_thread_start_info)); - if (ti == NULL) - { - return thrd_nomem; - } - ti->mFunction = func; - ti->mArg = arg; - - /* Create the thread */ -#if defined(_TTHREAD_WIN32_) - *thr = CreateThread(NULL, 0, _thrd_wrapper_function, (LPVOID) ti, 0, NULL); -#elif defined(_TTHREAD_POSIX_) - if(pthread_create(thr, NULL, _thrd_wrapper_function, (void *)ti) != 0) - { - *thr = 0; - } -#endif - - /* Did we fail to create the thread? */ - if(!*thr) - { - free(ti); - return thrd_error; - } - - return thrd_success; -} - -thrd_t thrd_current(void) -{ -#if defined(_TTHREAD_WIN32_) - return GetCurrentThread(); -#else - return pthread_self(); -#endif -} - -int thrd_detach(thrd_t thr) -{ -#if defined(_TTHREAD_WIN32_) - /* https://stackoverflow.com/questions/12744324/how-to-detach-a-thread-on-windows-c#answer-12746081 */ - return CloseHandle(thr) != 0 ? thrd_success : thrd_error; -#else - return pthread_detach(thr) == 0 ? thrd_success : thrd_error; -#endif -} - -int thrd_equal(thrd_t thr0, thrd_t thr1) -{ -#if defined(_TTHREAD_WIN32_) - return GetThreadId(thr0) == GetThreadId(thr1); -#else - return pthread_equal(thr0, thr1); -#endif -} - -void thrd_exit(int res) -{ -#if defined(_TTHREAD_WIN32_) - if (_tinycthread_tss_head != NULL) - { - _tinycthread_tss_cleanup(); - } - - ExitThread((DWORD)res); -#else - pthread_exit((void*)(intptr_t)res); -#endif -} - -int thrd_join(thrd_t thr, int *res) -{ -#if defined(_TTHREAD_WIN32_) - DWORD dwRes; - - if (WaitForSingleObject(thr, INFINITE) == WAIT_FAILED) - { - return thrd_error; - } - if (res != NULL) - { - if (GetExitCodeThread(thr, &dwRes) != 0) - { - *res = (int) dwRes; - } - else - { - return thrd_error; - } - } - CloseHandle(thr); -#elif defined(_TTHREAD_POSIX_) - void *pres; - if (pthread_join(thr, &pres) != 0) - { - return thrd_error; - } - if (res != NULL) - { - *res = (int)(intptr_t)pres; - } -#endif - return thrd_success; -} - -int thrd_sleep(const struct timespec *duration, struct timespec *remaining) -{ -#if !defined(_TTHREAD_WIN32_) - int res = nanosleep(duration, remaining); - if (res == 0) { - return 0; - } else if (errno == EINTR) { - return -1; - } else { - return -2; - } -#else - struct timespec start; - DWORD t; - - timespec_get(&start, TIME_UTC); - - t = SleepEx((DWORD)(duration->tv_sec * 1000 + - duration->tv_nsec / 1000000 + - (((duration->tv_nsec % 1000000) == 0) ? 0 : 1)), - TRUE); - - if (t == 0) { - return 0; - } else { - if (remaining != NULL) { - timespec_get(remaining, TIME_UTC); - remaining->tv_sec -= start.tv_sec; - remaining->tv_nsec -= start.tv_nsec; - if (remaining->tv_nsec < 0) - { - remaining->tv_nsec += 1000000000; - remaining->tv_sec -= 1; - } - } - - return (t == WAIT_IO_COMPLETION) ? -1 : -2; - } -#endif -} - -void thrd_yield(void) -{ -#if defined(_TTHREAD_WIN32_) - Sleep(0); -#else - sched_yield(); -#endif -} - -int tss_create(tss_t *key, tss_dtor_t dtor) -{ -#if defined(_TTHREAD_WIN32_) - *key = TlsAlloc(); - if (*key == TLS_OUT_OF_INDEXES) - { - return thrd_error; - } - _tinycthread_tss_dtors[*key] = dtor; -#else - if (pthread_key_create(key, dtor) != 0) - { - return thrd_error; - } -#endif - return thrd_success; -} - -void tss_delete(tss_t key) -{ -#if defined(_TTHREAD_WIN32_) - struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*) TlsGetValue (key); - struct TinyCThreadTSSData* prev = NULL; - if (data != NULL) - { - if (data == _tinycthread_tss_head) - { - _tinycthread_tss_head = data->next; - } - else - { - prev = _tinycthread_tss_head; - if (prev != NULL) - { - while (prev->next != data) - { - prev = prev->next; - } - } - } - - if (data == _tinycthread_tss_tail) - { - _tinycthread_tss_tail = prev; - } - - free (data); - } - _tinycthread_tss_dtors[key] = NULL; - TlsFree(key); -#else - pthread_key_delete(key); -#endif -} - -void *tss_get(tss_t key) -{ -#if defined(_TTHREAD_WIN32_) - struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key); - if (data == NULL) - { - return NULL; - } - return data->value; -#else - return pthread_getspecific(key); -#endif -} - -int tss_set(tss_t key, void *val) -{ -#if defined(_TTHREAD_WIN32_) - struct TinyCThreadTSSData* data = (struct TinyCThreadTSSData*)TlsGetValue(key); - if (data == NULL) - { - data = (struct TinyCThreadTSSData*)malloc(sizeof(struct TinyCThreadTSSData)); - if (data == NULL) - { - return thrd_error; - } - - data->value = NULL; - data->key = key; - data->next = NULL; - - if (_tinycthread_tss_tail != NULL) - { - _tinycthread_tss_tail->next = data; - } - else - { - _tinycthread_tss_tail = data; - } - - if (_tinycthread_tss_head == NULL) - { - _tinycthread_tss_head = data; - } - - if (!TlsSetValue(key, data)) - { - free (data); - return thrd_error; - } - } - data->value = val; -#else - if (pthread_setspecific(key, val) != 0) - { - return thrd_error; - } -#endif - return thrd_success; -} - -#if defined(_TTHREAD_EMULATE_TIMESPEC_GET_) -int _tthread_timespec_get(struct timespec *ts, int base) -{ -#if defined(_TTHREAD_WIN32_) - struct _timeb tb; -#elif !defined(CLOCK_REALTIME) - struct timeval tv; -#endif - - if (base != TIME_UTC) - { - return 0; - } - -#if defined(_TTHREAD_WIN32_) - _ftime_s(&tb); - ts->tv_sec = (time_t)tb.time; - ts->tv_nsec = 1000000L * (long)tb.millitm; -#elif defined(CLOCK_REALTIME) - base = (clock_gettime(CLOCK_REALTIME, ts) == 0) ? base : 0; -#else - gettimeofday(&tv, NULL); - ts->tv_sec = (time_t)tv.tv_sec; - ts->tv_nsec = 1000L * (long)tv.tv_usec; -#endif - - return base; -} -#endif /* _TTHREAD_EMULATE_TIMESPEC_GET_ */ - -#if defined(_TTHREAD_WIN32_) -void call_once(once_flag *flag, void (*func)(void)) -{ - /* The idea here is that we use a spin lock (via the - InterlockedCompareExchange function) to restrict access to the - critical section until we have initialized it, then we use the - critical section to block until the callback has completed - execution. */ - while (flag->status < 3) - { - switch (flag->status) - { - case 0: - if (InterlockedCompareExchange (&(flag->status), 1, 0) == 0) { - InitializeCriticalSection(&(flag->lock)); - EnterCriticalSection(&(flag->lock)); - flag->status = 2; - func(); - flag->status = 3; - LeaveCriticalSection(&(flag->lock)); - return; - } - break; - case 1: - break; - case 2: - EnterCriticalSection(&(flag->lock)); - LeaveCriticalSection(&(flag->lock)); - break; - } - } -} -#endif /* defined(_TTHREAD_WIN32_) */ - -#ifdef __cplusplus -} -#endif diff --git a/src/tinycthread.h b/src/tinycthread.h deleted file mode 100644 index 2fc7f62..0000000 --- a/src/tinycthread.h +++ /dev/null @@ -1,479 +0,0 @@ -/* -*- mode: c; tab-width: 2; indent-tabs-mode: nil; -*- -Copyright (c) 2012 Marcus Geelnard -Copyright (c) 2013-2016 Evan Nemerson - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. -*/ - -#ifndef _TINYCTHREAD_H_ -#define _TINYCTHREAD_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -/** -* @file -* @mainpage TinyCThread API Reference -* -* @section intro_sec Introduction -* TinyCThread is a minimal, portable implementation of basic threading -* classes for C. -* -* They closely mimic the functionality and naming of the C11 standard, and -* should be easily replaceable with the corresponding standard variants. -* -* @section port_sec Portability -* The Win32 variant uses the native Win32 API for implementing the thread -* classes, while for other systems, the POSIX threads API (pthread) is used. -* -* @section misc_sec Miscellaneous -* The following special keywords are available: #_Thread_local. -* -* For more detailed information, browse the different sections of this -* documentation. A good place to start is: -* tinycthread.h. -*/ - -/* Which platform are we on? */ -#if !defined(_TTHREAD_PLATFORM_DEFINED_) - #if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) - #define _TTHREAD_WIN32_ - #else - #define _TTHREAD_POSIX_ - #endif - #define _TTHREAD_PLATFORM_DEFINED_ -#endif - -/* Activate some POSIX functionality (e.g. clock_gettime and recursive mutexes) */ -#if defined(_TTHREAD_POSIX_) - #undef _FEATURES_H - #if !defined(_GNU_SOURCE) - #define _GNU_SOURCE - #endif - #if !defined(_POSIX_C_SOURCE) || ((_POSIX_C_SOURCE - 0) < 199309L) - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L - #endif - #if !defined(_XOPEN_SOURCE) || ((_XOPEN_SOURCE - 0) < 500) - #undef _XOPEN_SOURCE - #define _XOPEN_SOURCE 500 - #endif - #define _XPG6 -#endif - -/* Generic includes */ -#include - -/* Platform specific includes */ -#if defined(_TTHREAD_POSIX_) - #include -#elif defined(_TTHREAD_WIN32_) - #ifndef WIN32_LEAN_AND_MEAN - #define WIN32_LEAN_AND_MEAN - #define __UNDEF_LEAN_AND_MEAN - #endif - #include - #ifdef __UNDEF_LEAN_AND_MEAN - #undef WIN32_LEAN_AND_MEAN - #undef __UNDEF_LEAN_AND_MEAN - #endif -#endif - -/* Compiler-specific information */ -#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - #define TTHREAD_NORETURN _Noreturn -#elif defined(__GNUC__) - #define TTHREAD_NORETURN __attribute__((__noreturn__)) -#else - #define TTHREAD_NORETURN -#endif - -/* If TIME_UTC is missing, provide it and provide a wrapper for - timespec_get. */ -#ifndef TIME_UTC -#define TIME_UTC 1 -#define _TTHREAD_EMULATE_TIMESPEC_GET_ - -#if defined(_TTHREAD_WIN32_) -struct _tthread_timespec { - time_t tv_sec; - long tv_nsec; -}; -#define timespec _tthread_timespec -#endif - -int _tthread_timespec_get(struct timespec *ts, int base); -#define timespec_get _tthread_timespec_get -#endif - -/** TinyCThread version (major number). */ -#define TINYCTHREAD_VERSION_MAJOR 1 -/** TinyCThread version (minor number). */ -#define TINYCTHREAD_VERSION_MINOR 2 -/** TinyCThread version (full version). */ -#define TINYCTHREAD_VERSION (TINYCTHREAD_VERSION_MAJOR * 100 + TINYCTHREAD_VERSION_MINOR) - -/** -* @def _Thread_local -* Thread local storage keyword. -* A variable that is declared with the @c _Thread_local keyword makes the -* value of the variable local to each thread (known as thread-local storage, -* or TLS). Example usage: -* @code -* // This variable is local to each thread. -* _Thread_local int variable; -* @endcode -* @note The @c _Thread_local keyword is a macro that maps to the corresponding -* compiler directive (e.g. @c __declspec(thread)). -* @note This directive is currently not supported on Mac OS X (it will give -* a compiler error), since compile-time TLS is not supported in the Mac OS X -* executable format. Also, some older versions of MinGW (before GCC 4.x) do -* not support this directive, nor does the Tiny C Compiler. -* @hideinitializer -*/ - -#if !(defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201102L)) && !defined(_Thread_local) - #if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__SUNPRO_CC) || defined(__IBMCPP__) - #define _Thread_local __thread - #else - #define _Thread_local __declspec(thread) - #endif -#elif defined(__GNUC__) && defined(__GNUC_MINOR__) && (((__GNUC__ << 8) | __GNUC_MINOR__) < ((4 << 8) | 9)) - #define _Thread_local __thread -#endif - -/* Macros */ -#if defined(_TTHREAD_WIN32_) -#define TSS_DTOR_ITERATIONS (4) -#else -#define TSS_DTOR_ITERATIONS PTHREAD_DESTRUCTOR_ITERATIONS -#endif - -/* Function return values */ -#define thrd_error 0 /**< The requested operation failed */ -#define thrd_success 1 /**< The requested operation succeeded */ -#define thrd_timedout 2 /**< The time specified in the call was reached without acquiring the requested resource */ -#define thrd_busy 3 /**< The requested operation failed because a tesource requested by a test and return function is already in use */ -#define thrd_nomem 4 /**< The requested operation failed because it was unable to allocate memory */ - -/* Mutex types */ -#define mtx_plain 0 -#define mtx_timed 1 -#define mtx_recursive 2 - -/* Mutex */ -#if defined(_TTHREAD_WIN32_) -typedef struct { - union { - CRITICAL_SECTION cs; /* Critical section handle (used for non-timed mutexes) */ - HANDLE mut; /* Mutex handle (used for timed mutex) */ - } mHandle; /* Mutex handle */ - int mAlreadyLocked; /* TRUE if the mutex is already locked */ - int mRecursive; /* TRUE if the mutex is recursive */ - int mTimed; /* TRUE if the mutex is timed */ -} mtx_t; -#else -typedef pthread_mutex_t mtx_t; -#endif - -/** Create a mutex object. -* @param mtx A mutex object. -* @param type Bit-mask that must have one of the following six values: -* @li @c mtx_plain for a simple non-recursive mutex -* @li @c mtx_timed for a non-recursive mutex that supports timeout -* @li @c mtx_plain | @c mtx_recursive (same as @c mtx_plain, but recursive) -* @li @c mtx_timed | @c mtx_recursive (same as @c mtx_timed, but recursive) -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int mtx_init(mtx_t *mtx, int type); - -/** Release any resources used by the given mutex. -* @param mtx A mutex object. -*/ -void mtx_destroy(mtx_t *mtx); - -/** Lock the given mutex. -* Blocks until the given mutex can be locked. If the mutex is non-recursive, and -* the calling thread already has a lock on the mutex, this call will block -* forever. -* @param mtx A mutex object. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int mtx_lock(mtx_t *mtx); - -/** Lock the given mutex, or block until a specific point in time. -* Blocks until either the given mutex can be locked, or the specified TIME_UTC -* based time. -* @param mtx A mutex object. -* @param ts A UTC based calendar time -* @return @ref The mtx_timedlock function returns thrd_success on success, or -* thrd_timedout if the time specified was reached without acquiring the -* requested resource, or thrd_error if the request could not be honored. -*/ -int mtx_timedlock(mtx_t *mtx, const struct timespec *ts); - -/** Try to lock the given mutex. -* The specified mutex shall support either test and return or timeout. If the -* mutex is already locked, the function returns without blocking. -* @param mtx A mutex object. -* @return @ref thrd_success on success, or @ref thrd_busy if the resource -* requested is already in use, or @ref thrd_error if the request could not be -* honored. -*/ -int mtx_trylock(mtx_t *mtx); - -/** Unlock the given mutex. -* @param mtx A mutex object. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int mtx_unlock(mtx_t *mtx); - -/* Condition variable */ -#if defined(_TTHREAD_WIN32_) -typedef struct { - HANDLE mEvents[2]; /* Signal and broadcast event HANDLEs. */ - unsigned int mWaitersCount; /* Count of the number of waiters. */ - CRITICAL_SECTION mWaitersCountLock; /* Serialize access to mWaitersCount. */ -} cnd_t; -#else -typedef pthread_cond_t cnd_t; -#endif - -/** Create a condition variable object. -* @param cond A condition variable object. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int cnd_init(cnd_t *cond); - -/** Release any resources used by the given condition variable. -* @param cond A condition variable object. -*/ -void cnd_destroy(cnd_t *cond); - -/** Signal a condition variable. -* Unblocks one of the threads that are blocked on the given condition variable -* at the time of the call. If no threads are blocked on the condition variable -* at the time of the call, the function does nothing and return success. -* @param cond A condition variable object. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int cnd_signal(cnd_t *cond); - -/** Broadcast a condition variable. -* Unblocks all of the threads that are blocked on the given condition variable -* at the time of the call. If no threads are blocked on the condition variable -* at the time of the call, the function does nothing and return success. -* @param cond A condition variable object. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int cnd_broadcast(cnd_t *cond); - -/** Wait for a condition variable to become signaled. -* The function atomically unlocks the given mutex and endeavors to block until -* the given condition variable is signaled by a call to cnd_signal or to -* cnd_broadcast. When the calling thread becomes unblocked it locks the mutex -* before it returns. -* @param cond A condition variable object. -* @param mtx A mutex object. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int cnd_wait(cnd_t *cond, mtx_t *mtx); - -/** Wait for a condition variable to become signaled. -* The function atomically unlocks the given mutex and endeavors to block until -* the given condition variable is signaled by a call to cnd_signal or to -* cnd_broadcast, or until after the specified time. When the calling thread -* becomes unblocked it locks the mutex before it returns. -* @param cond A condition variable object. -* @param mtx A mutex object. -* @param xt A point in time at which the request will time out (absolute time). -* @return @ref thrd_success upon success, or @ref thrd_timeout if the time -* specified in the call was reached without acquiring the requested resource, or -* @ref thrd_error if the request could not be honored. -*/ -int cnd_timedwait(cnd_t *cond, mtx_t *mtx, const struct timespec *ts); - -/* Thread */ -#if defined(_TTHREAD_WIN32_) -typedef HANDLE thrd_t; -#else -typedef pthread_t thrd_t; -#endif - -/** Thread start function. -* Any thread that is started with the @ref thrd_create() function must be -* started through a function of this type. -* @param arg The thread argument (the @c arg argument of the corresponding -* @ref thrd_create() call). -* @return The thread return value, which can be obtained by another thread -* by using the @ref thrd_join() function. -*/ -typedef int (*thrd_start_t)(void *arg); - -/** Create a new thread. -* @param thr Identifier of the newly created thread. -* @param func A function pointer to the function that will be executed in -* the new thread. -* @param arg An argument to the thread function. -* @return @ref thrd_success on success, or @ref thrd_nomem if no memory could -* be allocated for the thread requested, or @ref thrd_error if the request -* could not be honored. -* @note A thread’s identifier may be reused for a different thread once the -* original thread has exited and either been detached or joined to another -* thread. -*/ -int thrd_create(thrd_t *thr, thrd_start_t func, void *arg); - -/** Identify the calling thread. -* @return The identifier of the calling thread. -*/ -thrd_t thrd_current(void); - -/** Dispose of any resources allocated to the thread when that thread exits. - * @return thrd_success, or thrd_error on error -*/ -int thrd_detach(thrd_t thr); - -/** Compare two thread identifiers. -* The function determines if two thread identifiers refer to the same thread. -* @return Zero if the two thread identifiers refer to different threads. -* Otherwise a nonzero value is returned. -*/ -int thrd_equal(thrd_t thr0, thrd_t thr1); - -/** Terminate execution of the calling thread. -* @param res Result code of the calling thread. -*/ -TTHREAD_NORETURN void thrd_exit(int res); - -/** Wait for a thread to terminate. -* The function joins the given thread with the current thread by blocking -* until the other thread has terminated. -* @param thr The thread to join with. -* @param res If this pointer is not NULL, the function will store the result -* code of the given thread in the integer pointed to by @c res. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int thrd_join(thrd_t thr, int *res); - -/** Put the calling thread to sleep. -* Suspend execution of the calling thread. -* @param duration Interval to sleep for -* @param remaining If non-NULL, this parameter will hold the remaining -* time until time_point upon return. This will -* typically be zero, but if the thread was woken up -* by a signal that is not ignored before duration was -* reached @c remaining will hold a positive time. -* @return 0 (zero) on successful sleep, -1 if an interrupt occurred, -* or a negative value if the operation fails. -*/ -int thrd_sleep(const struct timespec *duration, struct timespec *remaining); - -/** Yield execution to another thread. -* Permit other threads to run, even if the current thread would ordinarily -* continue to run. -*/ -void thrd_yield(void); - -/* Thread local storage */ -#if defined(_TTHREAD_WIN32_) -typedef DWORD tss_t; -#else -typedef pthread_key_t tss_t; -#endif - -/** Destructor function for a thread-specific storage. -* @param val The value of the destructed thread-specific storage. -*/ -typedef void (*tss_dtor_t)(void *val); - -/** Create a thread-specific storage. -* @param key The unique key identifier that will be set if the function is -* successful. -* @param dtor Destructor function. This can be NULL. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -* @note On Windows, the @c dtor will definitely be called when -* appropriate for threads created with @ref thrd_create. It will be -* called for other threads in most cases, the possible exception being -* for DLLs loaded with LoadLibraryEx. In order to be certain, you -* should use @ref thrd_create whenever possible. -*/ -int tss_create(tss_t *key, tss_dtor_t dtor); - -/** Delete a thread-specific storage. -* The function releases any resources used by the given thread-specific -* storage. -* @param key The key that shall be deleted. -*/ -void tss_delete(tss_t key); - -/** Get the value for a thread-specific storage. -* @param key The thread-specific storage identifier. -* @return The value for the current thread held in the given thread-specific -* storage. -*/ -void *tss_get(tss_t key); - -/** Set the value for a thread-specific storage. -* @param key The thread-specific storage identifier. -* @param val The value of the thread-specific storage to set for the current -* thread. -* @return @ref thrd_success on success, or @ref thrd_error if the request could -* not be honored. -*/ -int tss_set(tss_t key, void *val); - -#if defined(_TTHREAD_WIN32_) - typedef struct { - LONG volatile status; - CRITICAL_SECTION lock; - } once_flag; - #define ONCE_FLAG_INIT {0,} -#else - #define once_flag pthread_once_t - #define ONCE_FLAG_INIT PTHREAD_ONCE_INIT -#endif - -/** Invoke a callback exactly once - * @param flag Flag used to ensure the callback is invoked exactly - * once. - * @param func Callback to invoke. - */ -#if defined(_TTHREAD_WIN32_) - void call_once(once_flag *flag, void (*func)(void)); -#else - #define call_once(flag,func) pthread_once(flag,func) -#endif - -#ifdef __cplusplus -} -#endif - -#endif /* _TINYTHREAD_H_ */ diff --git a/src/unicode.c b/src/unicode.c deleted file mode 100644 index e6f19ad..0000000 --- a/src/unicode.c +++ /dev/null @@ -1,435 +0,0 @@ -#include "neonucleus.h" - -// both tables copied from: https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/scala/li/cil/oc/util/FontUtils.scala - -static const unsigned char nn_unicode_charWidth_table[] = { - 16, 16, 16, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 16, 33, 16, 16, 16, 34, 35, 36, - 37, 38, 39, 40, 16, 16, 41, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 42, 43, 16, 16, 44, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 45, 16, 46, 47, 48, 49, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 50, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 51, 16, 16, 52, - 53, 16, 54, 55, 56, 16, 16, 16, 16, 16, 16, 57, 16, 16, 58, 16, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, - 69, 70, 16, 71, 72, 73, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 74, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 75, 76, 16, 16, 16, 77, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 78, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 79, 80, 16, 16, 16, 16, 16, 16, 16, 81, 16, 16, 16, 16, 16, 82, 83, 84, 16, 16, 16, 16, 16, 85, - 86, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 248, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 254, 255, 255, 255, 255, 191, 182, 0, 0, 0, 0, 0, 0, 0, 63, 0, 255, 23, 0, 0, 0, 0, 0, 248, 255, - 255, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 191, 159, 61, 0, 0, 0, 128, 2, 0, 0, 0, 255, 255, 255, - 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 1, 0, 0, 0, 0, 0, 0, 248, 15, 32, 0, 0, 192, 251, 239, 62, 0, 0, - 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 255, - 255, 7, 0, 0, 0, 0, 0, 0, 20, 254, 33, 254, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 16, 30, 32, 0, 0, 12, 0, 0, - 64, 6, 0, 0, 0, 0, 0, 0, 16, 134, 57, 2, 0, 0, 0, 35, 0, 6, 0, 0, 0, 0, 0, 0, 16, 190, 33, 0, 0, 12, 0, 0, - 252, 2, 0, 0, 0, 0, 0, 0, 144, 30, 32, 64, 0, 12, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 1, 32, 0, 0, 0, 0, 0, 0, 17, - 0, 0, 0, 0, 0, 0, 192, 193, 61, 96, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 144, 64, 48, 0, 0, 12, 0, 0, 0, 3, 0, - 0, 0, 0, 0, 0, 24, 30, 32, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 92, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 242, 7, 128, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 242, 31, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 160, - 2, 0, 0, 0, 0, 0, 0, 254, 127, 223, 224, 255, 254, 255, 255, 255, 31, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 224, 253, 102, 0, 0, 0, 195, 1, 0, 30, 0, 100, 32, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 28, 0, 0, 0, 12, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 176, 63, 64, 254, - 15, 32, 0, 0, 0, 0, 0, 120, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 135, 1, 4, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 128, 9, 0, 0, 0, 0, 0, 0, 64, 127, 229, 31, 248, 159, 0, 0, 0, 0, 0, 0, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, - 15, 0, 0, 0, 0, 0, 208, 23, 4, 0, 0, 0, 0, 248, 15, 0, 3, 0, 0, 0, 60, 59, 0, 0, 0, 0, 0, 0, 64, 163, 3, 0, 0, - 0, 0, 0, 0, 240, 207, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 247, 255, 253, 33, 16, - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, - 251, 0, 248, 0, 0, 0, 124, 0, 0, 0, 0, 0, 0, 223, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, - 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 3, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, - 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 247, 63, 0, 0, 0, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 68, 8, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 255, 255, 3, 128, 0, 0, 0, 0, 192, 63, 0, 0, 128, 255, 3, 0, - 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 200, 51, 0, 0, 0, 0, 32, 0, 0, - 0, 0, 0, 0, 0, 0, 126, 102, 0, 8, 16, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 157, 193, 2, 0, 0, 0, 0, 48, 64, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 33, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, - 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, - 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 110, 240, 0, - 0, 0, 0, 0, 135, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 192, 255, 1, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 255, 127, 0, 0, 0, 0, 0, 0, 128, - 3, 0, 0, 0, 0, 0, 120, 38, 0, 32, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 128, 239, 31, 0, 0, 0, 0, 0, 0, 0, 8, 0, 3, 0, - 0, 0, 0, 0, 192, 127, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 211, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 128, 248, 7, 0, 0, 3, 0, 0, 0, 0, 0, 0, 24, 1, 0, 0, 0, 192, 31, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 255, 92, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 248, 133, 13, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 176, 1, 0, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 248, 167, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 224, 188, 15, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 255, 6, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 240, 12, 1, 0, 0, 0, 254, 7, 0, 0, 0, 0, 248, 121, 128, 0, 126, 14, 0, 0, 0, 0, 0, 252, - 127, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 255, - 255, 252, 109, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 126, 180, 191, 0, 0, 0, 0, 0, 0, 0, 0, 0, 163, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 255, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 128, 7, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 3, 248, 255, 231, 15, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 127, 248, 255, 255, 255, 255, 255, 31, 32, 0, 16, 0, 0, 248, 254, 255, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 127, 255, 255, 249, 219, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 240, 7, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; -static const unsigned char nn_unicode_charWidth_wide_table[] = { - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 19, 16, 20, 21, 22, 16, 16, 16, 23, 16, 16, 24, 25, 26, 27, 28, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 29, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 30, 16, 16, 16, 16, 31, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 17, 32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 16, 16, 16, 33, - 34, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 35, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, - 17, 17, 17, 17, 17, 17, 36, 17, 17, 37, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 17, 38, 39, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, - 16, 16, 16, 16, 16, 16, 16, 40, 41, 42, 43, 44, 45, 46, 47, 16, 48, 49, 16, 16, 16, 16, - 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 0, 0, 0, 80, 184, 0, 0, 0, 0, 0, 0, 0, 224, - 0, 0, 0, 1, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 251, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 15, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 63, 0, 0, 0, 255, 15, 255, 255, 255, 255, - 255, 255, 255, 127, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 127, 254, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 224, 255, 255, 255, 255, 255, 254, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 7, 255, 255, 255, 255, 15, 0, - 255, 255, 255, 255, 255, 127, 255, 255, 255, 255, 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, - 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 31, 255, 255, 255, 255, 255, 255, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 255, 255, 31, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 3, 0, 0, 255, 255, 255, 255, 247, 255, 127, 15, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 7, 0, 255, 255, 255, 127, 0, 0, 0, 0, 0, - 0, 7, 0, 240, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 254, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 255, 255, 255, - 255, 255, 15, 255, 1, 3, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, - 1, 224, 191, 255, 255, 255, 255, 255, 255, 255, 255, 223, 255, 255, 15, 0, 255, 255, 255, 255, - 255, 135, 15, 0, 255, 255, 17, 255, 255, 255, 255, 255, 255, 255, 255, 127, 253, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 159, 255, 255, 255, 255, 255, 255, 255, 63, 0, 120, 255, 255, 255, 0, 0, 4, 0, 0, 96, 0, 16, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, - 255, 255, 255, 255, 255, 255, 63, 16, 39, 0, 0, 24, 240, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 15, 0, - 0, 0, 224, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 123, 252, 255, 255, 255, - 255, 231, 199, 255, 255, 255, 231, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 15, 7, 7, 0, 63, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static nn_bool_t nn_unicode_is_continuation(unsigned char byte) { - return (byte >> 6) == 0b10; -} - -nn_bool_t nn_unicode_isValidCodepoint(const char *s) { - if(s[0] <= 0x7F) { - return true; - } else if((s[0] >> 5) == 0b110) { - if (!nn_unicode_is_continuation(s[1])) { - return false; - } - } else if((s[0] >> 4) == 0b1110) { - if (!nn_unicode_is_continuation(s[1])) { - return false; - } - if (!nn_unicode_is_continuation(s[2])) { - return false; - } - } else if((s[0] >> 3) == 0b11110) { - if (!nn_unicode_is_continuation(s[1])) { - return false; - } - if (!nn_unicode_is_continuation(s[2])) { - return false; - } - if (!nn_unicode_is_continuation(s[3])) { - return false; - } - } else { - return false; - } - return true; -} - -nn_bool_t nn_unicode_validate(const char *b) { - const unsigned char* s = (const unsigned char*)b; - while (*s) { - if(s[0] <= 0x7F) { - s++; - } else if((s[0] >> 5) == 0b110) { - if (!nn_unicode_is_continuation(s[1])) { - return false; - } - s += 2; - } else if((s[0] >> 4) == 0b1110) { - if (!nn_unicode_is_continuation(s[1])) { - return false; - } - if (!nn_unicode_is_continuation(s[2])) { - return false; - } - s += 3; - } else if((s[0] >> 3) == 0b11110) { - if (!nn_unicode_is_continuation(s[1])) { - return false; - } - if (!nn_unicode_is_continuation(s[2])) { - return false; - } - if (!nn_unicode_is_continuation(s[3])) { - return false; - } - s += 4; - } else { - return false; - } - } - return true; -} - -// A general unicode library, which assumes unicode encoding. -// It is used to power the Lua architecture's Unicode API re-implementation. -// It can also just be used to deal with unicode. - -char *nn_unicode_char(nn_Alloc *alloc, unsigned int *codepoints, nn_size_t codepointCount) { - nn_size_t len = 0; - for (nn_size_t i = 0; i < codepointCount; i++) { - unsigned int codepoint = codepoints[i]; - len += nn_unicode_codepointSize(codepoint); - } - - char *buf = nn_alloc(alloc, len+1); - if (buf == NULL) return buf; - - nn_size_t j = 0; - for (nn_size_t i = 0; i < codepointCount; i++) { - int codepoint = codepoints[i]; - nn_size_t codepointLen = 0; - char c[NN_MAXIMUM_UNICODE_BUFFER]; - nn_unicode_codepointToChar(c, codepoint, &codepointLen); - nn_memcpy(buf + j, c, codepointLen); - j += codepointLen; - } - buf[j] = '\0'; - - return buf; -} - -unsigned int *nn_unicode_codepoints(nn_Alloc *alloc, const char *s, nn_size_t *len) { - nn_size_t l = nn_unicode_len(s); - unsigned int *buf = nn_alloc(alloc, sizeof(unsigned int) * l); - if(buf == NULL) return NULL; - if(len != NULL) *len = l; - nn_size_t cur = 0; - nn_size_t bufidx = 0; - while(s[cur] != 0) { - unsigned int point = nn_unicode_codepointAt(s, cur); - cur += nn_unicode_codepointSize(point); - buf[bufidx++] = point; - } - return buf; -} - -nn_size_t nn_unicode_len(const char *b) { - nn_size_t count = 0; - const unsigned char* s = (const unsigned char*)b; - while (*s) { - count++; - if(s[0] <= 0x7F) { - s++; - } else if((s[0] >> 5) == 0b110) { - s += 2; - } else if((s[0] >> 4) == 0b1110) { - s += 3; - } else if((s[0] >> 3) == 0b11110) { - s += 4; - } - } - return count; -} - -unsigned int nn_unicode_codepointAt(const char *s, nn_size_t byteOffset) { - unsigned int point = 0; - const unsigned char *b = (const unsigned char *)s + byteOffset; - - const unsigned char subpartMask = 0b111111; - // look into nn_unicode_codepointToChar as well. - if(b[0] <= 0x7F) { - return b[0]; - } else if((b[0] >> 5) == 0b110) { - point += ((unsigned int)(b[0] & 0b11111)) << 6; - point += ((unsigned int)(b[1] & subpartMask)); - } else if((b[0] >> 4) == 0b1110) { - point += ((unsigned int)(b[0] & 0b1111)) << 12; - point += ((unsigned int)(b[1] & subpartMask)) << 6; - point += ((unsigned int)(b[2] & subpartMask)); - } else if((b[0] >> 3) == 0b11110) { - point += ((unsigned int)(b[0] & 0b111)) << 18; - point += ((unsigned int)(b[1] & subpartMask)) << 12; - point += ((unsigned int)(b[2] & subpartMask)) << 6; - point += ((unsigned int)(b[3] & subpartMask)); - } - return point; -} - -nn_size_t nn_unicode_codepointSize(unsigned int codepoint) { - if (codepoint <= 0x007f) { - return 1; - } else if (codepoint <= 0x07ff) { - return 2; - } else if (codepoint <= 0xffff) { - return 3; - } else if (codepoint <= 0x10ffff) { - return 4; - } - - return 1; -} - -void nn_unicode_codepointToChar(char *buffer, unsigned int codepoint, nn_size_t *len) { - nn_size_t codepointSize = nn_unicode_codepointSize(codepoint); - if(len != NULL) *len = codepointSize; - - nn_memset(buffer, 0, 4); // Clear static array - - if (codepointSize == 1) { - buffer[0] = (char)codepoint; - } else if (codepointSize == 2) { - buffer[0] = 0b11000000 + ((codepoint >> 6) & 0b11111); - buffer[1] = 0b10000000 + (codepoint & 0b111111); - } else if (codepointSize == 3) { - buffer[0] = 0b11100000 + ((codepoint >> 12) & 0b1111); - buffer[1] = 0b10000000 + ((codepoint >> 6) & 0b111111); - buffer[2] = 0b10000000 + (codepoint & 0b111111); - } else if (codepointSize == 4) { - buffer[0] = 0b11110000 + ((codepoint >> 18) & 0b111); - buffer[1] = 0b10000000 + ((codepoint >> 12) & 0b111111); - buffer[2] = 0b10000000 + ((codepoint >> 6) & 0b111111); - buffer[3] = 0b10000000 + (codepoint & 0b111111); - } -} - -// copied straight from opencomputers and musl's libc -// https://github.com/MightyPirates/OpenComputers/blob/52da41b5e171b43fea80342dc75d808f97a0f797/src/main/scala/li/cil/oc/util/FontUtils.scala#L205 -// https://git.musl-libc.org/cgit/musl/tree/src/ctype/wcwidth.c -nn_size_t nn_unicode_charWidth(unsigned int codepoint) { - if (codepoint < 0xff) { - if (((codepoint + 1) & 0x7f) >= 0x21) { - return 1; - } else { - return 0; - } - } else if ((codepoint & 0xfffeffff) < 0xfffe) { - if ((nn_unicode_charWidth_table[nn_unicode_charWidth_table[codepoint>>8]*32+((codepoint&255)>>3)]>>(codepoint&7))&1) - return 0; - if ((nn_unicode_charWidth_wide_table[nn_unicode_charWidth_wide_table[codepoint>>8]*32+((codepoint&255)>>3)]>>(codepoint&7))&1) - return 2; - return 1; - } else if (codepoint-0x20000 < 0x20000) { - return 2; - } else if (codepoint == 0xe0001 || codepoint-0xe0020 < 0x5f || codepoint-0xe0100 < 0xef) { - return 0; - } - return 1; -} - -nn_size_t nn_unicode_wlen(const char *s) { - nn_size_t wlen = 0; - while (*s) { - unsigned int codepoint = nn_unicode_codepointAt(s, 0); - nn_size_t codepointSize = nn_unicode_codepointSize(codepoint); - wlen += nn_unicode_charWidth(codepoint); - s += codepointSize; - } - return wlen; -} - -// NOT IMPLEMENTED YET - -unsigned int nn_unicode_upperCodepoint(unsigned int codepoint); -char *nn_unicode_upper(nn_Alloc *alloc, const char *s); -unsigned int nn_unicode_lowerCodepoint(unsigned int codepoint); -char *nn_unicode_lower(nn_Alloc *alloc, const char *s); - -unsigned int nn_unicode_nextCodepointPermissive(const char *s, nn_size_t *index) { - nn_size_t i = *index; - if(nn_unicode_isValidCodepoint(s + i)) { - // TODO: handle edge-case where suboptimial encoding is used - unsigned int p = nn_unicode_codepointAt(s, i); - *index = i + nn_unicode_codepointSize(p); - return p; - } - unsigned int p = (unsigned char)s[i]; - *index = i + 1; - return p; -} - -nn_size_t nn_unicode_lenPermissive(const char *b) { - nn_size_t len = 0; - nn_size_t cur = 0; - while(b[cur]) { - nn_unicode_nextCodepointPermissive(b, &cur); - len++; - } - return len; -} - -nn_size_t nn_unicode_wlenPermissive(const char *s) { - nn_size_t wlen = 0; - nn_size_t cur = 0; - while (s[cur]) { - unsigned int codepoint = nn_unicode_nextCodepointPermissive(s, &cur); - wlen += nn_unicode_charWidth(codepoint); - } - return wlen; -} - -nn_intptr_t nn_unicode_indexPermissive(const char *s, nn_size_t codepointIndex) { - nn_size_t bytes = 0; - while(true) { - if(codepointIndex == 0) return bytes; - nn_unicode_nextCodepointPermissive(s, &bytes); - codepointIndex--; - } -} diff --git a/src/universe.c b/src/universe.c deleted file mode 100644 index 2c3aca3..0000000 --- a/src/universe.c +++ /dev/null @@ -1,75 +0,0 @@ -#include "neonucleus.h" -#include "universe.h" - -nn_universe *nn_newUniverse(nn_Context ctx) { - nn_universe *u = nn_alloc(&ctx.allocator, sizeof(nn_universe)); - if(u == NULL) return u; - u->ctx = ctx; - // we leave udata uninitialized because it does not matter - u->udataLen = 0; - return u; -} - -nn_Context *nn_getContext(nn_universe *universe) { - return &universe->ctx; -} - -nn_Alloc *nn_getAllocator(nn_universe *universe) { - return &universe->ctx.allocator; -} - -nn_Clock *nn_getClock(nn_universe *universe) { - return &universe->ctx.clock; -} - -nn_LockManager *nn_getLockManager(nn_universe *universe) { - return &universe->ctx.lockManager; -} - -nn_Rng *nn_getRng(nn_universe *universe) { - return &universe->ctx.rng; -} - -void nn_unsafeDeleteUniverse(nn_universe *universe) { - for(nn_size_t i = 0; i < universe->udataLen; i++) { - nn_deallocStr(&universe->ctx.allocator, universe->udata[i].name); - } - nn_dealloc(&universe->ctx.allocator, universe, sizeof(nn_universe)); -} - -void *nn_queryUserdata(nn_universe *universe, const char *name) { - for(nn_size_t i = 0; i < universe->udataLen; i++) { - if(nn_strcmp(universe->udata[i].name, name) == 0) { - return universe->udata[i].userdata; - } - } - return NULL; -} - -void nn_storeUserdata(nn_universe *universe, const char *name, void *data) { - if(universe->udataLen == NN_MAX_USERDATA) return; // prevent overflow - - nn_size_t idx = universe->udataLen; - char *allocName = nn_strdup(&universe->ctx.allocator, name); - if(allocName == NULL) return; - - universe->udata[idx].name = allocName; - universe->udata[idx].userdata = data; - universe->udataLen++; -} - -double nn_getTime(nn_universe *universe) { - return universe->ctx.clock.proc(universe->ctx.clock.userdata); -} - -void nn_loadCoreComponentTables(nn_universe *universe) { - nn_loadEepromTable(universe); - nn_loadFilesystemTable(universe); - nn_loadDriveTable(universe); - nn_loadScreenTable(universe); - nn_loadGraphicsCardTable(universe); - nn_loadKeyboardTable(universe); - nn_loadModemTable(universe); - nn_loadTunnelTable(universe); - nn_loadDiskDriveTable(universe); -} diff --git a/src/universe.h b/src/universe.h deleted file mode 100644 index 8fa7f01..0000000 --- a/src/universe.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef NEONUCLEUS_UNIVERSE_H -#define NEONUCLEUS_UNIVERSE_H - -#include "neonucleus.h" - -typedef struct nn_universe_udata { - char *name; - void *userdata; -} nn_universe_udata; - -typedef struct nn_universe { - nn_Context ctx; - nn_universe_udata udata[NN_MAX_USERDATA]; - nn_size_t udataLen; -} nn_universe; - -#endif diff --git a/src/utils.c b/src/utils.c deleted file mode 100644 index 97e1f7d..0000000 --- a/src/utils.c +++ /dev/null @@ -1,462 +0,0 @@ -#include "neonucleus.h" - -#ifndef NN_BAREMETAL - -#ifdef NN_POSIX -#include -#else -#include -#include -#endif - -#endif - -void *nn_alloc(nn_Alloc *alloc, nn_size_t size) { - if(size == 0) return alloc->proc; - return alloc->proc(alloc->userdata, NULL, 0, size, NULL); -} - -void *nn_resize(nn_Alloc *alloc, void *memory, nn_size_t oldSize, nn_size_t newSize) { - if(oldSize == newSize) return memory; - if(newSize == 0) { - nn_dealloc(alloc, memory, oldSize); - return alloc->proc; - } - if(memory == NULL) { - return nn_alloc(alloc, newSize); - } - if(memory == alloc->proc) { - if(newSize == 0) return memory; - return nn_alloc(alloc, newSize); - } - return alloc->proc(alloc->userdata, memory, oldSize, newSize, NULL); -} - -void nn_dealloc(nn_Alloc *alloc, void *memory, nn_size_t size) { - if(memory == NULL) return; // matches free() - if(memory == alloc->proc) return; // 0-sized memory - alloc->proc(alloc->userdata, memory, size, 0, NULL); -} - -#ifndef NN_BAREMETAL - -#include - -static void *nn_libcAllocProc(void *_, void *ptr, nn_size_t oldSize, nn_size_t newSize, void *__) { - if(newSize == 0) { - //printf("Freed %lu bytes from %p\n", oldSize, ptr); - free(ptr); - return NULL; - } else { - void *rptr = realloc(ptr, newSize); - //printf("Allocated %lu bytes for %p\n", newSize - oldSize, rptr); - return rptr; - } -} - -nn_Alloc nn_libcAllocator(void) { - return (nn_Alloc) { - .userdata = NULL, - .proc = nn_libcAllocProc, - }; -} - -static nn_size_t nni_rand(void *userdata) { - return rand(); -} - -nn_Rng nn_libcRng(void) { - srand(time(NULL)); - return (nn_Rng) { - .userdata = NULL, - .maximum = RAND_MAX, - .proc = nni_rand, - }; -} - -nn_Context nn_libcContext(void) { - return (nn_Context) { - .allocator = nn_libcAllocator(), - .clock = nn_libcRealTime(), - .lockManager = nn_libcMutex(), - .rng = nn_libcRng(), - }; -} -#endif - - -// Utilities, both internal and external -char *nn_strdup(nn_Alloc *alloc, const char *s) { - nn_size_t l = nn_strlen(s); - char *m = nn_alloc(alloc, l+1); - if(m == NULL) return m; - return nn_strcpy(m, s); -} - -void *nn_memdup(nn_Alloc *alloc, const void *buf, nn_size_t len) { - char *m = nn_alloc(alloc, len); - if(m == NULL) return m; - nn_memcpy(m, buf, len); - return m; -} - -void nn_deallocStr(nn_Alloc *alloc, char *s) { - if(s == NULL) return; - nn_dealloc(alloc, s, nn_strlen(s)+1); -} - -static void nni_randomHex(nn_Context *ctx, char *buf, nn_size_t len) { - const char *hex = "0123456789abcdef"; - - for(nn_size_t i = 0; i < len; i++) { - int r = nn_rand(&ctx->rng) % 16; - buf[i] = hex[r]; - } -} - -nn_address nn_randomUUID(nn_Context *ctx) { - nn_address addr = nn_alloc(&ctx->allocator, 37); - if(addr == NULL) return NULL; - nni_randomHex(ctx, addr + 0, 8); - addr[8] = '-'; - nni_randomHex(ctx, addr + 9, 4); - addr[13] = '-'; - nni_randomHex(ctx, addr + 14, 4); - addr[18] = '-'; - nni_randomHex(ctx, addr + 19, 4); - addr[23] = '-'; - nni_randomHex(ctx, addr + 24, 12); - addr[36] = '\0'; - - - // UUIDv4 variant 1 - addr[14] = '4'; - addr[19] = '1'; - return addr; -} - -nn_size_t nn_rand(nn_Rng *rng) { - return rng->proc(rng->userdata); -} - -// returns from 0 to 1 (inclusive) -double nn_randf(nn_Rng *rng) { - double x = nn_rand(rng); - return x / rng->maximum; -} - -// returns from 0 to 1 (exclusive) -double nn_randfe(nn_Rng *rng) { - double x = nn_rand(rng); - if(x >= rng->maximum) return 0; - return x / rng->maximum; -} - -#ifndef NN_BAREMETAL - -#ifdef NN_POSIX - -static double nni_realTime(void) { - struct timespec time; - if(clock_gettime(CLOCK_MONOTONIC, &time) < 0) return 0; // oh no - return time.tv_sec + ((double)time.tv_nsec) / 1e9; -} - -#else - -static double nni_realTime(void) { - LARGE_INTEGER frequency = {0}; - if(!QueryPerformanceFrequency(&frequency)) return 0; - - LARGE_INTEGER now = {0}; - if(!QueryPerformanceCounter(&now)) return 0; - - return (double)now.QuadPart / frequency.QuadPart; -} - -#endif - -double nni_realTimeClock(void *_) { - return nni_realTime(); -} - -nn_Clock nn_libcRealTime(void) { - return (nn_Clock) { - .userdata = NULL, - .proc = nni_realTimeClock, - }; -} - -#endif - -// TODO: use OKLAB the color space for more accurate results. - -typedef struct nn_rgbColor { - double r, g, b; -} nn_rgbColor; - -nn_rgbColor nni_splitColorToRgb(int color) { - double r = (color & 0xFF0000) >> 16; - double g = (color & 0x00FF00) >> 8; - double b = color & 0x0000FF; - - int max = 0xFF; - return (nn_rgbColor) { - .r = r / max, - .g = g / max, - .b = b / max, - }; -} - -double nn_colorDistance(int colorA, int colorB) { - if(colorA == colorB) return 0; - nn_rgbColor a = nni_splitColorToRgb(colorA); - nn_rgbColor b = nni_splitColorToRgb(colorB); - - nn_rgbColor delta; - delta.r = a.r - b.r; - delta.g = a.g - b.g; - delta.b = a.b - b.b; - - return 0.2126 * delta.r*delta.r + 0.7152 * delta.g*delta.g + 0.0722 * delta.b*delta.b; -} - -int nn_mapColor(int color, int *palette, int paletteSize) { - if(paletteSize <= 0) return color; - int bestColor = palette[0]; - double fitness = nn_colorDistance(color, bestColor); - - for(int i = 1; i < paletteSize; i++) { - double dist = nn_colorDistance(color, palette[i]); - if(dist < fitness) { - bestColor = palette[i]; - fitness = dist; - } - if(bestColor == color) return color; - } - return bestColor; -} - -void nn_memset(void *buf, unsigned char byte, nn_size_t len) { - if(buf == NULL) return; - unsigned char *bytes = buf; - for(nn_size_t i = 0; i < len; i++) bytes[i] = byte; -} - -void nn_memcpy(void *dest, const void *src, nn_size_t len) { - if(dest == NULL) return; - if(src == NULL) return; - if(len == 0) return; - - char *destBytes = dest; - const char *srcBytes = src; - for(nn_size_t i = 0; i < len; i++) { - destBytes[i] = srcBytes[i]; - } -} - -char *nn_strcpy(char *dest, const char *src) { - if(dest == NULL) return dest; - nn_size_t i = 0; - while(src[i]) { - dest[i] = src[i]; - i++; - } - dest[i] = 0; - return dest; -} - -int nn_strcmp(const char *a, const char *b) { - nn_size_t i = 0; - while(NN_TRUE) { - unsigned char ca = a[i]; - unsigned char cb = b[i]; - - if(ca < cb) { - return -1; - } - if(ca > cb) { - return -1; - } - if(ca == 0 && cb == 0) { // reached terminator - return 0; - } - i++; - } -} - -const char *nn_strchr(const char *str, int ch) { - if(str == NULL) return NULL; - nn_size_t i = 0; - while(NN_TRUE) { - if(str[i] == ch) return str + i; - if(str[i] == 0) return NULL; - i++; - } -} - -nn_size_t nn_strlen(const char *a) { - if(a == NULL) return 0; - nn_size_t l = 0; - while(a[l]) l++; - return l; -} - -nn_bool_t nn_strbegin(const char *s, const char *prefix) { - nn_size_t i = 0; - while(true) { - if(prefix[i] == 0) return true; // prefix over, it matched - if(s[i] == 0) return false; // string over, it didn't match - if(s[i] != prefix[i]) return false; - i++; - } -} - -nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf) { - if(buf == NULL) return true; - return buf[0] == 0; -} - -void nn_error_write(nn_errorbuf_t buf, const char *s) { - if(buf == NULL) return; - for(nn_size_t i = 0; i < NN_MAX_ERROR_BUFFER; i++) { - buf[i] = s[i]; - if(s[i] == 0) break; - } - // just to be sure - buf[NN_MAX_ERROR_BUFFER-1] = 0; -} - -void nn_error_clear(nn_errorbuf_t buf) { - if(buf == NULL) return; - buf[0] = 0; -} - - -nn_bool_t nn_path_hasSlash(const char *path) { - while(*path) { - if(*path == '/') return true; - path++; - } - return false; -} - -nn_size_t nn_path_firstSlash(const char *path) { - for(nn_size_t i = 0; path[i]; i++) { - if(path[i] == '/') return i; - } - return 0; // should never happen -} - -nn_size_t nn_path_lastSlash(const char *path) { - nn_size_t slash = 0; - for(nn_size_t i = 0; path[i]; i++) { - if(path[i] == '/') slash = i; - } - return slash; -} - -// returns whether it is the last name -nn_bool_t nn_path_firstName(const char *path, char firstDirectory[NN_MAX_PATH], char subpath[NN_MAX_PATH]) { - if(!nn_path_hasSlash(path)) { - nn_strcpy(firstDirectory, path); - nn_strcpy(subpath, ""); - return true; // end - } - nn_size_t slash = nn_path_firstSlash(path); - - nn_memcpy(firstDirectory, path, slash); - firstDirectory[slash] = 0; - - nn_strcpy(subpath, path + slash + 1); - return false; -} - -// returns whether it is the only name -nn_bool_t nn_path_lastName(const char *path, char name[NN_MAX_PATH], char parent[NN_MAX_PATH]) { - if(!nn_path_hasSlash(path)) { - nn_strcpy(name, path); - nn_strcpy(parent, ""); - return true; // end - } - - nn_size_t slash = nn_path_lastSlash(path); - nn_strcpy(name, path + slash + 1); - - nn_memcpy(parent, path, slash); - parent[slash] = 0; - return false; -} - -const char *nn_path_illegal = "\"\\:*?<>|"; - -nn_bool_t nn_path_isValid(const char *path) { - // if we don't check for these, we will be FUCKED - - for(nn_size_t i = 0; nn_path_illegal[i] != '\0'; i++) { - if(nn_strchr(path, nn_path_illegal[i]) != NULL) return false; - } - return nn_strlen(path) < NN_MAX_PATH; // less then because we depend on the terminator -} - -static nn_bool_t nni_path_isAllDots(const char *path, nn_size_t len) { - for(nn_size_t i = 0; i < len; i++) { - if(path[i] != '.') return false; - } - return true; -} - -nn_bool_t nn_path_canonical(const char *path, char canonical[NN_MAX_PATH]) { - // attempts to convert a random barely legal path - // if this shit is ever bugged and a sandbox escape is done - // by tricking it into sneaking some .. in there - // !!!! WE WILL BE FUCKED, THE SERVER WILL BE HACKED, AND WE WILL DIE !!!! - - if(!nn_path_isValid(path)) { - // HELL NO - return true; - } - - // 0'd out because it fills it up with terminators, simplifying the rest of the code - // in theory this is suboptimal, however, I'm lazy - nn_memset(canonical, 0, NN_MAX_PATH); - nn_size_t ptr = 0; - nn_size_t i = 0; - - // TODO: burn it with fire and get banned from programming - while(true) { - while(path[i] == '/') i++; // just do not ask - if(path[i] == 0) break; - - const char *subpath = path + i; - nn_size_t len = nn_path_firstSlash(subpath); - - if(len == 0) { - len = nn_strlen(path) - i; - } - - if(nni_path_isAllDots(subpath, len)) { - // we don't actually resolve them because they shouldn't be there - // to begin with - i += len; - continue; - } - - if(ptr == 0) { - // at the start - nn_memcpy(canonical, subpath, len); - ptr = len; - i += len; - continue; - } - // just append to it - canonical[ptr] = '/'; - ptr++; - nn_memcpy(canonical + ptr, subpath, len); - ptr += len; - i += len; - continue; - } - - return false; -} diff --git a/src/value.c b/src/value.c deleted file mode 100644 index 7c3fdbd..0000000 --- a/src/value.c +++ /dev/null @@ -1,257 +0,0 @@ -#include "neonucleus.h" - -nn_value nn_values_nil(void) { - return (nn_value) {.tag = NN_VALUE_NIL}; -} - -nn_value nn_values_integer(nn_integer_t integer) { - return (nn_value) {.tag = NN_VALUE_INT, .integer = integer}; -} - -nn_value nn_values_number(double num) { - return (nn_value) {.tag = NN_VALUE_NUMBER, .number = num}; -} - -nn_value nn_values_boolean(nn_bool_t boolean) { - return (nn_value) {.tag = NN_VALUE_BOOL, .boolean = boolean}; -} - -nn_value nn_values_cstring(const char *string) { - return (nn_value) {.tag = NN_VALUE_CSTR, .cstring = string}; -} - -nn_value nn_values_string(nn_Alloc *alloc, const char *string, nn_size_t len) { - char *buf = nn_alloc(alloc, len+1); - if(buf == NULL) { - return nn_values_nil(); - } - nn_memcpy(buf, string, len); - buf[len] = '\0'; - - nn_string *s = nn_alloc(alloc, sizeof(nn_string)); - if(s == NULL) { - nn_dealloc(alloc, buf, len+1); - return nn_values_nil(); - } - s->data = buf; - s->len = len; - s->refc = 1; - s->alloc = *alloc; - - return (nn_value) {.tag = NN_VALUE_STR, .string = s}; -} - -nn_value nn_values_array(nn_Alloc *alloc, nn_size_t len) { - nn_array *arr = nn_alloc(alloc, sizeof(nn_array)); - if(arr == NULL) { - return nn_values_nil(); - } - arr->alloc = *alloc; - arr->refc = 1; - arr->len = len; - nn_value *values = nn_alloc(alloc, sizeof(nn_value) * len); - if(values == NULL) { - nn_dealloc(alloc, arr, sizeof(nn_array)); - return nn_values_nil(); - } - for(nn_size_t i = 0; i < len; i++) { - values[i] = nn_values_nil(); - } - arr->values = values; - return (nn_value) {.tag = NN_VALUE_ARRAY, .array = arr}; -} - -nn_value nn_values_table(nn_Alloc *alloc, nn_size_t pairCount) { - nn_table *table = nn_alloc(alloc, sizeof(nn_table)); - if(table == NULL) { - return nn_values_nil(); - } - table->alloc = *alloc; - table->refc = 1; - table->len = pairCount; - nn_pair *pairs = nn_alloc(alloc, sizeof(nn_pair) * pairCount); - if(pairs == NULL) { - nn_dealloc(alloc, table, sizeof(nn_table)); - return nn_values_nil(); - } - for(nn_size_t i = 0; i < pairCount; i++) { - pairs[i].key = nn_values_nil(); - pairs[i].val = nn_values_nil(); - } - table->pairs = pairs; - return (nn_value) {.tag = NN_VALUE_TABLE, .table = table}; -} - -nn_value nn_values_resource(nn_size_t id) { - return (nn_value) { - .tag = NN_VALUE_RESOURCE, - .resourceID = id, - }; -} - -nn_size_t nn_values_getType(nn_value val) { - return val.tag; -} - -nn_value nn_values_retain(nn_value val) { - if(val.tag == NN_VALUE_STR) { - val.string->refc++; - } else if(val.tag == NN_VALUE_ARRAY) { - val.array->refc++; - } else if(val.tag == NN_VALUE_TABLE) { - val.table->refc++; - } - return val; -} - -void nn_values_drop(nn_value val) { - if(val.tag == NN_VALUE_STR) { - val.string->refc--; - if(val.string->refc == 0) { - nn_Alloc *a = &val.string->alloc; - nn_dealloc(a, val.string->data, val.string->len + 1); - nn_dealloc(a, val.string, sizeof(nn_string)); - } - } else if(val.tag == NN_VALUE_ARRAY) { - val.array->refc--; - if(val.array->refc == 0) { - for(nn_size_t i = 0; i < val.array->len; i++) { - nn_values_drop(val.array->values[i]); - } - nn_Alloc *a = &val.array->alloc; - nn_dealloc(a, val.array->values, sizeof(nn_value) * val.array->len); - nn_dealloc(a, val.array, sizeof(nn_array)); - } - } else if(val.tag == NN_VALUE_TABLE) { - val.table->refc--; - if(val.table->refc == 0) { - for(nn_size_t i = 0; i < val.table->len; i++) { - nn_values_drop(val.table->pairs[i].key); - nn_values_drop(val.table->pairs[i].val); - } - nn_Alloc *a = &val.table->alloc; - nn_dealloc(a, val.table->pairs, sizeof(nn_pair) * val.table->len); - nn_dealloc(a, val.table, sizeof(nn_table)); - } - } -} - -void nn_values_dropAll(nn_value *values, nn_size_t len) { - for(nn_size_t i = 0; i < len; i++) { - nn_values_drop(values[i]); - } -} - -void nn_values_set(nn_value arr, nn_size_t idx, nn_value val) { - if(arr.tag != NN_VALUE_ARRAY) return; - if(idx >= arr.array->len) return; - nn_values_drop(arr.array->values[idx]); - arr.array->values[idx] = val; -} - -nn_value nn_values_get(nn_value arr, nn_size_t idx) { - if(arr.tag != NN_VALUE_ARRAY) return nn_values_nil(); - if(idx >= arr.array->len) return nn_values_nil(); - return arr.array->values[idx]; -} - -void nn_values_setPair(nn_value obj, nn_size_t idx, nn_value key, nn_value val) { - if(obj.tag != NN_VALUE_TABLE) return; - if(idx >= obj.table->len) return; - nn_values_drop(obj.table->pairs[idx].key); - nn_values_drop(obj.table->pairs[idx].val); - obj.table->pairs[idx].key = key; - obj.table->pairs[idx].val = val; -} - -nn_pair nn_values_getPair(nn_value obj, nn_size_t idx) { - nn_pair badPair = {.key = nn_values_nil(), .val = nn_values_nil()}; - if(obj.tag != NN_VALUE_TABLE) return badPair; - if(idx >= obj.table->len) return badPair; - return obj.table->pairs[idx]; -} - -nn_integer_t nn_toInt(nn_value val) { - if(val.tag == NN_VALUE_INT) return val.integer; - if(val.tag == NN_VALUE_NUMBER) return val.number; - return 0; -} - -double nn_toNumber(nn_value val) { - if(val.tag == NN_VALUE_INT) return val.integer; - if(val.tag == NN_VALUE_NUMBER) return val.number; - return 0; -} - -nn_bool_t nn_toBoolean(nn_value val) { - if(val.tag == NN_VALUE_NIL) return false; - if(val.tag == NN_VALUE_BOOL) return val.boolean; - return true; -} - -const char *nn_toCString(nn_value val) { - if(val.tag == NN_VALUE_CSTR) return val.cstring; - if(val.tag == NN_VALUE_STR) return val.string->data; - return NULL; -} - -const char *nn_toString(nn_value val, nn_size_t *len) { - nn_size_t l = 0; - const char *c = NULL; - - if(val.tag == NN_VALUE_CSTR) { - c = val.cstring; - l = nn_strlen(c); - } - if(val.tag == NN_VALUE_STR) { - c = val.string->data; - l = val.string->len; - } - - if(len != NULL) *len = l; - return c; -} - -nn_integer_t nn_toIntOr(nn_value val, nn_integer_t defaultVal) { - if(val.tag == NN_VALUE_INT) return val.integer; - if(val.tag == NN_VALUE_NUMBER) return val.number; - return defaultVal; -} - -double nn_toNumberOr(nn_value val, double defaultVal) { - if(val.tag == NN_VALUE_INT) return val.integer; - if(val.tag == NN_VALUE_NUMBER) return val.number; - return defaultVal; -} - -nn_bool_t nn_toBooleanOr(nn_value val, nn_bool_t defaultVal) { - if(val.tag == NN_VALUE_BOOL) return val.boolean; - return defaultVal; -} - -nn_size_t nn_measurePacketSize(nn_value *vals, nn_size_t len) { - nn_size_t size = 0; - for(nn_size_t i = 0; i < len; i++) { - nn_value val = vals[i]; - size += 2; - if(val.tag == NN_VALUE_INT || val.tag == NN_VALUE_NUMBER) { - size += 8; - } else if(val.tag == NN_VALUE_STR) { - nn_size_t len = val.string->len; - if(len == 0) len = 1; // ask OC - size += len; - } else if(val.tag == NN_VALUE_CSTR) { - nn_size_t len = nn_strlen(val.cstring); - if(len == 0) len = 1; // ask OC - size += len; - } else if(val.tag == NN_VALUE_BOOL || val.tag == NN_VALUE_NIL) { - size += 4; - } else { - // yeah no fuck off - // we abuse 2's complement - // TODO: NN_SIZE_MAX - return -1; - } - } - return size; -}