commit ad9176d90914fa166930cb8118c9b13864582d35 Author: nicejs-is-cool Date: Mon May 25 21:33:48 2026 -0300 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..60c4254 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*.o +*.so +*.a +*.class +hs_err_pid*.log \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..bdc357e --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "NeoNucleus"] + path = NeoNucleus + url = https://gitea.codersquack.nl/NeoFlock/NeoNucleus.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..b5dae12 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,18 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/lib/jvm/java-25-openjdk/include", + "/usr/lib/jvm/java-25-openjdk/include/linux" + ], + "defines": [], + "compilerPath": "/usr/lib64/ccache/clang", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "linux-clang-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..082b194 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "makefile.configureOnOpen": false +} \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c40cb7a --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +CC=gcc +CXX=g++ +CFLAGS=-Wall -Wextra +TARGET=libneonucleusjni.so + +BASE_PATH=./src/main/org/neoflock/NeoNucleus +JAVA_HOME=/usr/lib/jvm/java-25-openjdk/ +BASE_NPATH=./src/native/ +JAVA_SRCS = $(wildcard ./src/main/org/neoflock/NeoNucleus/*.java) +JAVA_CLASSES = $(wildcard ./src/main/org/neoflock/NeoNucleus/*.class) + +CPP_SRCS = $(wildcard ${BASE_NPATH}/*.cpp) +OBJS = $(CPP_SRCS:.cpp=.o) +NATIVE_NAME=main + +all: $(TARGET) + +$(TARGET): $(OBJS) + g++ -shared -o ${BASE_NPATH}/$(TARGET) \ + $(OBJS) ${BASE_NPATH}/libneonucleus.a -lc +%.o: %.cpp + $(CXX) -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux $(CFLAGS) \ + $< -o $@ + +java: ${JAVA_CLASSES} + javac -h ./src/native/ ${JAVA_SRCS} + +run: all + cd src/main && java -cp . -Djava.library.path=${PWD}/${BASE_NPATH} -Xcheck:jni org.neoflock.NeoNucleus.NativeBindings + +clean: + rm ${JAVA_CLASSES} + rm $(OBJS) $(TARGET) + +.PHONY: all clean \ No newline at end of file diff --git a/NeoNucleus b/NeoNucleus new file mode 160000 index 0000000..3b04fd4 --- /dev/null +++ b/NeoNucleus @@ -0,0 +1 @@ +Subproject commit 3b04fd45c7695ae007d6aed4dfda1f0063a32952 diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a2e749 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Carbon Hydrogen +NeoNucleus JNI bindings. +### Dev Setup Notes +Java expects libneonucleusjni.so on the src/native directory. This is the default unless you're not using the Makefile. \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..a76686d --- /dev/null +++ b/build.sh @@ -0,0 +1,14 @@ +#!/usr/bin/bash +# deprecated, use Makefile instead +BASE_PATH=./src/main/org/neoflock/NeoNucleus +JAVA_HOME=/usr/lib/jvm/java-21-openjdk/ +BASE_NPATH=./src/native/ +#NATIVE_NAME=org_neoflock_NeoNucleus_NativeBindings +NATIVE_NAME=main +echo ":: Building java class" +javac -h ./src/native/ $BASE_PATH/NativeBindings.java $BASE_PATH/Context.java $BASE_PATH/Universe.java $BASE_PATH/PointerBackedClass.java $BASE_PATH/ExitCode.java $BASE_PATH/Architecture.java $BASE_PATH/ManuallyAllocated.java +echo ":: Compiling" +g++ -c -fPIC -I${JAVA_HOME}/include -I${JAVA_HOME}/include/linux ${BASE_NPATH}/${NATIVE_NAME}.cpp -o ${BASE_NPATH}/${NATIVE_NAME}.o +echo ":: Linking" +g++ -shared -o ${BASE_NPATH}/libneonucleusjni.so ${BASE_NPATH}/${NATIVE_NAME}.o ${BASE_NPATH}/libneonucleus.a -lc + diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..bfa5020 --- /dev/null +++ b/run.sh @@ -0,0 +1,5 @@ +#!/usr/bin/bash +# deprecated, use Makefile instead +NLIB_PATH=$PWD/src/native/ +cd src/main && java -cp . -Djava.library.path=${NLIB_PATH} org.neoflock.NeoNucleus.NativeBindings +cd ../.. \ No newline at end of file diff --git a/src/main/org/neoflock/NeoNucleus/ComponentHandler.java b/src/main/org/neoflock/NeoNucleus/ComponentHandler.java new file mode 100644 index 0000000..7d7ac01 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ComponentHandler.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +public interface ComponentHandler { + // TODO +} diff --git a/src/main/org/neoflock/NeoNucleus/ComponentState.java b/src/main/org/neoflock/NeoNucleus/ComponentState.java new file mode 100644 index 0000000..6efec22 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ComponentState.java @@ -0,0 +1,7 @@ +package org.neoflock.NeoNucleus; + +public sealed class ComponentState extends PointerBackedClass permits ncl_ScreenState { + // TODO + // opaque class, you'll need to cast it to do anything + protected ComponentState() {} +} diff --git a/src/main/org/neoflock/NeoNucleus/ManuallyAllocated.java b/src/main/org/neoflock/NeoNucleus/ManuallyAllocated.java new file mode 100644 index 0000000..ae8fe28 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ManuallyAllocated.java @@ -0,0 +1,6 @@ +package org.neoflock.NeoNucleus; + +public interface ManuallyAllocated { + boolean allocate(); + boolean free(); +} diff --git a/src/main/org/neoflock/NeoNucleus/NativeBindings.java b/src/main/org/neoflock/NeoNucleus/NativeBindings.java new file mode 100644 index 0000000..0434cd7 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/NativeBindings.java @@ -0,0 +1,135 @@ +package org.neoflock.NeoNucleus; + +public class NativeBindings { + static { + System.loadLibrary("neonucleusjni"); + } + public static void main(String[] args) { + //nn_Context ctx = nn_initContext(); + nn_EEPROM eeprom = new nn_EEPROM(100, 200, 300, 400, 0, 0, 0, 0); + eeprom.allocate(); + System.out.println("we live"); + eeprom.free(); + System.out.println("about to double free"); + eeprom.free(); + //System.out.println(ctx.ptr); + } + public static native nn_Context nn_initContext(); + public static native void nn_initPalettes(); + // nn_Universe *nn_createUniverse(nn_Context *ctx, void *userdata) + public static native nn_Universe nn_createUniverse(nn_Context ctx); + // nn_Component *nn_createComponent(nn_Universe *universe, const char *address, const char *type) + public static native nn_Component nn_createComponent(nn_Universe universe, String address, String type); + public static native nn_Exit nn_setComponentMethods(nn_Component component, nn_Method[] methods); + // void nn_setComponentHandler(nn_Component *c, nn_ComponentHandler *handler) + public static native void nn_setComponentHandler(nn_Component component, ComponentHandler handler); + // nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool silent) + public static native nn_Exit nn_mountComponent(nn_Computer c, nn_Component comp, int slot, boolean silent); + // nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, const char *code, size_t codelen, bool isReadonly) + public static native nn_Component ncl_createEEPROM(nn_Universe universe, String address, nn_EEPROM eeprom, String code, boolean isReadonly); + // nn_Computer *nn_createComputer(nn_Universe *universe, void *userdata, const char *address, size_t totalMemory, size_t maxComponents, size_t maxDevices) + public static native nn_Computer nn_createComputer(nn_Universe universe, String address, long totalMemory, long maxComponents, long maxDevices); + // void nn_setComputerEnvironment(nn_Computer *computer, nn_Environment env) + public static native void nn_setComputerEnvironment(nn_Computer computer, nn_Environment env); + // void nn_setCallBudget(nn_Computer *computer, double budget) + public static native void nn_setCallBudget(nn_Computer computer, double budget); + // void nn_setTotalEnergy(nn_Computer *computer, double maxEnergy) + public static native void nn_setTotalEnergy(nn_Computer computer, double maxEnergy); + // void nn_setArchitecture(nn_Computer *computer, const nn_Architecture *arch) + public static native void nn_setArchitecture(nn_Computer computer, nn_Architecture arch); + // nn_Exit nn_addSupportedArchitecture(nn_Computer *computer, const nn_Architecture *arch) + public static native nn_Exit nn_addSupportedArchitecture(nn_Computer computer, nn_Architecture arch); + // nn_Exit nn_setTmpAddress(nn_Computer *computer, const char *address) + /** + * address is copied. + * It can be NULL if you wish to have no tmp address. + * It can fail due to out-of-memory errors. + */ + public static native nn_Exit nn_setTmpAddress(nn_Computer computer, String address); + // const char *nn_getComponentAddress(nn_Component *c) + public static native String nn_getComponentAddress(nn_Component c); + // nn_Exit nn_addCommonDeviceInfo(nn_Computer *computer, const char *addr, nn_CommonDeviceInfo info) + public static native nn_Exit nn_addCommonDeviceInfo(nn_Computer computer, String addr, nn_CommonDeviceInfo info); + // it doesn't really make sense to implement nn_clearCommonDeviceInfo, so it wont be. + // void *nn_getComponentState(nn_Component *c) + public static native ComponentState nn_getComponentState(nn_Component c); + // void ncl_lockScreen(ncl_ScreenState *state) + public static native void ncl_lockScreen(ncl_ScreenState state); + // void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height) + public static native Resolution ncl_getScreenViewport(ncl_ScreenState state); + // void ncl_setScreenViewport(ncl_ScreenState *state, size_t width, size_t height) + public static native void ncl_setScreenViewport(ncl_ScreenState state, long width, long height); + // ncl_ScreenFlags ncl_getScreenFlags(const ncl_ScreenState *state) + public static native ncl_ScreenFlags ncl_getScreenFlags(ncl_ScreenState state); + // int ncl_cellHeight(ncl_GlyphCache *gc) + //public static native int ncl_cellHeight(ncl_GlyphCache gc); + // void ncl_destroyGlyphCache(ncl_GlyphCache *gc) + //public static native void ncl_destroyGlyphCache(ncl_GlyphCache gc); + // ncl_GlyphCache *ncl_createGlyphCache(const char *fontPath, int fontSize) + //public static native ncl_GlyphCache ncl_createGlyphCache(String fontPath, int fontSize); + // int ncl_cellWidth(ncl_GlyphCache *gc) + //public static native int ncl_cellWidth(ncl_GlyphCache gc); + // double ncl_getScreenBrightness(ncl_ScreenState *state) + public static native double ncl_getScreenBrightness(ncl_ScreenState state); + // ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y) + public static native ncl_Pixel ncl_getScreenPixel(ncl_ScreenState state, int x, int y); + // void ncl_needGlyph(ncl_GlyphCache *gc, nn_codepoint cp) + /** + * @param gc + * @param cp an nn_codepoint + */ + //public static native void ncl_needGlyph(ncl_GlyphCache gc, int cp); + // void ncl_drawGlyph(ncl_GlyphCache *gc, nn_codepoint cp, Vector2 pos, float size, Color tint) + //public static native void ncl_drawGlyph(ncl_GlyphCache gc, int cp, Vector2 pos, float size, Color tint); + // void ncl_unlockScreen(ncl_ScreenState *state) + public static native void ncl_unlockScreen(ncl_ScreenState state); + // nn_Exit nn_pushTouch(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player) + public static native nn_Exit nn_pushTouch(nn_Computer computer, String screenAddress, double x, double y, int button, String player); + // nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player) + public static native nn_Exit nn_pushDrop(nn_Computer computer, String screenAddress, double x, double y, int button, String player); + // nn_Exit nn_pushDrag(nn_Computer *computer, const char *screenAddress, double x, double y, int button, const char *player) + public static native nn_Exit nn_pushDrag(nn_Computer computer, String screenAddress, double x, double y, int button, String player); + // nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player) + public static native nn_Exit nn_pushScroll(nn_Computer computer, String screenAddress, double x, double y, double direction, String player); + // size_t nn_getUsedMemory(nn_Computer *computer) + public static native long nn_getUsedMemory(nn_Computer computer); + // size_t nn_getTotalMemory(nn_Computer *computer) + public static native long nn_getTotalMemory(nn_Computer computer); + // nn_Exit nn_pushClipboard(nn_Computer *computer, const char *keyboardAddress, const char *clipboard, const char *player) + public static native nn_Exit nn_pushClipboard(nn_Computer computer, String keyboardAddress, String clipboard, String player); + // nn_Exit nn_pushKeyDown(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player) + public static native nn_Exit nn_pushKeyDown(nn_Computer computer, String keyboardAddress, int charcode, int keycode, String player); + // nn_Exit nn_pushKeyUp(nn_Computer *computer, const char *keyboardAddress, nn_codepoint charcode, int keycode, const char *player) + public static native nn_Exit nn_pushKeyUp(nn_Computer computer, String keyboardAddress, int charcode, int keycode, String player); + // void nn_clearstack(nn_Computer *computer) + public static native nn_Exit nn_clearstack(nn_Computer computer); + // bool nn_removeEnergy(nn_Computer *computer, double energy) + public static native boolean nn_removeEnergy(nn_Computer computer, double energy); + // nn_Exit nn_tickSynchronized(nn_Computer *computer) + public static native nn_Exit nn_tickSynchronized(nn_Computer computer); + // double ncl_getScreenEnergyUsage(ncl_ScreenState *state) + public static native double ncl_getScreenEnergyUsage(ncl_ScreenState state); + // void nn_resetIdleTime(nn_Computer *computer) + public static native void nn_resetIdleTime(nn_Computer computer); + // nn_Exit nn_tick(nn_Computer *computer) + public static native nn_Exit nn_tick(nn_Computer computer); + // nn_ComputerState nn_getComputerState(nn_Computer *computer) + public static native nn_ComputerState nn_getComputerState(nn_Computer computer); + // const char *nn_getError(nn_Computer *computer) + public static native String nn_getError(nn_Computer computer); + // nn_Architecture nn_getDesiredArchitecture(nn_Computer *computer) + public static native nn_Architecture nn_getDesiredArchitecture(nn_Computer computer); + // void nn_stopComputer(nn_Computer *computer) + public static native void nn_stopComputer(nn_Computer computer); + // void ncl_resetScreen(ncl_ScreenState *state) + public static native void ncl_resetScreen(ncl_ScreenState state); + // void nn_addIdleTime(nn_Computer *computer, double time) + public static native void nn_addIdleTime(nn_Computer computer, double time); + // void nn_destroyComputer(nn_Computer *computer) + public static native void nn_destroyComputer(nn_Computer computer); + // void nn_dropComponent(nn_Component *c) + public static native void nn_dropComponent(nn_Component c); + // void nn_destroyUniverse(nn_Universe *universe) + public static native void nn_destroyUniverse(nn_Universe universe); + +} diff --git a/src/main/org/neoflock/NeoNucleus/PointerBackedClass.java b/src/main/org/neoflock/NeoNucleus/PointerBackedClass.java new file mode 100644 index 0000000..b7d1474 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/PointerBackedClass.java @@ -0,0 +1,10 @@ +package org.neoflock.NeoNucleus; + +public class PointerBackedClass { + private long ptr; + /*public static PointerBackedClass createFromPointer(long ptr) { + PointerBackedClass clazz = new PointerBackedClass(); + clazz.ptr = ptr; + return clazz; + }*/ +} diff --git a/src/main/org/neoflock/NeoNucleus/Resolution.java b/src/main/org/neoflock/NeoNucleus/Resolution.java new file mode 100644 index 0000000..df8f231 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/Resolution.java @@ -0,0 +1,3 @@ +package org.neoflock.NeoNucleus; + +public record Resolution(long width, long height) {} diff --git a/src/main/org/neoflock/NeoNucleus/ncl_GlyphCache.java b/src/main/org/neoflock/NeoNucleus/ncl_GlyphCache.java new file mode 100644 index 0000000..c632073 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ncl_GlyphCache.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +/*public class ncl_GlyphCache { + +}*/ \ No newline at end of file diff --git a/src/main/org/neoflock/NeoNucleus/ncl_Pixel.java b/src/main/org/neoflock/NeoNucleus/ncl_Pixel.java new file mode 100644 index 0000000..f3e91e0 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ncl_Pixel.java @@ -0,0 +1,21 @@ +package org.neoflock.NeoNucleus; +/* +typedef struct ncl_Pixel { + // 0xRRGGBB format + unsigned int fgColor; + // 0xRRGGBB format + unsigned int bgColor; + // the codepoint + nn_codepoint codepoint; +} ncl_Pixel; +*/ +public class ncl_Pixel { + public final int fgColor; + public final int bgColor; + public final int codepoint; + public ncl_Pixel(int fgColor, int bgColor, int codepoint) { + this.fgColor = fgColor; + this.bgColor = bgColor; + this.codepoint = codepoint; + } +} diff --git a/src/main/org/neoflock/NeoNucleus/ncl_ScreenFlags.java b/src/main/org/neoflock/NeoNucleus/ncl_ScreenFlags.java new file mode 100644 index 0000000..0a9a5b2 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ncl_ScreenFlags.java @@ -0,0 +1,17 @@ +package org.neoflock.NeoNucleus; + +/*public enum ncl_ScreenFlags { + NCL_SCREEN_ON(1<<0), + NCL_SCREEN_PRECISE(1<<1), + NCL_SCREEN_TOUCHINVERTED(1<<2); + public final int value; + private ncl_ScreenFlags(int flag) { + this.value = flag; + } +}*/ +public class ncl_ScreenFlags { + public static final int NCL_SCREEN_ON = 1<<0; + public static final int NCL_SCREEN_PRECISE = 1<<1; + public static final int NCL_SCREEN_TOUCHINVERTED = 1<<2; + +} \ No newline at end of file diff --git a/src/main/org/neoflock/NeoNucleus/ncl_ScreenState.java b/src/main/org/neoflock/NeoNucleus/ncl_ScreenState.java new file mode 100644 index 0000000..67785ba --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/ncl_ScreenState.java @@ -0,0 +1,4 @@ +package org.neoflock.NeoNucleus; + +// opaque type, not much to do here +public final class ncl_ScreenState extends ComponentState {} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Architecture.java b/src/main/org/neoflock/NeoNucleus/nn_Architecture.java new file mode 100644 index 0000000..f3b4ec4 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Architecture.java @@ -0,0 +1,21 @@ +package org.neoflock.NeoNucleus; + +import java.util.function.Function; + +public final class nn_Architecture extends PointerBackedClass implements ManuallyAllocated { + public final String name; + public final Function handler; + + public nn_Architecture(String name, Function handler) { // string is a placeholder; please fix + this.name = name; + this.handler = handler; + + } + + private int handleNative() { // i aint gonna be trying to call that Function from jni man + return handler.apply("placeholder"); + } + + public native boolean allocate(); + public native boolean free(); +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_CommonDeviceInfo.java b/src/main/org/neoflock/NeoNucleus/nn_CommonDeviceInfo.java new file mode 100644 index 0000000..dc66f10 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_CommonDeviceInfo.java @@ -0,0 +1,48 @@ +package org.neoflock.NeoNucleus; + +/* const char *CLASS; + const char *DESC; + const char *VENDOR; + const char *PRODUCT; + const char *VERSION; + const char *SERIAL; + const char *CAPACITY; + const char *SIZE; + const char *CLOCK; + const char *WIDTH; */ + +public class nn_CommonDeviceInfo { + public final String CLASS; + public final String DESC; + public final String VENDOR; + public final String PRODUCT; + public final String VERSION; + public final String SERIAL; + public final String CAPACITY; + public final String SIZE; + public final String CLOCK; + public final String WIDTH; + public nn_CommonDeviceInfo( + String CLASS, + String DESC, + String VENDOR, + String PRODUCT, + String VERSION, + String SERIAL, + String CAPACITY, + String SIZE, + String CLOCK, + String WIDTH + ) { + this.CLASS = CLASS; + this.DESC = DESC; + this.VENDOR = VENDOR; + this.PRODUCT = PRODUCT; + this.VERSION = VERSION; + this.SERIAL = SERIAL; + this.CAPACITY = CAPACITY; + this.SIZE = SIZE; + this.CLOCK = CLOCK; + this.WIDTH = WIDTH; + } +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Component.java b/src/main/org/neoflock/NeoNucleus/nn_Component.java new file mode 100644 index 0000000..ab47303 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Component.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +public final class nn_Component extends PointerBackedClass { + private nn_Component() {} +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Computer.java b/src/main/org/neoflock/NeoNucleus/nn_Computer.java new file mode 100644 index 0000000..1707375 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Computer.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +public class nn_Computer { + // TODO +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_ComputerState.java b/src/main/org/neoflock/NeoNucleus/nn_ComputerState.java new file mode 100644 index 0000000..e7fc76b --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_ComputerState.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +public class nn_ComputerState { + // TODO +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Context.java b/src/main/org/neoflock/NeoNucleus/nn_Context.java new file mode 100644 index 0000000..423ad56 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Context.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +public class nn_Context extends PointerBackedClass { + public native void free(); +} \ No newline at end of file diff --git a/src/main/org/neoflock/NeoNucleus/nn_EEPROM.java b/src/main/org/neoflock/NeoNucleus/nn_EEPROM.java new file mode 100644 index 0000000..02e4c86 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_EEPROM.java @@ -0,0 +1,54 @@ +package org.neoflock.NeoNucleus; +/* +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; +*/ +public class nn_EEPROM extends PointerBackedClass implements ManuallyAllocated { + public final long size; + public final long dataSize; + public final double readEnergyCost; + public final double readDataEnergyCost; + public final double writeEnergyCost; + public final double writeDataEnergyCost; + public final double writeDelay; + public final double writeDataDelay; + public nn_EEPROM( + long size, + long dataSize, + double readEnergyCost, + double readDataEnergyCost, + double writeEnergyCost, + double writeDataEnergyCost, + double writeDelay, + double writeDataDelay + ) { + this.size = size; + this.dataSize = dataSize; + this.readEnergyCost = readEnergyCost; + this.readDataEnergyCost = readDataEnergyCost; + this.writeEnergyCost = writeEnergyCost; + this.writeDataEnergyCost = writeDataEnergyCost; + this.writeDelay = writeDelay; + this.writeDataDelay = writeDataDelay; + } + @Override + public native boolean allocate(); + @Override + public native boolean free(); +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Environment.java b/src/main/org/neoflock/NeoNucleus/nn_Environment.java new file mode 100644 index 0000000..696b099 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Environment.java @@ -0,0 +1,6 @@ +package org.neoflock.NeoNucleus; + +public class nn_Environment { + public String a; + // TODO +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Exit.java b/src/main/org/neoflock/NeoNucleus/nn_Exit.java new file mode 100644 index 0000000..1f52890 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Exit.java @@ -0,0 +1,18 @@ +package org.neoflock.NeoNucleus; + +public enum nn_Exit { + // no error + NN_OK, + // 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, +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Method.java b/src/main/org/neoflock/NeoNucleus/nn_Method.java new file mode 100644 index 0000000..b38bb0c --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Method.java @@ -0,0 +1,16 @@ +package org.neoflock.NeoNucleus; + +public class nn_Method { + public final String name; + public final String doc; + public final byte flags; + public nn_Method( + String name, + String doc, + byte flags + ) { + this.name = name; + this.doc = doc; + this.flags = flags; + } +} diff --git a/src/main/org/neoflock/NeoNucleus/nn_Universe.java b/src/main/org/neoflock/NeoNucleus/nn_Universe.java new file mode 100644 index 0000000..3a31d67 --- /dev/null +++ b/src/main/org/neoflock/NeoNucleus/nn_Universe.java @@ -0,0 +1,5 @@ +package org.neoflock.NeoNucleus; + +public class nn_Universe extends PointerBackedClass { + +} \ No newline at end of file diff --git a/src/native/carbon.cpp b/src/native/carbon.cpp new file mode 100644 index 0000000..4fea826 --- /dev/null +++ b/src/native/carbon.cpp @@ -0,0 +1,82 @@ +#include "carbon.hpp" +// redefine it here with the impl +// theres probably a better way to do this +#define CARBON_EXCEPTION_FUNC(fname, classpath) jint fname(JNIEnv* env, const char* message) { return THROW_EXCEPTION(classpath, message); } +#define CARBON_MAP_TO_NN_EXIT(enumcase) case enumcase:\ + jfieldID id = env->GetFieldID(clazz, #enumcase, "Lorg.neoflock.NeoNucleus.nn_Exit;");\ + return env->GetStaticObjectField(clazz, id); +// codename for NN JNI is Carbon because i needed something better than NN JNI +bool Carbon::PointerBacked::SetPointer(JNIEnv * env, jobject obj, void* ptr) { + jfieldID id = env->GetFieldID(env->GetObjectClass(obj), "ptr", "J"); + if (id == NULL) return false; + env->SetLongField(obj, id, (jlong) ptr); + return true; +} +void* Carbon::PointerBacked::GetPointer(JNIEnv * env, jobject obj) { + jfieldID id = env->GetFieldID(env->GetObjectClass(obj), "ptr", "J"); + IF_NULL_RET(id); + jlong ptr = env->GetLongField(obj, id); + + return (void*) ptr; +} +bool Carbon::PointerBacked::ResetPointer(JNIEnv * env, jobject obj) { + jfieldID id = env->GetFieldID(env->GetObjectClass(obj), "ptr", "J"); + if (id == NULL) return false; + env->SetLongField(obj, id, (jlong) NULL); + return true; +} +nn_Exit Carbon::archRequestHandler(nn_ArchitectureRequest *req) { + GlobalArchState* gstate = (GlobalArchState*)req->globalState; + JNIEnv* env = gstate->target.env; + jmethodID mid = env->GetMethodID( + env->GetObjectClass(gstate->target.obj), "handleNative", "()V"); + // TODO: null check mid + jbyte ret = env->CallByteMethod(gstate->target.obj, mid); + return (nn_Exit) ret; // todo: finish the args; probably an unnecessary cast +} + +jobject Carbon::InstantiateOpaqueClass(JNIEnv* env, const char* classpath) { + jclass cls = env->FindClass(classpath); + IF_NULL_RET(cls); + jmethodID methodID = env->GetMethodID(cls, "", "()V"); + IF_NULL_RET(methodID); + jobject obj = env->NewObject(cls, methodID); + return obj; +} +// pbc = pointer backed class +jobject Carbon::InstantiateDefaultPBC(JNIEnv* env, const char* classpath, void* ptr) { + jobject clazz = InstantiateOpaqueClass(env, classpath); + IF_NULL_RET(clazz); + Carbon::PointerBacked::SetPointer(env, clazz, ptr); // FIXME: this can silently fail + return clazz; +} +nn_Exit Carbon::JNIComponentHandler(nn_ComponentRequest* request) { + Carbon::JavaObjectTarget* ot = (Carbon::JavaObjectTarget*) request->state; // todo: make this a proper struct with a target for the component handler specifically + if (ot == NULL) return NN_EBADSTATE; // welp + //ud->target.env-> + jmethodID callMethod = ot->env->GetMethodID(ot->env->GetObjectClass(ot->obj), "handleRequest", "(Lorg/neoflock/NeoNucleus/nn_ComponentRequest;)Lorg/neoflock/NeoNucleus/nn_Exit;"); + jobject retex = ot->env->CallObjectMethod(ot->obj, callMethod, NULL); // todo: args + return Carbon::Map::From_nn_Exit(ot->env, retex); +} +jobject Carbon::Map::To_nn_Exit(JNIEnv* env, nn_Exit a) { + jclass clazz = env->FindClass("org/neoflock/NeoNucleus/nn_Exit"); + switch (a) { + CARBON_MAP_TO_NN_EXIT(NN_OK) + CARBON_MAP_TO_NN_EXIT(NN_ENOMEM) + CARBON_MAP_TO_NN_EXIT(NN_ELIMIT) + CARBON_MAP_TO_NN_EXIT(NN_EBELOWSTACK) + CARBON_MAP_TO_NN_EXIT(NN_ENOSTACK) + CARBON_MAP_TO_NN_EXIT(NN_EBADCALL) + CARBON_MAP_TO_NN_EXIT(NN_EBADSTATE) + } + return NULL; +} +nn_Exit From_nn_Exit(JNIEnv* env, jobject a) { + jclass clazz = env->FindClass("org/neoflock/NeoNucleus/nn_Exit"); + jmethodID ordMID = env->GetMethodID(clazz, "ordinal", "()I"); + jint value = env->CallIntMethod(a, ordMID); + return (nn_Exit) value; // this should be fine as long as the header and java side don't desync +} +namespace Carbon::Exceptions { + CARBON_EXCEPTION_FUNC(ThrowNullPtr, "java/lang/NullPointerException"); +} \ No newline at end of file diff --git a/src/native/carbon.hpp b/src/native/carbon.hpp new file mode 100644 index 0000000..3093415 --- /dev/null +++ b/src/native/carbon.hpp @@ -0,0 +1,54 @@ +#pragma once +#include "neonucleus.h" +#include "org_neoflock_NeoNucleus_NativeBindings.h" +#define IF_NULL_RET(param) if (param == NULL) return NULL; +#define THROW_EXCEPTION(classpath, msg) env->ThrowNew(env->FindClass(classpath), msg); +#define CARBON_EXCEPTION_FUNC(fname, classpath) jint fname(JNIEnv* env, const char* message); + +#define NULLPTR_THROW(cname) Carbon::Exceptions::ThrowNullPtr(env, "Received null pointer for " #cname); +#define NULLPTR_CHECK(var, cname) if (var == NULL) {\ + NULLPTR_THROW(cname);\ + return NULL;\ + } +#define NULLPTR_CHECKNR(var, cname) if (var == NULL) {\ + NULLPTR_THROW(cname);\ + return;\ + } + +// codename for NN JNI is Carbon because i needed something better than NN JNI + +namespace Carbon { + namespace PointerBacked { + bool SetPointer(JNIEnv * env, jobject obj, void* ptr); + void* GetPointer(JNIEnv * env, jobject obj); + bool ResetPointer(JNIEnv * env, jobject obj); + } + namespace Map { + jobject To_nn_Exit(JNIEnv* env, nn_Exit a); + nn_Exit From_nn_Exit(JNIEnv* env, jobject a); + + } + + typedef struct JavaObjectTarget { // i might lowkey drop this struct + JNIEnv * env; + jobject obj; + } JavaObjectTarget; + typedef struct ComputerUserdata { + JavaObjectTarget target; + } ComputerUserdata; + typedef struct GlobalArchState { + JavaObjectTarget target; + } GlobalArchState; + nn_Exit archRequestHandler(nn_ArchitectureRequest *req); + jobject InstantiateOpaqueClass(JNIEnv* env, const char* classpath); + // pbc = pointer backed class + jobject InstantiateDefaultPBC(JNIEnv* env, const char* classpath, void* ptr); + // typedef nn_Exit (nn_ComponentHandler)(nn_ComponentRequest *request); + nn_Exit JNIComponentHandler(nn_ComponentRequest* request); + bool CallGC(JNIEnv* env); +} +namespace Carbon::Exceptions { + CARBON_EXCEPTION_FUNC(ThrowNullPtr, "java/lang/NullPointerException"); + +} +#undef CARBON_EXCEPTION_FUNC \ No newline at end of file diff --git a/src/native/main.cpp b/src/native/main.cpp new file mode 100644 index 0000000..8a9ea38 --- /dev/null +++ b/src/native/main.cpp @@ -0,0 +1,170 @@ +#include "org_neoflock_NeoNucleus_NativeBindings.h" +#include +#include "neonucleus.h" +#include "ncomplib.h" + +#include "carbon.hpp" + +// TODO: null checks fucking everywhere (thanks java) +// NOTE: almost NOTHING of this has been tested yet +// WARN: **apparently** GetFieldID can error out on OOM, this will not be an issue for now (i think), +// but we need to account for it before or after MVP (modded minecraft is famously known for being memory hungry) +// NOTE: not sure if i should use nn_alloc over malloc where possible + +/*nn_Architecture readArch(JNIEnv * env, jobject obj) { + jclass clazz = env->GetObjectClass(obj); + jfieldID nid = env->GetFieldID(clazz, "name", "Ljava.lang.String;"); + jstring jstr = (jstring)env->GetObjectField(obj, nid); + const char * name = env->GetStringUTFChars(jstr, (jboolean) false);// this shit will so become invalid after the func + + Carbon::GlobalArchState* statePtr = (Carbon::GlobalArchState*)Carbon::PointerBacked::GetPointer(env, obj); + if (statePtr == NULL) { + // we cant just store this on the stack since nn_Architecture wants a *pointer*, + // if i do a pointer to a variable on the stack its just gonna vanish as soon as the function ends + Carbon::GlobalArchState* state = (Carbon::GlobalArchState*)malloc(sizeof(Carbon::GlobalArchState)); + *state = { + .target = { + .env = env, + .obj = env->NewGlobalRef(obj) + } + }; + } + + return (nn_Architecture) { + .name = name, + .state = (void*) statePtr, + .handler = Carbon::archRequestHandler + }; +}*/ + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1initContext + (JNIEnv * env, jclass ogClazz) { + //jclass clazz = env->FindClass("org/neoflock/NeoNucleus/nn_Context"); + //jmethodID creator = env->GetStaticMethodID(clazz, "createFromPointer", "(J)Lorg/neoflock/NeoNucleus/Context;"); + jobject obj = Carbon::InstantiateOpaqueClass(env, "org/neoflock/NeoNucleus/nn_Context"); + nn_Context * ctx = (nn_Context*)malloc(sizeof(nn_Context)); + nn_initContext(ctx); + //jobject result = env->CallStaticObjectMethod(clazz, creator, (jlong) ctx); + Carbon::PointerBacked::SetPointer(env, obj, ctx); + return obj; +} + +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1initPalettes + (JNIEnv *, jclass) { // fucking cakewalk compared to the horrors that'll unfold within this cpp file + nn_initPalettes(); +} + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1createUniverse + (JNIEnv * env, jclass, jobject ctx) { + nn_Context* nnCtx = (nn_Context*) Carbon::PointerBacked::GetPointer(env, ctx); + // TODO: check if this is error handled properly + NULLPTR_CHECK(nnCtx, nn_Context); + + nn_Universe* universe = nn_createUniverse(nnCtx, NULL); + /*jclass clazz = env->FindClass("org/neoflock/NeoNucleus/nn_Universe"); + jmethodID creator = env->GetStaticMethodID(clazz, "createFromPointer", "(J)Lorg/neoflock/NeoNucleus/Universe;"); + return env->CallStaticObjectMethod(clazz, creator, (jlong) universe);*/ + return Carbon::InstantiateDefaultPBC(env, "org/neoflock/NeoNucleus/nn_Universe", universe); +} +// public static native nn_Component nn_createComponent(nn_Universe universe, String address, String type); + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1createComponent + (JNIEnv * env, jclass, jobject universe, jstring address, jstring type) { + nn_Universe* nnUni = (nn_Universe*) Carbon::PointerBacked::GetPointer(env, universe); + NULLPTR_CHECK(nnUni, nn_Universe); + + const char* addressChars = env->GetStringUTFChars(address, NULL); + const char* typeChars = env->GetStringUTFChars(type, NULL); + + nn_Component* component = nn_createComponent(nnUni, addressChars, typeChars); + + env->ReleaseStringUTFChars(address, addressChars); + env->ReleaseStringUTFChars(type, typeChars); + + return Carbon::InstantiateDefaultPBC(env, "org/neoflock/NeoNucleus/nn_Component", component); +} + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setComponentMethods + (JNIEnv * env, jclass, jobject component, jobjectArray methods) { + NULLPTR_CHECK(methods, methods); + nn_Component* nnComp = (nn_Component*) Carbon::PointerBacked::GetPointer(env, component); + NULLPTR_CHECK(nnComp, nn_Component); + jclass methodClass = env->FindClass("org/neoflock/NeoNucleus/nn_Method"); + jfieldID nameField = env->GetFieldID(methodClass, "name", "Ljava.lang.String"); + jfieldID docField = env->GetFieldID(methodClass, "doc", "Ljava.lang.String"); + jfieldID flagsField = env->GetFieldID(methodClass, "flags", "B"); + + size_t len = (size_t) env->GetArrayLength(methods); + nn_Method nMethods[len]; // stack allocated, shouldnt be an issue unless you're a freak pushing thousands of methods + for (size_t i = 0; i < len; i++) { + jobject oArr = env->GetObjectArrayElement(methods, i); + jstring nameStr = (jstring) env->GetObjectField(oArr, nameField); + jstring docStr = (jstring) env->GetObjectField(oArr, docField); + jbyte flagsByte = env->GetByteField(oArr, flagsField); + nMethods[i] = { + .name = env->GetStringUTFChars(nameStr, NULL), + .doc = env->GetStringUTFChars(docStr, NULL), + .flags = (nn_MethodFlags) env->GetByteField(oArr, NULL) + }; + } + + // no shot this line below is actually gonna work + nn_Exit exitCode = nn_setComponentMethodsArray(nnComp, const_cast(&nMethods[0]), len); + // lets cleanup!!! + for (size_t i = 0; i < len; i++) { + jobject oArr = env->GetObjectArrayElement(methods, i); + jstring nameStr = (jstring) env->GetObjectField(oArr, nameField); // TODO: check if this returns a different pointer on a second run + jstring docStr = (jstring) env->GetObjectField(oArr, docField); // if it does then we'll have to store the original field jstrings too to clean them up properly + nn_Method nm = nMethods[i]; + env->ReleaseStringUTFChars(nameStr, nm.name); + env->ReleaseStringUTFChars(docStr, nm.doc); + } + + return Carbon::Map::To_nn_Exit(env, exitCode); +} + +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setComponentHandler + (JNIEnv * env, jclass, jobject component, jobject handler) { + nn_Component* nnComp = (nn_Component*) Carbon::PointerBacked::GetPointer(env, component); + NULLPTR_CHECKNR(nnComp, nn_Component); + nn_setComponentHandler(nnComp, &Carbon::JNIComponentHandler); +} + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1mountComponent + (JNIEnv * env, jclass, jobject computer, jobject comp, jint slot, jboolean silent) { + nn_Computer* nnPC = (nn_Computer*) Carbon::PointerBacked::GetPointer(env, computer); + nn_Component* nnComp = (nn_Component*) Carbon::PointerBacked::GetPointer(env, comp); + NULLPTR_CHECK(nnPC, nn_Component); + NULLPTR_CHECK(nnComp, nn_Computer); + return Carbon::Map::To_nn_Exit(env, nn_mountComponent(nnPC, nnComp, slot, silent)); +} + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1createEEPROM + (JNIEnv * env, jclass, jobject universe, jstring address, jobject eeprom, jstring code, jboolean isReadonly) { + nn_Universe* nnUni = (nn_Universe*) Carbon::PointerBacked::GetPointer(env, universe); + nn_EEPROM* nnEEPROM = (nn_EEPROM*) Carbon::PointerBacked::GetPointer(env, eeprom); + NULLPTR_CHECK(nnUni, nn_Universe); + NULLPTR_CHECK(nnEEPROM, nn_EEPROM); + + const char * addr = env->GetStringUTFChars(address, NULL); + const char * codeStr = env->GetStringUTFChars(code, NULL); + jsize codeLen = env->GetStringUTFLength(code); // TODO: check if this is the byte count or character count + + nn_Component* comp = ncl_createEEPROM(nnUni, addr, nnEEPROM, codeStr, codeLen, isReadonly); + env->ReleaseStringUTFChars(address, addr); + env->ReleaseStringUTFChars(code, codeStr); + + return Carbon::InstantiateDefaultPBC(env, "org/neoflock/NeoNucleus/nn_Component", comp); +} + +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1createComputer + (JNIEnv * env, jclass, jobject universe, jstring address, jlong totalMemory, jlong maxComponents, jlong maxDevices) { + nn_Universe* nnUni = (nn_Universe*) Carbon::PointerBacked::GetPointer(env, universe); + NULLPTR_CHECK(nnUni, nn_Universe); + + const char * addr = env->GetStringUTFChars(address, NULL); + // TODO: computer jni userdata lol + nn_Computer* computer = nn_createComputer(nnUni, NULL, addr, totalMemory, maxComponents, maxDevices); + env->ReleaseStringUTFChars(address, addr); + return Carbon::InstantiateDefaultPBC(env, "org/neoflock/NeoNucleus/nn_Computer", computer); +} diff --git a/src/native/neonucleus.h b/src/native/neonucleus.h new file mode 100644 index 0000000..013436c --- /dev/null +++ b/src/native/neonucleus.h @@ -0,0 +1,2437 @@ +#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__) + #ifndef NN_WINDOWS + #define NN_WINDOWS + #endif +#elif __APPLE__ + #ifndef NN_MACOS + #define NN_MACOS + #endif +#elif __linux__ + #ifndef NN_LINUX + #define NN_LINUX + #endif +#endif + +#if __unix__ + #ifndef NN_UNIX + #define NN_UNIX + #endif + #ifndef NN_POSIX + #define NN_POSIX + #endif +#elif defined(_POSIX_VERSION) + #ifndef NN_POSIX + #define NN_POSIX + #endif +#endif + + +#if defined(_MSC_VER) && !defined(__cplusplus) +#ifndef NN_ATOMIC_MSVC +#define NN_ATOMIC_MSVC +#endif +#endif + +#ifdef _MSC_VER +#define NN_INIT(type) +#else +#define NN_INIT(type) (type) +#endif + +// every C standard header we depend on, conveniently put here +#include // for NULL, +#include // for intptr_t +#include // for true, false and bool + +/* MSVC can't use VLA; + * What we see : NN_VLA(const char *, comps, len); + * What compiler see after preproccessor : const char * comps[len]; + * What actaully was : const char *comps[len]; +*/ +// Test: gcc -E -DNN_VLA\(type,name,count\)="type name[count]" -x c - <<< 'NN_VLA(const char *, comps, len);' +#ifdef _MSC_VER +// avoid #include , for it can pollute the namespace +// with symbols to functions which are not linked with in baremetal. +void *_alloca(size_t); +#define NN_VLA(type, name, count) type *name = (type *)_alloca(sizeof(type) * (count)) +#else +#define NN_VLA(type, name, count) type name[count] +#endif + +// 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 ((size_t)1024 * NN_GiB) + +// 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 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 16 +// 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, 64 is unrealistically long, as UUIDv4 only needs 36. +// Please, do not go above this. +#define NN_MAX_ADDRESS 64 +// 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, it is the same stuff, but direct ones may still be used across threads. +// 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); + +char *nn_strdup(nn_Context *ctx, const char *s); +void nn_strfree(nn_Context *ctx, char *s); + +typedef struct nn_Lock nn_Lock; + +nn_Lock *nn_createLock(nn_Context *ctx); +void nn_destroyLock(nn_Context *ctx, nn_Lock *lock); +void nn_lock(nn_Context *ctx, nn_Lock *lock); +void nn_unlock(nn_Context *ctx, nn_Lock *lock); + +double nn_currentTime(nn_Context *ctx); + +// generate a random RNG from 0 to the maximum +size_t nn_rand(nn_Context *ctx); +// generate a random float [0, 1) +double nn_randf(nn_Context *ctx); +// generate a random float [0, 1] +double nn_randfi(nn_Context *ctx); + +typedef char nn_uuid[37]; +void nn_randomUUID(nn_Context *ctx, nn_uuid uuid); + +// Basic utils + +// Does canonical path handling. Is used for sandboxing paths. +// It will turn \ to /, will turn invalid characters into spaces, +// and will resolve ... +// it also gets rid of / prefixes, / suffixes and // +void nn_simplifyPath(const char original[NN_MAX_PATH], char simplified[NN_MAX_PATH]); + +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 *userdata); +void nn_destroyUniverse(nn_Universe *universe); +void *nn_getUniverseData(nn_Universe *universe); +size_t nn_getUniverseMemoryLimit(nn_Universe *universe); +size_t nn_limitMemory(nn_Universe *universe, size_t memory); +void nn_setUniverseMemoryLimit(nn_Universe *universe, size_t limit); +size_t nn_getUniverseStorageLimit(nn_Universe *universe); +void nn_setUniverseStorageLimit(nn_Universe *universe, size_t limit); +size_t nn_limitStorage(nn_Universe *universe, size_t storage); + +// 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 or synchronized task + NN_ARCH_TICK, + // get the free memory + NN_ARCH_FREEMEM, + // deserialize from an encoded state, passed into request. + NN_ARCH_DESERIALIZE, + // serialize to an encoded state, pushed it as a string + NN_ARCH_SERIALIZE, +} 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_TICK, where the tick is synchronized + bool synchronized; + // in the case of NN_ARCH_FREEMEM, the free memory + size_t freeMemory; + // in the case of NN_ARCH_DESERIALIZE, the buffer. + struct { + const char *memIn; + size_t memLen; + }; + }; +} 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]; + +typedef struct nn_Beep { + // frequenc, in Hz + double frequency; + // duration, in seconds + double duration; + // 0 is mute, 1 is 100% + double volume; +} nn_Beep; + +// Morse beep, like a normal beep except it follows a morse code pattern. +// . is a short sound +// - is a long sound, 2x in length +// a space is a pause, 2x in length and dead silent. +// Every sound, including actual pauses, have a 50ms pause between them. +typedef struct nn_MorseBeep { + const char *pattern; + // frequency, in Hz + double frequency; + // duration of a ., in seconds + double beepDuration; + // 0 is mute, 1 is 100% + double volume; +} nn_MorseBeep; + +typedef enum nn_EnvironmentAction { + NN_ENV_DRAWENERGY, + NN_ENV_POWERON, + NN_ENV_POWEROFF, + NN_ENV_CRASHED, + NN_ENV_BEEP, + NN_ENV_BEEPMORSE, +} nn_EnvironmentAction; + +typedef struct nn_EnvironmentRequest { + nn_Computer *computer; + void *userdata; + nn_EnvironmentAction action; + union { + // for DRAWENERGY, is the amount to remove, and must be set to the amount remaining + double energy; + // for BEEP, information about the beep + nn_Beep beep; + // for BEEPMORSE, information about the beep + nn_MorseBeep morseBeep; + }; +} nn_EnvironmentRequest; + +typedef void nn_EnvironmentHandler(nn_EnvironmentRequest *req); + +typedef struct nn_Environment { + void *userdata; + nn_EnvironmentHandler *handler; +} nn_Environment; + +// 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); +void nn_retainComputer(nn_Computer *computer); +void nn_retainComputerN(nn_Computer *computer, size_t n); +// Destroys the state, effectively shutting down the computer. +void nn_destroyComputer(nn_Computer *computer); +void nn_destroyComputerN(nn_Computer *computer, size_t n); +void nn_lockComputer(nn_Computer *computer); +void nn_unlockComputer(nn_Computer *computer); +// stops the computer if an architecture state is already present, +// will also clear the signal buffer and set the state to NN_BOOTUP. +nn_Exit nn_startComputer(nn_Computer *computer); +// destroys the architecture state if present. +// Will also do other shutdown routines, such as unmounting every +void nn_stopComputer(nn_Computer *computer); +void nn_forceCrashComputer(nn_Computer *computer, const char *s); +// returns whether an architecture state is present +bool nn_isComputerOn(nn_Computer *computer); +void nn_setComputerEnvironment(nn_Computer *computer, nn_Environment env); + +void nn_setDirectCost(nn_Computer *computer, double directCost); +double nn_getDirectCost(nn_Computer *computer); + +// Device information + +// Standard device attribute fields + +#define NN_DEVICEATTR_CLASS "class" +#define NN_DEVICEATTR_DESC "description" +#define NN_DEVICEATTR_VENDOR "vendor" +#define NN_DEVICEATTR_PRODUCT "product" +#define NN_DEVICEATTR_VERSION "version" +#define NN_DEVICEATTR_SERIAL "serial" +#define NN_DEVICEATTR_CAPACITY "capacity" +#define NN_DEVICEATTR_SIZE "size" +#define NN_DEVICEATTR_CLOCK "clock" +#define NN_DEVICEATTR_WIDTH "width" + +// Standard device classes + +#define NN_DEVICECLASS_SYSTEM "system" +#define NN_DEVICECLASS_BRIDGE "bridge" +#define NN_DEVICECLASS_MEMORY "memory" +#define NN_DEVICECLASS_PROCESSOR "processor" +#define NN_DEVICECLASS_ADDRESS "address" +#define NN_DEVICECLASS_STORAGE "storage" +#define NN_DEVICECLASS_DISK "disk" +#define NN_DEVICECLASS_TAPE "tape" +#define NN_DEVICECLASS_BUS "bus" +#define NN_DEVICECLASS_NETWORK "network" +#define NN_DEVICECLASS_DISPLAY "display" +#define NN_DEVICECLASS_INPUT "input" +#define NN_DEVICECLASS_PRINTER "printer" +#define NN_DEVICECLASS_MULTIMEDIA "multimedia" +#define NN_DEVICECLASS_COMMUNICATION "communication" +#define NN_DEVICECLASS_POWER "power" +#define NN_DEVICECLASS_VOLUME "volume" +#define NN_DEVICECLASS_GENERIC "generic" + +typedef struct nn_DeviceField { + const char *name; + const char *value; +} nn_DeviceField; + +typedef struct nn_CommonDeviceInfo { + const char *CLASS; + const char *DESC; + const char *VENDOR; + const char *PRODUCT; + const char *VERSION; + const char *SERIAL; + const char *CAPACITY; + const char *SIZE; + const char *CLOCK; + const char *WIDTH; +} nn_CommonDeviceInfo; + +const char *nn_deviceInfoAt(nn_Computer *computer, size_t idx); +const nn_DeviceField *nn_getDeviceInfo(nn_Computer *computer, size_t idx, size_t *fieldCount); +nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *addr, const nn_DeviceField *fields); +nn_Exit nn_addDeviceInfoL(nn_Computer *computer, const char *addr, const nn_DeviceField *fields, size_t fieldCount); +nn_Exit nn_addCommonDeviceInfo(nn_Computer *computer, const char *addr, nn_CommonDeviceInfo info); +// Sets every field to NULL. +void nn_clearCommonDeviceInfo(nn_CommonDeviceInfo *info); +bool nn_removeDeviceInfo(nn_Computer *computer, const char *addr); + +void nn_beepComputer(nn_Computer *computer, nn_Beep beep); +void nn_beepComputerMorse(nn_Computer *computer, nn_MorseBeep beep); + +// 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); + +// 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. +// Will push it as a string and then call the architecture, which will decode it and pop it. +nn_Exit nn_deserializeComputer(nn_Computer *computer, const char *buf, size_t buflen); + +// Serialize the computer state. +// Encoding depends on architecture. +// Pushes the output, if successful, as a string on the stack. +nn_Exit nn_serializeComputer(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); + +// 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); +void nn_resetIdleTime(nn_Computer *computer); +// runs a tick of the computer. Make sure to check the state as well! +// Does not do anything if we're currently waiting on a synced call +// 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); + +// runs a synchronized tick of the computer. How this differs depends on architecture. +// Generally, this is meant to be in the same thread for all computers, and is if the external world is fundamentally not thread-safe, +// however components must interact with it. +// In this case, those component methods would be marked as NN_INDIRECT, or more accurately will not be marked as NN_DIRECT, and the architecture would queue them as synchronized tasks. +// Architectures should generally NOT ignore this if they can. +nn_Exit nn_tickSynchronized(nn_Computer *computer); + +// raw component and methods + +typedef struct nn_Component nn_Component; + +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 *doc; + nn_MethodFlags flags; +} nn_Method; + +typedef enum nn_ComponentAction { + // component dropped + NN_COMP_DROP, + // component method invoked + NN_COMP_INVOKE, + // checking if component method is enabled + // (may be locked by tier) + NN_COMP_CHECKMETHOD, + // userdata request + NN_COMP_USERDATA, +} nn_ComponentAction; + +typedef enum nn_UserdataAction { + NN_USER_DROP, + NN_USER_SERIALIZE, + NN_USER_DESERIALIZE, + NN_USER_GETMETHOD, + NN_USER_INVOKE, +} nn_UserdataAction; + +typedef struct nn_UserdataRequest { + void *state; + nn_UserdataAction action; + union { + struct { + const char *data; + size_t len; + } deserialize; + struct { + size_t idx; + // set to NULL if idx is out of bounds + nn_Method *method; + } getmethod; + struct { + const char *method; + size_t returnCount; + } invoke; + }; +} nn_UserdataRequest; + +typedef struct nn_ComponentRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + void *classState; + const char *compAddress; + nn_ComponentAction action; + // method index + unsigned int methodIdx; + union { + // return count + size_t returnCount; + // method enabled + bool methodEnabled; + nn_UserdataRequest *user; + }; +} nn_ComponentRequest; + +typedef nn_Exit (nn_ComponentHandler)(nn_ComponentRequest *request); + +// creates a blank component. +// It has no methods, +nn_Component *nn_createComponent(nn_Universe *universe, const char *address, const char *type); +void nn_retainComponent(nn_Component *c); +void nn_retainComponentN(nn_Component *c, size_t n); +void nn_dropComponent(nn_Component *c); +void nn_dropComponentN(nn_Component *c, size_t n); + +// configure the state +void nn_setComponentHandler(nn_Component *c, nn_ComponentHandler *handler); +void nn_setComponentState(nn_Component *c, void *state); +void nn_setComponentClassState(nn_Component *c, void *state); +// sets the methods, same implications as setComponentMethodsArray. +// methods is NULL-terminated, as in, it is terminated by a method with a NULL name. +nn_Exit nn_setComponentMethods(nn_Component *c, const nn_Method *methods); +// sets the methods. +// The memory of the strings is copied, so they can be freed after this returns. +// This operation is NOT atomic, if it fails, it will clear out the previous methods. +nn_Exit nn_setComponentMethodsArray(nn_Component *c, const nn_Method *methods, size_t count); +// Sets an internal type ID, which is meant to be a more precise typename. +// For example, ncomplib would set ncl-screen for the screen component, +// so the GPU can confirm it is being bound to a screen it knows how to use. +nn_Exit nn_setComponentTypeID(nn_Component *c, const char *internalTypeID); + +// Sets the method flags +void nn_setComponentMethodFlags(nn_Component *c, const char *method, nn_MethodFlags flags); +// combines method flags +void nn_addComponentMethodFlags(nn_Component *c, const char *method, nn_MethodFlags flags); +// removes method flags +void nn_removeComponentMethodFlags(nn_Component *c, const char *method, nn_MethodFlags flags); + +// get component state +void *nn_getComponentState(nn_Component *c); +// get component class state +void *nn_getComponentClassState(nn_Component *c); +// counts how many methods are registered. May return too many if some of them are not enabled. +size_t nn_countComponentMethods(nn_Component *c); +// will fill the methodnames array with the names of the *enabled* methods. +// Will set *len to the amount of methods. +void nn_getComponentMethods(nn_Component *c, const char **methodnames, size_t *len); +// whether a method is defined and enabled +bool nn_hasComponentMethod(nn_Component *c, const char *method); +const char *nn_getComponentDoc(nn_Component *c, const char *method); +nn_MethodFlags nn_getComponentMethodFlags(nn_Component *c, const char *method); +const char *nn_getComponentType(nn_Component *c); +const char *nn_getComponentTypeID(nn_Component *c); +const char *nn_getComponentAddress(nn_Component *c); + +// Adds a component to the computer on a given slot. +// This will also queue a component_added signal if the computer is in a running state, unless silent is true. +// If the component already is mounted, an error is returned. +nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool silent); +// Removes a component from the computer. +// This will also queue a component_removed signal if the computer is in a running state, unless silent is true. +// If the component is not mounted, no error is returned. +nn_Exit nn_unmountComponent(nn_Computer *c, const char *address, bool silent); +nn_Exit nn_swapComponents(nn_Computer *c, nn_Component *previous, nn_Component *next, int slot); +// gets a component by address. Will return NULL if there is none. +nn_Component *nn_getComponent(nn_Computer *c, const char *address); +int nn_getComponentSlot(nn_Computer *c, const char *address); +size_t nn_countComponents(nn_Computer *c); +void nn_getComponents(nn_Computer *c, const char **components); + +// invoke the component method. +// Everything on-stack is taken as an argument. +// Will pop off trailing nulls. +// Every remaining stack value is what the component returned. +// In the case of errors, the contents of the stack is undefined +nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method); + +// send a signal to a component. +// Computer actually can be NULL, but the component may crash if the signal +// assumes one is specified. +nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal); + +// Userdata!!!! + +// Allocates a userdata index. Returns -1 on failure or if there are too many. +int nn_allocUserdata(nn_Computer *computer, void *state, const char *compAddress); + +// Frees a userdata index. +void nn_freeUserdata(nn_Computer *computer, size_t userdata); + +// Returns whether the userdata index is valid +bool nn_isUserdataValid(nn_Computer *computer, size_t userdata); + +// If compAddress is correct and userdata is valid, returns the state pointer. +// If not, returns NULL, to prevent UB. +void *nn_unwrapUserdata(nn_Computer *computer, size_t userdata, const char *compAddress); + +// gets the component address which manages this userdata +const char *nn_getUserdataComponent(nn_Computer *computer, size_t userdata); + +// Gets information about a method of this userdata, by index. +// If idx is out of bounds, this returns true, which means to stop iteration. +// If method->name is NULL, the method should be skipped. +bool nn_getUserdataMethod(nn_Computer *computer, size_t userdata, size_t idx, nn_Method *method); + +// Invokes a method on some userdata, same semantics as nn_invokeComponent +nn_Exit nn_invokeUserdata(nn_Computer *computer, size_t userdata, const char *method); + +// Serializes the userdata into a buffer and pushes it as a string. +// Make sure to keep track of its index and component address! +nn_Exit nn_serializeUserdata(nn_Computer *computer, size_t userdata); + +// Deserializes userdata at a particular index. +// NOTE: if the component does not exist, or the userdata index is already taken, this errors. +nn_Exit nn_deserializeUserdata(nn_Computer *computer, size_t userdata, const char *compAddress, const char *buf, size_t len); + +// default call budgets for 4 tiers of CPUs +extern double nn_defaultCallBudgets[4]; +// the call budget of a creative CPU +extern double nn_unlimitedCallBudget; +// default component limits for 4 tiers of component buses / CPUs +extern size_t nn_defaultComponentLimits[4]; +// the component limit of a creative component bus +extern size_t nn_creativeComponentLimit; + +// Sets the call budget. +// The default is 1. +void nn_setCallBudget(nn_Computer *computer, double budget); + +// gets the total call budget +double nn_getCallBudget(nn_Computer *computer); + +// returns the remaining call budget +double 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, 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, 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, with 1 small change to match OC's code. +// and is as follows: +// - Every value adds a 2 byte overhead +// - Numbers add another 8 bytes, true/false/null another 1 byte, 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); + +nn_Exit nn_transferErrorFrom(nn_Exit exit, nn_Computer *from, nn_Computer *to); + +// 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 + +// the bottom side, can also be downwards / negative y +#define NN_SIDE_BOTTOM 0 +// the top side, can also be upwards / positive y +#define NN_SIDE_TOP 1 +// backwards, can also be north / negative z +#define NN_SIDE_BACK 2 +// forwards, can also be south / positive z +#define NN_SIDE_FRONT 3 +// to the right, can also be west / negative x +#define NN_SIDE_RIGHT 4 +// to the left, can also be east / positive x +#define NN_SIDE_LEFT 5 +// absolutely no clue +#define NN_SIDE_UNKNOWN 6 + +// 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); + +// pushes a redstone_changed signal. +// side is the side of the device the redstone changed on +// oldValue is the old value +// newValue is the new value, must not be equal to oldValue +// color is the color of the redstone signal +// if color < 0, it is set to null +nn_Exit nn_pushRedstoneChanged(nn_Computer *computer, const char *redstoneAddress, int side, int oldValue, int newValue, int color); + +// pushes a motion signal. +// Do note that it is meant to only be sent if the entity has a direct line of sight and if |(relX, relY, relZ)| >= sensitivity. +// This signal does *not* check if the motion sensor actually is sensitive enough to detect it, so make sure to check it yourself. +// relX, relY and relZ are the relative postion in 3D Cartesian space. +// entityName can be NULL if the entity has no name. +nn_Exit nn_pushMotion(nn_Computer *computer, double relX, double relY, double relZ, const char *entityName); + +// Pushes an internet_ready signal. +// This signal is queued when an internet socket is ready for commmunication. +nn_Exit nn_pushInternetReady(nn_Computer *computer, const char *id, size_t idlen); + +// A buffer with encoded values +typedef struct nn_EncodedNetworkContents { + nn_Context *ctx; + char *buf; + size_t buflen; + size_t valueCount; +} nn_EncodedNetworkContents; + +// applies basic encoding to a network message. This encoding has a header, and thus should remain backwards-compatible. +// The encoding serves 2 purposes: +// 1. Prevent shared memory between computers. Values do not use atomic reference counting, and thus this could lead to UAF or memory leaks. +// 2. Simplify implementing packet queues, which should be used in relays. The lack of access to raw values would force implementers to use +// an encoding anyways. +// This only encodes the contents, not the sender, hops, or other metadata which may be needed in the queue. +// This does not pop the values, in case you need them afterwards. If you don't just call nn_popn(). +// The encoding is architecture-dependent, so be careful with storing it on-disk. +// Do note that the architecture-dependent parts are sizeof(double), sizeof(size_t) and endianness. +// The encoding is simple: +// - 0x00 for null +// - 0x01 for true +// - 0x02 for false +// - 0x03 + for a number +// - 0x04 + + for a string +// - 0x05 + for resource +// - 0x06 + + for a table +nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount); +// Allocates a copy of [buf] and stores it in contents. +// This is useful for copying network contents, either from storage or from another buffer. +nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount); +void nn_dropNetworkContents(nn_EncodedNetworkContents *contents); +// Pushes the encoded contents onto the stack. +// This does not drop the network contents. +nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents); + +// push a modem_message, can be queued by both modems and tunnels. +// This does not check if the modem has that port open, so make sure to check it yourself. +// It does not check if the distance is within the modem's range, if it is wireless, and thus does not send it. +// Note that relays should change the sender. +nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents); + +// EEPROM class + +// 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; + +typedef enum nn_EEPROMAction { + // component is dropped + NN_EEPROM_DROP, + // check if readonly. If so, buflen should be 1, else it should be 0. + NN_EEPROM_ISRO, + // make the EEPROM readonly. Checksum already verified. + NN_EEPROM_MKRO, + // write the contents of the code into buf. + // Set buflen to the length. + NN_EEPROM_GET, + // store the contents in buf into the EEPROM as code. + // the length of buf is in buflen. + NN_EEPROM_SET, + NN_EEPROM_GETDATA, + NN_EEPROM_SETDATA, + NN_EEPROM_GETARCH, + NN_EEPROM_SETARCH, + NN_EEPROM_GETLABEL, + NN_EEPROM_SETLABEL, +} nn_EEPROMAction; + +typedef struct nn_EEPROMRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_EEPROM *eeprom; + nn_EEPROMAction action; + union { + char *buf; + const char *robuf; + bool readonly; + }; + size_t buflen; +} nn_EEPROMRequest; + +typedef nn_Exit (nn_EEPROMHandler)(nn_EEPROMRequest *request); + +// Tier 1 - The normal EEPROM equivalent +// Tier 2 - A better EEPROM +// Tier 3 - An even better EEPROM +// Tier 4- The best EEPROM +extern const nn_EEPROM nn_defaultEEPROMs[4]; + +nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler); + +// Filesystem class + +typedef struct nn_Filesystem { + // the maximum capacity of the filesystem + size_t spaceTotal; + // how many read calls can be done per tick + // seek also count as reads. + double readsPerTick; + // how many write calls can be done per tick + double writesPerTick; + // how many open calls can be done per tick + double opensPerTick; + // 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; + // maximum size of a read. + // Do note that this entire buffer is allocated, and thus if you + // set it to a high number, you may get weird high allocations. + // This also determines read performance. + size_t maxReadSize; +} nn_Filesystem; + +typedef enum nn_FSAction { + NN_FS_DROP, + + // drive metadata + NN_FS_SPACEUSED, + NN_FS_GETLABEL, + NN_FS_SETLABEL, + NN_FS_ISRO, + + // for file I/O + NN_FS_OPEN, + NN_FS_CLOSE, + NN_FS_READ, + NN_FS_WRITE, + NN_FS_SEEK, + + // for list + NN_FS_OPENDIR, + NN_FS_READDIR, + NN_FS_CLOSEDIR, + + // checking metadata + NN_FS_STAT, + // make directory, recursively + NN_FS_MKDIR, + + // rename, if renamed to NULL then remove + NN_FS_RENAME, +} nn_FSAction; + +typedef enum nn_FSWhence { + NN_SEEK_SET, + NN_SEEK_CUR, + NN_SEEK_END, +} nn_FSWhence; + +typedef struct nn_FSRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Filesystem *fs; + nn_FSAction action; + int fd; + union { + struct { + const char *path; + const char *mode; + } open; + struct { + char *buf; + size_t len; + } read; + struct { + const char *buf; + size_t len; + } write; + struct { + nn_FSWhence whence; + // set to new offset + int off; + } seek; + const char *opendir; + struct { + // directory path, as a reminder if need be + const char *dirpath; + char *buf; + // set to length of entry name + size_t len; + } readdir; + struct { + // set to NULL if missing + const char *path; + // whether it is a directory + bool isDirectory; + // in seconds. Result will be multiplied by 1000. + // This is because OpenOS code is garbage. + intptr_t lastModified; + // size. 0 for directories. + size_t size; + } stat; + struct { + const char *from; + // if NULL, delete from, recursively. + const char *to; + } rename; + const char *mkdir; + struct { + const char *buf; + size_t len; + } setlabel; + struct { + char *buf; + size_t len; + } getlabel; + bool isReadonly; + size_t spaceUsed; + }; +} nn_FSRequest; + +typedef nn_Exit (nn_FSHandler)(nn_FSRequest *request); + +// 4 Tiers. +// 0 - Tier 1 equivalent +// 1 - Tier 2 equivalent +// 2 - Tier 3 equivalent +// 3 - Tier 4, a better version of Tier 3. +extern const nn_Filesystem nn_defaultFilesystems[4]; +// a basic floppy +extern const nn_Filesystem nn_defaultFloppy; +// a generic tmpfs +extern const nn_Filesystem nn_defaultTmpFS; + +nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler); + +bool nn_mergeFilesystems(nn_Filesystem *merged, const nn_Filesystem *fs, size_t len); + +// Drive class + +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. + // Anything that kicks out the current cacheline counts as a 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 (may be eaten by cache) 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 with infinite lifespan. + // 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. + // It is aligned to the cache lines. + 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; + +extern const nn_Drive nn_defaultDrives[4]; +extern const nn_Drive nn_floppyDrive; + + +typedef enum nn_DriveAction { + // drive gone + NN_DRIVE_DROP, + // get current label + NN_DRIVE_GETLABEL, + // set or remove current label + NN_DRIVE_SETLABEL, + // get the current head position, as a sector + NN_DRIVE_CURPOS, + // read a sector + NN_DRIVE_READSECTOR, + // write a sector + NN_DRIVE_WRITESECTOR, + // write a byte + NN_DRIVE_WRITEBYTE, + // is drive read-only + NN_DRIVE_ISRO, +} nn_DriveAction; + +typedef struct nn_DriveRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Drive *drv; + nn_DriveAction action; + union { + struct { + char *buf; + size_t len; + } getlabel; + struct { + const char *label; + size_t len; + } setlabel; + size_t curpos; + struct { + // 1-indexed + size_t sector; + char *buf; + } readSector; + struct { + // 1-indexed + size_t sector; + const char *buf; + } writeSector; + struct { + // 1-indexed + size_t byte; + unsigned char value; + } writeByte; + bool readonly; + }; +} nn_DriveRequest; + +typedef nn_Exit (nn_DriveHandler)(nn_DriveRequest *request); + +nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const nn_Drive *drive, void *state, nn_DriveHandler *handler); + +bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len); + +typedef struct nn_NandFlash { + // capacity of flash + size_t capacity; + // sector size + size_t sectorSize; + // reads per tick + size_t readsPerTick; + // writes per tick + size_t writesPerTick; + // The layering, in bits. + // 1 is SLC, 2 is MLC, 3 is TLC, etc. + // This number may amplify how quickly the total write count increases. + size_t cellLevel; + // the maximum amount of write amplification. + // Set to 0 to disable amplification RNG. + // The game will generate, using Context RNG, a real number from [0, 1] + // then raise it to writeAmplificationExponent, + // then multiply it by this number, and by the cell level. + // then clamp it to be at least 1 and at most this maximum. + unsigned int maxWriteAmplification; + int writeAmplificationExponent; + // the maximum amount of writes *per sector.* + // Set to 0 to make the nandflash eternal. + size_t maxWriteCount; + // how much per byte + double dataEnergyCost; +} nn_NandFlash; + +typedef enum nn_FlashAction { + NN_FLASH_DROP, + NN_FLASH_GETLABEL, + NN_FLASH_SETLABEL, + NN_FLASH_ISRO, + // read a sector + NN_FLASH_READSECTOR, + // write a sector + // also adds an amount of writes + NN_FLASH_WRITESECTOR, + // write a sector + // also adds an amount of writes + NN_FLASH_WRITEBYTE, + // get the amount of writes + NN_FLASH_GETWRITES, +} nn_FlashAction; + +typedef struct nn_FlashRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_NandFlash *flash; + nn_FlashAction action; + union { + struct { + char *buf; + size_t len; + } getlabel; + struct { + const char *buf; + size_t len; + } setlabel; + struct { + char *buf; + // 1-indexed + size_t sec; + } readsector; + struct { + const char *buf; + // 1-indexed + size_t sec; + // how many writes to add + size_t writesAdded; + } writesector; + struct { + size_t byte; + char val; + // how many writes to add + size_t writesAdded; + } writebyte; + // for GETWRITES + size_t writeCount; + bool readonly; + }; +} nn_FlashRequest; + +typedef nn_Exit (nn_FlashHandler)(nn_FlashRequest *request); + +extern const nn_NandFlash nn_defaultSSDs[4]; +extern const nn_NandFlash nn_floppySSD; + +nn_Component *nn_createFlash(nn_Universe *universe, const char *address, const nn_NandFlash *drive, void *state, nn_FlashHandler *handler); + +bool nn_mergeFlash(nn_NandFlash *merged, const nn_NandFlash *flash, size_t len); + +// Screen class + +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 { + // maximum width + int maxWidth; + // maximum height + int maxHeight; + // screen features + nn_ScreenFeatures features; + // default palette, if applicable. + // Can be NULL if there is none, + // in which case consider memsetting + // them to #000000. + int *defaultPalette; + // the amount of editable palette colors + int paletteColors; + // how many editable palette colors there are. + // It'd always be the first N ones. + int editableColors; + // the maximum depth of the screen + char maxDepth; + // energy per fully white pixel. + // Scaled to mathc luminance of each pixel. + // This is meant to be per Minecraft tick, so 20 times per second. + double energyPerPixel; + // minimum brightness. Default brightness is always 100% + // The value here is meant to be scaled such that 1 means 100% brightness + double minBrightness; + // maximum brightness. Note that brightness rendering is emulator-specific + // The value here is meant to be scaled such that 1 means 100% brightness + double maxBrightness; +} nn_ScreenConfig; + +// OC has 3 tiers, NN adds a 4th one as well. +extern const nn_ScreenConfig nn_defaultScreens[4]; + + +// GPU class +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 pixel set. + double energyPerWrite; + // energy per pixel filled + double energyPerFill; + // energy per pixel copied + double energyPerCopy; + // energy per space set. + double energyPerClear; +} nn_GPU; + +// 1 GPU tier for every screen. +extern const nn_GPU nn_defaultGPUs[4]; + +typedef enum nn_GPUAction { + NN_GPU_DROP, + NN_GPU_BIND, + NN_GPU_GETSCREEN, + NN_GPU_GETBG, + NN_GPU_SETBG, + NN_GPU_GETFG, + NN_GPU_SETFG, + NN_GPU_GETPALETTE, + NN_GPU_SETPALETTE, + NN_GPU_MAXDEPTH, + NN_GPU_GETDEPTH, + NN_GPU_SETDEPTH, + NN_GPU_MAXRES, + NN_GPU_GETRES, + NN_GPU_SETRES, + NN_GPU_GETVIEWPORT, + NN_GPU_SETVIEWPORT, + NN_GPU_GET, + NN_GPU_SET, + NN_GPU_COPY, + NN_GPU_FILL, + NN_GPU_GETACTIVEBUF, + NN_GPU_SETACTIVEBUF, + NN_GPU_BUFFERS, + NN_GPU_ALLOCBUF, + NN_GPU_FREEBUF, + NN_GPU_FREEALLBUFS, + NN_GPU_FREEMEM, + NN_GPU_GETBUFSIZE, + NN_GPU_BITBLT, +} nn_GPUAction; + +typedef struct nn_GPURequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_GPU *gpu; + nn_GPUAction action; + union { + struct { + const char *address; + bool reset; + } bind; + // GETSCREEN result + char screenAddr[NN_MAX_ADDRESS]; + // GET/SET BG/FG + struct { + int color; + bool isPalette; + int oldColor; + bool wasPalette; + int oldPaletteIdx; // -1 if none + } color; + // GET/SET PALETTE + struct { + int index; + int color; + int oldColor; + } palette; + // MAXDEPTH / GETDEPTH / SETDEPTH + struct { + char depth; + char oldDepth; + } depth; + // MAXRES/GETRES/SETRES/GETVIEWPORT/SETVIEWPORT + struct { + int width; + int height; + } resolution; + // GET pixel + struct { + int x, y; + nn_codepoint codepoint; + int fg, bg; + int fgIdx, bgIdx; // -1 if not palette + } get; + // SET string + struct { + int x, y; + const char *value; + size_t len; + bool vertical; + } set; + // COPY + struct { + int x, y, w, h, tx, ty; + } copy; + // FILL + struct { + int x, y, w, h; + nn_codepoint codepoint; + } fill; + // GET/SET ACTIVE BUFFER, FREE BUFFER + struct { + int index; + } buffer; + // ALLOCATE BUFFER + struct { + int w, h, index; + } allocBuf; + // TOTALMEM / FREEMEM + size_t memory; + // GETBUFSIZE + struct { + int index, w, h; + } bufSize; + // BITBLT + struct { + int dst, col, row, w, h; + int src, fromCol, fromRow; + } bitblt; + // BUFFERS / count returned here, indices + // pushed on stack by handler + size_t bufCount; + }; +} nn_GPURequest; + +typedef nn_Exit (nn_GPUHandler)(nn_GPURequest *req); + +nn_Component *nn_createGPU( + nn_Universe *universe, const char *address, + const nn_GPU *gpu, void *state, + nn_GPUHandler *handler); + +typedef enum nn_ScreenAction { + NN_SCREEN_DROP, + NN_SCREEN_ISON, + NN_SCREEN_TURNON, + NN_SCREEN_TURNOFF, + NN_SCREEN_GETASPECTRATIO, + NN_SCREEN_GETKEYBOARDS, + NN_SCREEN_SETPRECISE, + NN_SCREEN_ISPRECISE, + NN_SCREEN_SETTOUCHINVERTED, + NN_SCREEN_ISTOUCHINVERTED, + // sets the brightness (from 0 to 1) + NN_SCREEN_SETBRIGHT, + // get the brightness (from 0 to 1) + NN_SCREEN_GETBRIGHT, +} nn_ScreenAction; + +typedef struct nn_ScreenRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_ScreenConfig *screen; + nn_ScreenAction action; + union { + // turnOn / turnOff / isOn + struct { + bool wasOn; + bool isOn; + } power; + // getAspectRatio + struct { + int w, h; + } aspect; + // getKeyboards — addresses pushed on stack by + // handler; count returned here + size_t kbCount; + // setPrecise / isPrecise / + // setTouchModeInverted / isTouchModeInverted + bool flag; + double brightness; + }; +} nn_ScreenRequest; + +typedef nn_Exit (nn_ScreenHandler)(nn_ScreenRequest *req); + +nn_Component *nn_createScreen( + nn_Universe *universe, const char *address, + const nn_ScreenConfig *scrconf, void *state, + nn_ScreenHandler *handler +); + +// Computes a CRC32 checksum +unsigned int nn_computeCRC32(const char *data, size_t datalen); + +typedef struct nn_DataCard { + // The buffer size of the data card, limit in both input and output. + // In OC, this value is 1MiB regardless of tier. + // As there is no out buffer and you are expected to push strings, + // the buffer is not pre-allocated. This is intentional, as memory + // would be wasted and it could be a potential attack vector otherwise. + size_t limit; + + // The maximum amount of secure random bytes that can be generated. + // In OC, this was hardcoded to 1024. Here, its configurable. + // Unlike the normal limit, this does not preallocate the maximum capacity, + // as the amount of bytes needed is known perfectly. + size_t maxRandom; + + // for encoding/decoding. OC defaulted to 32 + size_t base64PerTick; + // for deflate/inflate. OC defaulted to 4 + size_t deflatingPerTick; + // OC defaulted to 32 + size_t crc32PerTick; + // OC defaulted to 8 + size_t md5PerTick; + // OC defaulted to 4 + size_t sha256PerTick; + // for encrypt/decrypt. OC defaulted to 4 + size_t encryptPerTick; + // for generateKeyPair. OC defaulted to 1 + size_t genPerTick; + // for deserializeKey. OC defaulted to 8 + size_t deserializePerTick; + // OC defaulted to 1 + size_t ecdhPerTick; + // OC defaulted to 1 + size_t ecdsaPerTick; + // OC defaults to 4 + size_t randomPerTick; + + // Capabilities + bool canHash; + bool canEncrypt; + bool canECDH; + bool canCompress; + + // Trivial operation cost (CRC32 and base64 encoding/decoding) + double trivialCost; + double trivialCostByte; + + // Simple operation cost (MD5 and encryption/decryption) + double simpleCost; + double simpleCostByte; + + double complexCost; + double complexCostByte; + + // Assymetric operation cost (ECDH, ECDSA). ECDH has no per-byte cost, and ECDSA uses complexCostByte as it relies on SHA256. + double assymetricCost; +} nn_DataCard; + +typedef enum nn_DataCardAction { + // Data card destroyed + NN_DATA_DROP, + + // If you want to match the behavior of OC, which you should if you want + // data compressed or encrypted in OC to work in your emulators, you should + // aim to match what the JVM, com.google.common.hash.Hashing and javax.crypto do. + // For more details, see https://github.com/MightyPirates/OpenComputers/blob/master-MC1.7.10/src/main/scala/li/cil/oc/server/component/DataCard.scala + + // encoding base64 + NN_DATA_ENCODE64, + + NN_DATA_DECODE64, + // hashing + + // CRC32, little endian + NN_DATA_CRC32, + + // SHA2-256 hash, optional HMAC key (javax.crypto HmacSHA256) + NN_DATA_SHA256, + + // MD5 hash, optional HMAC key (javax.crypto HmacMD5) + NN_DATA_MD5, + + // Deflate. To match OC, make it follow the ZLIB format, which has a 2 byte header. + NN_DATA_DEFLATE, + + // Inflate. Should support the ZLIB format, as thats what OC uses, and GZIP support is optional. + NN_DATA_INFLATE, + + // Encrypt data with AES-128. The full algorithm is AES/CBC/PKCS5, as is use PKCS5 for padding, CBC for block sequences, and AES-128 for encrypting blocks. + // It does also receive a 128-bit AES Initialization Vector, for better security. + NN_DATA_ENCRYPT, + + // Decrypt data, also using AES/CBC/PKCS5. + NN_DATA_DECRYPT, + + // Meant to be *secure RNG*, and can generate anywhere between 1 and the data card's maxRandom. + NN_DATA_RANDOM, + + // Generate an ECDH public/private pair of either 256 or 384 bits each. + NN_DATA_GENKEYS, + + // validate key, cuz we feel like it + NN_DATA_VALIDATEKEY, + + // Does an ECDH pass, matching javax.crypto.KeyAgreement. + // This generates a shared secret as binary data. + // This is not an AES key as output, the AES key is often computed by either MD5 hashing + // the shared secret, or SHA256 hashing it and chopping the hash in half. + NN_DATA_ECDH, + + // ECDSA algorithm, sign data using an ECDH (private) key. + NN_DATA_ECDSA_SIGN, + // ECDSA algorithm, verify data using an ECDH (public) key and signature. + NN_DATA_ECDSA_VERIFY, +} nn_DataCardAction; + +// The representation of datacard key userdata +typedef struct nn_DataKey { + bool isPublic; + unsigned short bytelen; + char bytes[]; +} nn_DataKey; + +typedef struct nn_DataCardRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_DataCard *dataCard; + nn_DataCardAction action; + // TODO: the fields + union { + struct { + const char *data; + size_t datalen; + char checksum[4]; + } crc32; + struct { + const char *data; + size_t datalen; + char checksum[16]; + } md5; + struct { + const char *data; + size_t datalen; + char checksum[32]; + } sha256; + // for encrypt/decrypt + struct { + const char *data; + size_t datalen; + const char *key; + const char *iv; + } encrypt; + struct { + char *buf; + size_t buflen; + } randbuf; + // for deflate, inflate, encode64 and decode64 + struct { + const char *data; + size_t datalen; + }; + struct { + const char *buf; + size_t len; + bool isPublic; + } validatekey; + struct { + const nn_DataKey *publicKey; + const nn_DataKey *privateKey; + } ecdh; + struct { + const char *data; + size_t len; + const nn_DataKey *privateKey; + } sign; + struct { + const char *data; + size_t datalen; + const nn_DataKey *publicKey; + const char *signature; + size_t siglen; + // returns whether the signature actually passed + bool sigpassed; + } checksig; + int genkeybitsize; + }; +} nn_DataCardRequest; + +typedef nn_Exit (nn_DataCardHandler)(nn_DataCardRequest *req); + +extern nn_DataCard nn_defaultDataCards[3]; + +nn_Component *nn_createDataCard(nn_Universe *universe, const char *address, const nn_DataCard *dataCard, void *state, nn_DataCardHandler *handler); + +typedef struct nn_Modem { + // maximum range. Set to 0 for non-wireless modems + size_t maxRange; + // maximum values in a packet + size_t maxValues; + // maximum logical packet size. Note that the encoding is more efficient than the packet size algorithm estimates + size_t maxPacketSize; + // the maximum amount of open ports + size_t maxOpenPorts; + // whether the modem supports wired connectivity. + // Support for wireless checks if maxRange > 0. + bool isWired; + // base energy cost of 1 network message + double basePacketCost; + // energy cost of a full packet, at the maximum logical size + double fullPacketCost; + // energy cost per wireless packet strength level + double costPerStrength; +} nn_Modem; + +extern nn_Modem nn_defaultWiredModem; +extern nn_Modem nn_defaultWirelessModems[2]; + +typedef enum nn_ModemAction { + // modem dropped + NN_MODEM_DROP, + // check whether a port is open + NN_MODEM_ISOPEN, + // open a port + NN_MODEM_OPEN, + // close a port + NN_MODEM_CLOSE, + // get open ports + NN_MODEM_GETPORTS, + // send/broadcast a message + NN_MODEM_SEND, + // get current modem strength + NN_MODEM_GETSTRENGTH, + // set current modem strength + NN_MODEM_SETSTRENGTH, + // returns the wake message + NN_MODEM_GETWAKEMESSAGE, + // set the wake message + NN_MODEM_SETWAKEMESSAGE, +} nn_ModemAction; + +typedef struct nn_ModemRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Modem *modem; + const char *localAddress; + nn_ModemAction action; + union { + struct { + size_t port; + bool opened; + } isOpen; + size_t openPort; + // NN_CLOSEPORTS means close all + size_t closePort; + struct { + // store the port numbers in this buffer + unsigned short *activePorts; + // the amount of active ports. + // the initial value is the capacity of activePorts + size_t len; + } getPorts; + struct { + const nn_EncodedNetworkContents *contents; + // NULL for broadcast + const char *address; + size_t port; + // The signal strength it was sent at + // This is an *OUT* field, it is assumed the handler keeps track of the correct strength + size_t strengthSent; + } send; + // for getStrength, setStrength. + size_t strength; + struct { + char *buf; + size_t len; + bool isFuzzy; + } getWake; + struct { + const char *buf; + size_t len; + bool isFuzzy; + } setWake; + }; +} nn_ModemRequest; + +typedef nn_Exit (nn_ModemHandler)(nn_ModemRequest *req); + +nn_Component *nn_createModem(nn_Universe *universe, const char *address, const nn_Modem *modem, void *state, nn_ModemHandler *handler); + +typedef struct nn_Tunnel { + // maximum values in a packet + size_t maxValues; + // maximum logical packet size. Note that the encoding is more efficient than the packet size algorithm estimates + size_t maxPacketSize; + // minimum energy cost of 1 transmission + double basePacketCost; + // extra energy cost of 1 full transmission + double fullPacketCost; +} nn_Tunnel; + +extern nn_Tunnel nn_defaultTunnel; + +typedef enum nn_TunnelAction { + // tunnel dropped + NN_TUNNEL_DROP, + // gets the channel, result should be pushed to the top of the stack + NN_TUNNEL_GETCHANNEL, + // send/broadcast a message + NN_TUNNEL_SEND, + // returns the wake message + NN_TUNNEL_GETWAKEMESSAGE, + // set the wake message + NN_TUNNEL_SETWAKEMESSAGE, +} nn_TunnelAction; + +typedef struct nn_TunnelRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Tunnel *tunnel; + const char *localAddress; + nn_TunnelAction action; + union { + // for send + const nn_EncodedNetworkContents *toSend; + struct { + char *buf; + size_t len; + bool isFuzzy; + } getWake; + struct { + const char *buf; + size_t len; + bool isFuzzy; + } setWake; + }; +} nn_TunnelRequest; + +typedef nn_Exit (nn_TunnelHandler)(nn_TunnelRequest *req); + +nn_Component *nn_createTunnel(nn_Universe *universe, const char *address, const nn_Tunnel *modem, void *state, nn_TunnelHandler *handler); + +typedef enum nn_InternetProtocol { + NN_INET_NONE = 0, + NN_INET_HTTP = 1<<0, + NN_INET_TCP = 1<<1, + NN_INET_UDP = 1<<2, + NN_INET_WEBSOCKET = 1<<3, + NN_INET_TLS = 1<<4, + + NN_INET_ALL = NN_INET_HTTP | NN_INET_TCP | NN_INET_UDP | NN_INET_WEBSOCKET | NN_INET_TLS, +} nn_InternetProtocol; + +typedef struct nn_InternetCard { + // bitwise OR multiple of them + unsigned char protocolsSupported; + // per-byte cost of a write + double transmissionEnergyCost; +} nn_InternetCard; + +extern nn_InternetCard nn_defaultInternetCard; + +typedef struct nn_InternetConnection { + nn_InternetProtocol protocol; + void *state; +} nn_InternetConnection; + +typedef struct nn_HTTPHeader { + const char *name; + const char *value; +} nn_HTTPHeader; + +typedef struct nn_InternetRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Tunnel *tunnel; + const char *localAddress; + nn_InternetConnection *connection; + union { + // does a socket connection, for any of the supported protocols + struct { + // URL of connection + const char *url; + // port. Useless for HTTP connections, as they should use 80 for HTTP and 443 for HTTPS + unsigned short port; + // HTTP specific + size_t postdatalen; + const char *postdata; + const nn_HTTPHeader *headers; + size_t headerlen; + } connect; + }; +} nn_InternetRequest; + +// 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(); + +// Returns a number from 0 to 1 representing the perceived luminance. +double nn_colorLuminance(int color); +// 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. +// Invalid depths behave identically to 24-bit, in which case the color is left unchanged. +int nn_mapDepth(int color, int depth); + +// 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); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/native/neonucleus.h.old b/src/native/neonucleus.h.old new file mode 100644 index 0000000..a24f266 --- /dev/null +++ b/src/native/neonucleus.h.old @@ -0,0 +1,2004 @@ +#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__) + #ifndef NN_WINDOWS + #define NN_WINDOWS + #endif +#elif __APPLE__ + #ifndef NN_MACOS + #define NN_MACOS + #endif +#elif __linux__ + #ifndef NN_LINUX + #define NN_LINUX + #endif +#endif + +#if __unix__ + #ifndef NN_UNIX + #define NN_UNIX + #endif + #ifndef NN_POSIX + #define NN_POSIX + #endif +#elif defined(_POSIX_VERSION) + #ifndef NN_POSIX + #define NN_POSIX + #endif +#endif + + +#if defined(_MSC_VER) && !defined(__cplusplus) +#ifndef NN_ATOMIC_MSVC +#define NN_ATOMIC_MSVC +#endif +#endif + +#ifdef _MSC_VER +#define NN_INIT(type) +#else +#define NN_INIT(type) (type) +#endif + +// every C standard header we depend on, conveniently put here +#include // for NULL, +#include // for intptr_t +#include // for true, false and bool + +/* MSVC can't use VLA; + * What we see : NN_VLA(const char *, comps, len); + * What compiler see after preproccessor : const char * comps[len]; + * What actaully was : const char *comps[len]; +*/ +// Test: gcc -E -DNN_VLA\(type,name,count\)="type name[count]" -x c - <<< 'NN_VLA(const char *, comps, len);' +#ifdef _MSC_VER +// avoid #include , for it can pollute the namespace +// with symbols to functions which are not linked with in baremetal. +void *_alloca(size_t); +#define NN_VLA(type, name, count) type *name = (type *)_alloca(sizeof(type) * (count)) +#else +#define NN_VLA(type, name, count) type name[count] +#endif + +// 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 ((size_t)1024 * NN_GiB) + +// 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 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 16 +// 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, it is the same stuff, but direct ones may still be used across threads. +// 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); + +char *nn_strdup(nn_Context *ctx, const char *s); +void nn_strfree(nn_Context *ctx, char *s); + +typedef struct nn_Lock nn_Lock; + +nn_Lock *nn_createLock(nn_Context *ctx); +void nn_destroyLock(nn_Context *ctx, nn_Lock *lock); +void nn_lock(nn_Context *ctx, nn_Lock *lock); +void nn_unlock(nn_Context *ctx, nn_Lock *lock); + +double nn_currentTime(nn_Context *ctx); + +// generate a random RNG from 0 to the maximum +size_t nn_rand(nn_Context *ctx); +// generate a random float [0, 1) +double nn_randf(nn_Context *ctx); +// generate a random float [0, 1] +double nn_randfi(nn_Context *ctx); + +typedef char nn_uuid[37]; +void nn_randomUUID(nn_Context *ctx, nn_uuid uuid); + +// Basic utils + +// Does canonical path handling. Is used for sandboxing paths. +// It will turn \ to /, will turn invalid characters into spaces, +// and will resolve ... +// it also gets rid of / prefixes, / suffixes and // +void nn_simplifyPath(const char original[NN_MAX_PATH], char simplified[NN_MAX_PATH]); + +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 *userdata); +void nn_destroyUniverse(nn_Universe *universe); +void *nn_getUniverseData(nn_Universe *universe); +size_t nn_getUniverseMemoryLimit(nn_Universe *universe); +size_t nn_limitMemory(nn_Universe *universe, size_t memory); +void nn_setUniverseMemoryLimit(nn_Universe *universe, size_t limit); +size_t nn_getUniverseStorageLimit(nn_Universe *universe); +void nn_setUniverseStorageLimit(nn_Universe *universe, size_t limit); +size_t nn_limitStorage(nn_Universe *universe, size_t storage); + +// 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 or synchronized task + 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_TICK, where the tick is synchronized + bool synchronized; + // 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; + 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); +void nn_lockComputer(nn_Computer *computer); +void nn_unlockComputer(nn_Computer *computer); +// stops the computer if an architecture state is already present, +// will also clear the signal buffer and set the state to NN_BOOTUP. +nn_Exit nn_startComputer(nn_Computer *computer); +// destroys the architecture state if present. +// Will also do other shutdown routines, such as unmounting every +void nn_stopComputer(nn_Computer *computer); +void nn_forceCrashComputer(nn_Computer *computer, const char *s); +// returns whether an architecture state is present +bool nn_isComputerOn(nn_Computer *computer); + +typedef enum nn_ComputerEvent { + // when powered on + NN_COMPUTER_POWERON, + // when powered off + NN_COMPUTER_POWEROFF, + // when force-crashed + NN_COMPUTER_FORCECRASH, + // when crashed + NN_COMPUTER_CRASH, +} nn_ComputerEvent; + +typedef void nn_ComputerListener(nn_Computer *computer, nn_ComputerEvent event); +void nn_setComputerListener(nn_Computer *computer, nn_ComputerListener *listener); + +typedef struct nn_Beep { + double frequency; + double duration; + double volume; +} nn_Beep; + +void nn_setComputerBeep(nn_Computer *computer, nn_Beep beep); +bool nn_getComputerBeep(nn_Computer *computer, nn_Beep *beep); +void nn_clearComputerBeep(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); +void nn_resetIdleTime(nn_Computer *computer); +// runs a tick of the computer. Make sure to check the state as well! +// Does not do anything if we're currently waiting on a synced call +// 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); + +// runs a synchronized tick of the computer. How this differs depends on architecture. +// Generally, this is meant to be in the same thread for all computers, and is if the external world is fundamentally not thread-safe, +// however components must interact with it. +// In this case, those component methods would be marked as NN_INDIRECT, or more accurately will not be marked as NN_DIRECT, and the architecture would queue them as synchronized tasks. +// Architectures should generally NOT ignore this if they can. +nn_Exit nn_tickSynchronized(nn_Computer *computer); + +// raw component and methods + +typedef struct nn_Component nn_Component; + +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 *doc; + nn_MethodFlags flags; +} nn_Method; + +// component signals + +// mounted +#define NN_CSIGMOUNTED "mounted" +// unmounted +#define NN_CSIGUNMOUNTED "unmounted" + +typedef enum nn_ComponentAction { + // component dropped + NN_COMP_DROP, + // component method invoked + NN_COMP_INVOKE, + // checking if component method is enabled + // (may be locked by tier) + NN_COMP_CHECKMETHOD, + // signal sent to the machine + NN_COMP_SIGNAL, +} nn_ComponentAction; + +typedef struct nn_ComponentRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + void *classState; + const char *compAddress; + nn_ComponentAction action; + // method index + unsigned int methodIdx; + union { + // return count + size_t returnCount; + // method enabled + bool methodEnabled; + // signal invocation + const char *signal; + }; +} nn_ComponentRequest; + +typedef nn_Exit (nn_ComponentHandler)(nn_ComponentRequest *request); + +// creates a blank component. +// It has no methods, +nn_Component *nn_createComponent(nn_Universe *universe, const char *address, const char *type); +void nn_retainComponent(nn_Component *c); +void nn_retainComponentN(nn_Component *c, size_t n); +void nn_dropComponent(nn_Component *c); +void nn_dropComponentN(nn_Component *c, size_t n); + +// configure the state +void nn_setComponentHandler(nn_Component *c, nn_ComponentHandler *handler); +void nn_setComponentState(nn_Component *c, void *state); +void nn_setComponentClassState(nn_Component *c, void *state); +// sets the methods, same implications as setComponentMethodsArray. +// methods is NULL-terminated, as in, it is terminated by a method with a NULL name. +nn_Exit nn_setComponentMethods(nn_Component *c, const nn_Method *methods); +// sets the methods. +// The memory of the strings is copied, so they can be freed after this returns. +// This operation is NOT atomic, if it fails, it will clear out the previous methods. +nn_Exit nn_setComponentMethodsArray(nn_Component *c, const nn_Method *methods, size_t count); +// Sets an internal type ID, which is meant to be a more precise typename. +// For example, ncomplib would set ncl-screen for the screen component, +// so the GPU can confirm it is being bound to a screen it knows how to use. +nn_Exit nn_setComponentTypeID(nn_Component *c, const char *internalTypeID); + +// Sets the method flags +void nn_setComponentMethodFlags(nn_Component *c, const char *method, nn_MethodFlags flags); +// combines method flags +void nn_addComponentMethodFlags(nn_Component *c, const char *method, nn_MethodFlags flags); +// removes method flags +void nn_removeComponentMethodFlags(nn_Component *c, const char *method, nn_MethodFlags flags); + +// get component state +void *nn_getComponentState(nn_Component *c); +// get component class state +void *nn_getComponentClassState(nn_Component *c); +// counts how many methods are registered. May return too many if some of them are not enabled. +size_t nn_countComponentMethods(nn_Component *c); +// will fill the methodnames array with the names of the *enabled* methods. +// Will set *len to the amount of methods. +void nn_getComponentMethods(nn_Component *c, const char **methodnames, size_t *len); +// whether a method is defined and enabled +bool nn_hasComponentMethod(nn_Component *c, const char *method); +const char *nn_getComponentDoc(nn_Component *c, const char *method); +nn_MethodFlags nn_getComponentMethodFlags(nn_Component *c, const char *method); +const char *nn_getComponentType(nn_Component *c); +const char *nn_getComponentTypeID(nn_Component *c); +const char *nn_getComponentAddress(nn_Component *c); + +// Adds a component to the computer on a given slot. +// This will also queue a component_added signal if the computer is in a running state, unless silent is true. +// If the component already is mounted, an error is returned. +nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot, bool silent); +// Removes a component from the computer. +// This will also queue a component_removed signal if the computer is in a running state, unless silent is true. +// If the component is not mounted, no error is returned. +nn_Exit nn_unmountComponent(nn_Computer *c, const char *address, bool silent); +nn_Exit nn_swapComponents(nn_Computer *c, nn_Component *previous, nn_Component *next, int slot); +// gets a component by address. Will return NULL if there is none. +nn_Component *nn_getComponent(nn_Computer *c, const char *address); +int nn_getComponentSlot(nn_Computer *c, const char *address); +size_t nn_countComponents(nn_Computer *c); +void nn_getComponents(nn_Computer *c, const char **components); + +// invoke the component method. +// Everything on-stack is taken as an argument. +// Will pop off trailing nulls. +// Every remaining is what the component returned. +// In the case of +nn_Exit nn_invokeComponent(nn_Computer *computer, const char *compAddress, const char *method); + +// send a signal to a component. +// Computer actually can be NULL, but the component may crash if the signal +// assumes one is specified. +nn_Exit nn_signalComponent(nn_Component *component, nn_Computer *computer, const char *signal); + +// 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, with 1 small change to match OC's code. +// and is as follows: +// - Every value adds a 2 byte overhead +// - Numbers add another 8 bytes, true/false/null another 1 byte, 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 component classes. +// These components still make no assumptions about the OS, and still require handlers to connect them to the outside work. + +// Wrapping a computer as a component. +// It's a new instance each time. +// The computer MUST NOT be dropped before this component is fully gone. + +nn_Exit nn_transferErrorFrom(nn_Exit exit, nn_Computer *from, nn_Computer *to); +nn_Computer *nn_fromWrappedComputer(nn_Component *component); +nn_Component *nn_wrapComputer(nn_Computer *computer); + +// EEPROM class + +// 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; + +typedef enum nn_EEPROMAction { + // component is dropped + NN_EEPROM_DROP, + // check if readonly. If so, buflen should be 1, else it should be 0. + NN_EEPROM_ISRO, + // make the EEPROM readonly. Checksum already verified. + NN_EEPROM_MKRO, + // write the contents of the code into buf. + // Set buflen to the length. + NN_EEPROM_GET, + // store the contents in buf into the EEPROM as code. + // the length of buf is in buflen. + NN_EEPROM_SET, + NN_EEPROM_GETDATA, + NN_EEPROM_SETDATA, + NN_EEPROM_GETARCH, + NN_EEPROM_SETARCH, + NN_EEPROM_GETLABEL, + NN_EEPROM_SETLABEL, +} nn_EEPROMAction; + +typedef struct nn_EEPROMRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_EEPROM *eeprom; + nn_EEPROMAction action; + union { + char *buf; + const char *robuf; + bool readonly; + }; + size_t buflen; +} nn_EEPROMRequest; + +typedef nn_Exit (nn_EEPROMHandler)(nn_EEPROMRequest *request); + +// Tier 1 - The normal EEPROM equivalent +// Tier 2 - A better EEPROM +// Tier 3 - An even better EEPROM +// Tier 4- The best EEPROM +extern const nn_EEPROM nn_defaultEEPROMs[4]; + +nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler); + +// Filesystem class + +typedef struct nn_Filesystem { + // the maximum capacity of the filesystem + size_t spaceTotal; + // how many read calls can be done per tick + // seek also count as reads. + double readsPerTick; + // how many write calls can be done per tick + double writesPerTick; + // how many open calls can be done per tick + double opensPerTick; + // 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; + // maximum size of a read. + // Do note that this entire buffer is allocated, and thus if you + // set it to a high number, you may get weird high allocations. + // This also determines read performance. + size_t maxReadSize; +} nn_Filesystem; + +typedef enum nn_FSAction { + NN_FS_DROP, + + // drive metadata + NN_FS_SPACEUSED, + NN_FS_GETLABEL, + NN_FS_SETLABEL, + NN_FS_ISRO, + + // for file I/O + NN_FS_OPEN, + NN_FS_CLOSE, + NN_FS_READ, + NN_FS_WRITE, + NN_FS_SEEK, + + // for list + NN_FS_OPENDIR, + NN_FS_READDIR, + NN_FS_CLOSEDIR, + + // checking metadata + NN_FS_STAT, + // make directory, recursively + NN_FS_MKDIR, + + // rename, if renamed to NULL then remove + NN_FS_RENAME, +} nn_FSAction; + +typedef enum nn_FSWhence { + NN_SEEK_SET, + NN_SEEK_CUR, + NN_SEEK_END, +} nn_FSWhence; + +typedef struct nn_FSRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Filesystem *fs; + nn_FSAction action; + int fd; + union { + struct { + const char *path; + const char *mode; + } open; + struct { + char *buf; + size_t len; + } read; + struct { + const char *buf; + size_t len; + } write; + struct { + nn_FSWhence whence; + // set to new offset + int off; + } seek; + const char *opendir; + struct { + // directory path, as a reminder if need be + const char *dirpath; + char *buf; + // set to length of entry name + size_t len; + } readdir; + struct { + // set to NULL if missing + const char *path; + // whether it is a directory + bool isDirectory; + // in seconds. Result will be multiplied by 1000. + // This is because OpenOS code is garbage. + intptr_t lastModified; + // size. 0 for directories. + size_t size; + } stat; + struct { + const char *from; + // if NULL, delete from, recursively. + const char *to; + } rename; + const char *mkdir; + struct { + const char *buf; + size_t len; + } setlabel; + struct { + char *buf; + size_t len; + } getlabel; + bool isReadonly; + size_t spaceUsed; + }; +} nn_FSRequest; + +typedef nn_Exit (nn_FSHandler)(nn_FSRequest *request); + +// 4 Tiers. +// 0 - Tier 1 equivalent +// 1 - Tier 2 equivalent +// 2 - Tier 3 equivalent +// 3 - Tier 4, a better version of Tier 3. +extern const nn_Filesystem nn_defaultFilesystems[4]; +// a basic floppy +extern const nn_Filesystem nn_defaultFloppy; +// a generic tmpfs +extern const nn_Filesystem nn_defaultTmpFS; + +nn_Component *nn_createFilesystem(nn_Universe *universe, const char *address, const nn_Filesystem *fs, void *state, nn_FSHandler *handler); + +bool nn_mergeFilesystems(nn_Filesystem *merged, const nn_Filesystem *fs, size_t len); + +// Drive class + +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. + // Anything that kicks out the current cacheline counts as a 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 (may be eaten by cache) 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 with infinite lifespan. + // 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. + // It is aligned to the cache lines. + 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; + +extern const nn_Drive nn_defaultDrives[4]; +extern const nn_Drive nn_floppyDrive; + + +typedef enum nn_DriveAction { + // drive gone + NN_DRIVE_DROP, + // get current label + NN_DRIVE_GETLABEL, + // set or remove current label + NN_DRIVE_SETLABEL, + // get the current head position, as a sector + NN_DRIVE_CURPOS, + // read a sector + NN_DRIVE_READSECTOR, + // write a sector + NN_DRIVE_WRITESECTOR, + // write a byte + NN_DRIVE_WRITEBYTE, + // is drive read-only + NN_DRIVE_ISRO, +} nn_DriveAction; + +typedef struct nn_DriveRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Drive *drv; + nn_DriveAction action; + union { + struct { + char *buf; + size_t len; + } getlabel; + struct { + const char *label; + size_t len; + } setlabel; + size_t curpos; + struct { + // 1-indexed + size_t sector; + char *buf; + } readSector; + struct { + // 1-indexed + size_t sector; + const char *buf; + } writeSector; + struct { + // 1-indexed + size_t byte; + unsigned char value; + } writeByte; + bool readonly; + }; +} nn_DriveRequest; + +typedef nn_Exit (nn_DriveHandler)(nn_DriveRequest *request); + +nn_Component *nn_createDrive(nn_Universe *universe, const char *address, const nn_Drive *drive, void *state, nn_DriveHandler *handler); + +bool nn_mergeDrives(nn_Drive *merged, const nn_Drive *drives, size_t len); + +typedef struct nn_NandFlash { + // capacity of flash + size_t capacity; + // sector size + size_t sectorSize; + // reads per tick + size_t readsPerTick; + // writes per tick + size_t writesPerTick; + // The layering, in bits. + // 1 is SLC, 2 is MLC, 3 is TLC, etc. + // This number may amplify how quickly the total write count increases. + size_t cellLevel; + // the maximum amount of write amplification. + // Set to 0 to disable amplification RNG. + // The game will generate, using Context RNG, a real number from [0, 1] + // then raise it to writeAmplificationExponent, + // then multiply it by this number, and by the cell level. + // then clamp it to be at least 1 and at most this maximum. + unsigned int maxWriteAmplification; + int writeAmplificationExponent; + // the maximum amount of writes *per sector.* + // Set to 0 to make the nandflash eternal. + size_t maxWriteCount; + // how much per byte + double dataEnergyCost; +} nn_NandFlash; + +typedef enum nn_FlashAction { + NN_FLASH_DROP, + NN_FLASH_GETLABEL, + NN_FLASH_SETLABEL, + NN_FLASH_ISRO, + // read a sector + NN_FLASH_READSECTOR, + // write a sector + // also adds an amount of writes + NN_FLASH_WRITESECTOR, + // write a sector + // also adds an amount of writes + NN_FLASH_WRITEBYTE, + // get the amount of writes + NN_FLASH_GETWRITES, +} nn_FlashAction; + +typedef struct nn_FlashRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_NandFlash *flash; + nn_FlashAction action; + union { + struct { + char *buf; + size_t len; + } getlabel; + struct { + const char *buf; + size_t len; + } setlabel; + struct { + char *buf; + // 1-indexed + size_t sec; + } readsector; + struct { + const char *buf; + // 1-indexed + size_t sec; + // how many writes to add + size_t writesAdded; + } writesector; + struct { + size_t byte; + char val; + // how many writes to add + size_t writesAdded; + } writebyte; + // for GETWRITES + size_t writeCount; + bool readonly; + }; +} nn_FlashRequest; + +typedef nn_Exit (nn_FlashHandler)(nn_FlashRequest *request); + +extern const nn_NandFlash nn_defaultSSDs[4]; +extern const nn_NandFlash nn_floppySSD; + +nn_Component *nn_createFlash(nn_Universe *universe, const char *address, const nn_NandFlash *drive, void *state, nn_FlashHandler *handler); + +bool nn_mergeFlash(nn_NandFlash *merged, const nn_NandFlash *flash, size_t len); + +// Screen class + +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 { + // maximum width + int maxWidth; + // maximum height + int maxHeight; + // screen features + nn_ScreenFeatures features; + // default palette, if applicable. + // Can be NULL if there is none, + // in which case consider memsetting + // them to #000000. + int *defaultPalette; + // the amount of editable palette colors + int paletteColors; + // how many editable palette colors there are. + // It'd always be the first N ones. + int editableColors; + // the maximum depth of the screen + char maxDepth; + // energy per fully white pixel. + // Scaled to mathc luminance of each pixel. + // This is meant to be per Minecraft tick, so 20 times per second. + double energyPerPixel; + // minimum brightness. Default brightness is always 100% + // The value here is meant to be scaled such that 1 means 100% brightness + double minBrightness; + // maximum brightness. Note that brightness rendering is emulator-specific + // The value here is meant to be scaled such that 1 means 100% brightness + double maxBrightness; +} nn_ScreenConfig; + +// OC has 3 tiers, NN adds a 4th one as well. +extern const nn_ScreenConfig nn_defaultScreens[4]; + + +// GPU class +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; + +// 1 GPU tier for every screen. +extern const nn_GPU nn_defaultGPUs[4]; + +typedef enum nn_GPUAction { + NN_GPU_DROP, + NN_GPU_BIND, + NN_GPU_GETSCREEN, + NN_GPU_GETBG, + NN_GPU_SETBG, + NN_GPU_GETFG, + NN_GPU_SETFG, + NN_GPU_GETPALETTE, + NN_GPU_SETPALETTE, + NN_GPU_MAXDEPTH, + NN_GPU_GETDEPTH, + NN_GPU_SETDEPTH, + NN_GPU_MAXRES, + NN_GPU_GETRES, + NN_GPU_SETRES, + NN_GPU_GETVIEWPORT, + NN_GPU_SETVIEWPORT, + NN_GPU_GET, + NN_GPU_SET, + NN_GPU_COPY, + NN_GPU_FILL, + NN_GPU_GETACTIVEBUF, + NN_GPU_SETACTIVEBUF, + NN_GPU_BUFFERS, + NN_GPU_ALLOCBUF, + NN_GPU_FREEBUF, + NN_GPU_FREEALLBUFS, + NN_GPU_FREEMEM, + NN_GPU_GETBUFSIZE, + NN_GPU_BITBLT, +} nn_GPUAction; + +typedef struct nn_GPURequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_GPU *gpu; + nn_GPUAction action; + union { + struct { + const char *address; + bool reset; + } bind; + // GETSCREEN result + char screenAddr[NN_MAX_ADDRESS]; + // GET/SET BG/FG + struct { + int color; + bool isPalette; + int oldColor; + bool wasPalette; + int oldPaletteIdx; // -1 if none + } color; + // GET/SET PALETTE + struct { + int index; + int color; + int oldColor; + } palette; + // MAXDEPTH / GETDEPTH / SETDEPTH + struct { + char depth; + char oldDepth; + } depth; + // MAXRES/GETRES/SETRES/GETVIEWPORT/SETVIEWPORT + struct { + int width; + int height; + } resolution; + // GET pixel + struct { + int x, y; + nn_codepoint codepoint; + int fg, bg; + int fgIdx, bgIdx; // -1 if not palette + } get; + // SET string + struct { + int x, y; + const char *value; + size_t len; + bool vertical; + } set; + // COPY + struct { + int x, y, w, h, tx, ty; + } copy; + // FILL + struct { + int x, y, w, h; + nn_codepoint codepoint; + } fill; + // GET/SET ACTIVE BUFFER, FREE BUFFER + struct { + int index; + } buffer; + // ALLOCATE BUFFER + struct { + int w, h, index; + } allocBuf; + // TOTALMEM / FREEMEM + size_t memory; + // GETBUFSIZE + struct { + int index, w, h; + } bufSize; + // BITBLT + struct { + int dst, col, row, w, h; + int src, fromCol, fromRow; + } bitblt; + // BUFFERS / count returned here, indices + // pushed on stack by handler + size_t bufCount; + }; +} nn_GPURequest; + +typedef nn_Exit (nn_GPUHandler)(nn_GPURequest *req); + +nn_Component *nn_createGPU( + nn_Universe *universe, const char *address, + const nn_GPU *gpu, void *state, + nn_GPUHandler *handler); + +typedef enum nn_ScreenAction { + NN_SCREEN_DROP, + NN_SCREEN_ISON, + NN_SCREEN_TURNON, + NN_SCREEN_TURNOFF, + NN_SCREEN_GETASPECTRATIO, + NN_SCREEN_GETKEYBOARDS, + NN_SCREEN_SETPRECISE, + NN_SCREEN_ISPRECISE, + NN_SCREEN_SETTOUCHINVERTED, + NN_SCREEN_ISTOUCHINVERTED, + // sets the brightness (from 0 to 1) + NN_SCREEN_SETBRIGHT, + // get the brightness (from 0 to 1) + NN_SCREEN_GETBRIGHT, +} nn_ScreenAction; + +typedef struct nn_ScreenRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_ScreenConfig *screen; + nn_ScreenAction action; + union { + // turnOn / turnOff / isOn + struct { + bool wasOn; + bool isOn; + } power; + // getAspectRatio + struct { + int w, h; + } aspect; + // getKeyboards — addresses pushed on stack by + // handler; count returned here + size_t kbCount; + // setPrecise / isPrecise / + // setTouchModeInverted / isTouchModeInverted + bool flag; + double brightness; + }; +} nn_ScreenRequest; + +typedef nn_Exit (nn_ScreenHandler)(nn_ScreenRequest *req); + +nn_Component *nn_createScreen( + nn_Universe *universe, const char *address, + const nn_ScreenConfig *scrconf, void *state, + nn_ScreenHandler *handler +); + +typedef struct nn_Modem { + // maximum range. Set to 0 for non-wireless modems + size_t maxRange; + // maximum values in a packet + size_t maxValues; + // maximum logical packet size. Note that the encoding is more efficient than the packet size algorithm estimates + size_t maxPacketSize; + // the maximum amount of open ports + size_t maxOpenPorts; + // whether the modem supports wired connectivity. + // Support for wireless checks if maxRange > 0. + bool isWired; + // base energy cost of 1 network message + double basePacketCost; + // energy cost of a full packet, at the maximum logical size + double fullPacketCost; + // energy cost per wireless packet strength level + double costPerStrength; +} nn_Modem; + +// A buffer with encoded values +typedef struct nn_EncodedNetworkContents { + nn_Context *ctx; + char *buf; + size_t buflen; + size_t valueCount; +} nn_EncodedNetworkContents; + +extern nn_Modem nn_defaultWiredModem; +extern nn_Modem nn_defaultWirelessModems[2]; + +typedef enum nn_ModemAction { + // modem dropped + NN_MODEM_DROP, + // check whether a port is open + NN_MODEM_ISOPEN, + // open a port + NN_MODEM_OPEN, + // close a port + NN_MODEM_CLOSE, + // get open ports + NN_MODEM_GETPORTS, + // send/broadcast a message + NN_MODEM_SEND, + // get current modem strength + NN_MODEM_GETSTRENGTH, + // set current modem strength + NN_MODEM_SETSTRENGTH, + // returns the wake message + NN_MODEM_GETWAKEMESSAGE, + // set the wake message + NN_MODEM_SETWAKEMESSAGE, +} nn_ModemAction; + +typedef struct nn_ModemRequest { + nn_Context *ctx; + nn_Computer *computer; + void *state; + const nn_Modem *modem; + const char *localAddress; + nn_ModemAction action; + union { + struct { + size_t port; + bool opened; + } isOpen; + size_t openPort; + // NN_CLOSEPORTS means close all + size_t closePort; + struct { + // store the port numbers in this buffer + size_t *activePorts; + // the amount of active ports. + // the initial value is the capacity of activePorts + size_t len; + } getPorts; + struct { + const nn_EncodedNetworkContents *contents; + // NULL for broadcast + const char *address; + size_t port; + } send; + // for getStrength, setStrength. + size_t strength; + struct { + char *buf; + size_t len; + bool isFuzzy; + } getWake; + struct { + const char *buf; + size_t len; + bool isFuzzy; + } setWake; + }; +} nn_ModemRequest; + +typedef nn_Exit (nn_ModemHandler)(nn_ModemRequest *req); + +nn_Component *nn_createModem(nn_Universe *universe, const char *address, const nn_Modem *modem, void *state, nn_ModemHandler *handler); + +typedef struct nn_Tunnel { + // maximum values in a packet + size_t maxValues; + // maximum logical packet size. Note that the encoding is more efficient than the packet size algorithm estimates + size_t maxPacketSize; +} nn_Tunnel; + +extern nn_Tunnel nn_defaultTunnel; + +// 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(); + +// Returns a number from 0 to 1 representing the perceived luminance. +double nn_colorLuminance(int color); +// 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. +// Invalid depths behave identically to 24-bit, in which case the color is left unchanged. +int nn_mapDepth(int color, int depth); + +// 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 + +// the bottom side, can also be downwards / negative y +#define NN_SIDE_BOTTOM 0 +// the top side, can also be upwards / positive y +#define NN_SIDE_TOP 1 +// backwards, can also be north / negative z +#define NN_SIDE_BACK 2 +// forwards, can also be south / positive z +#define NN_SIDE_FRONT 3 +// to the right, can also be west / negative x +#define NN_SIDE_RIGHT 4 +// to the left, can also be east / positive x +#define NN_SIDE_LEFT 5 +// absolutely no clue +#define NN_SIDE_UNKNOWN 6 + +// 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); + +// pushes a redstone_changed signal. +// side is the side of the device the redstone changed on +// oldValue is the old value +// newValue is the new value, must not be equal to oldValue +// color is the color of the redstone signal +// if color < 0, it is set to null +nn_Exit nn_pushRedstoneChanged(nn_Computer *computer, const char *redstoneAddress, int side, int oldValue, int newValue, int color); + +// pushes a motion signal. +// Do note that it is meant to only be sent if the entity has a direct line of sight and if |(relX, relY, relZ)| >= sensitivity. +// This signal does *not* check if the motion sensor actually is sensitive enough to detect it, so make sure to check it yourself. +// relX, relY and relZ are the relative postion in 3D Cartesian space. +// entityName can be NULL if the entity has no name. +nn_Exit nn_pushMotion(nn_Computer *computer, double relX, double relY, double relZ, const char *entityName); + +// applies basic encoding to a network message. This encoding has a header, and thus should remain backwards-compatible. +// The encoding serves 2 purposes: +// 1. Prevent shared memory between computers. Values do not use atomic reference counting, and thus this could lead to UAF or memory leaks. +// 2. Simplify implementing packet queues, which should be used in relays. The lack of access to raw values would force implementers to use +// an encoding anyways. +// This only encodes the contents, not the sender, hops, or other metadata which may be needed in the queue. +// This does not pop the values, in case you need them afterwards. If you don't just call nn_popn(). +// The encoding is architecture-dependent, so be careful with storing it on-disk. +// Do note that the architecture-dependent parts are sizeof(double), sizeof(size_t) and endianness. +// The encoding is simple: +// - 0x00 for null +// - 0x01 for true +// - 0x02 for false +// - 0x03 + for a number +// - 0x04 + + for a string +// - 0x05 + for resource +// - 0x06 + + for a table +nn_Exit nn_encodeNetworkContents(nn_Computer *computer, nn_EncodedNetworkContents *contents, size_t valueCount); +// Allocates a copy of [buf] and stores it in contents. +// This is useful for copying network contents, either from storage or from another buffer. +nn_Exit nn_copyNetworkContents(nn_Context *ctx, nn_EncodedNetworkContents *contents, const char *buf, size_t buflen, size_t valueCount); +void nn_dropNetworkContents(nn_EncodedNetworkContents *contents); +// Pushes the encoded contents onto the stack. +// This does not drop the network contents. +nn_Exit nn_pushNetworkContents(nn_Computer *computer, const nn_EncodedNetworkContents *contents); + +// push a modem_message, can be queued by both modems and tunnels. +// This does not check if the modem has that port open, so make sure to check it yourself. +// It does not check if the distance is within the modem's range, if it is wireless, and thus does not send it. +// Note that relays should change the sender. +nn_Exit nn_pushModemMessage(nn_Computer *computer, const char *modemAddress, const char *sender, int port, double distance, const nn_EncodedNetworkContents *contents); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/native/nn_Context.cpp b/src/native/nn_Context.cpp new file mode 100644 index 0000000..dae6178 --- /dev/null +++ b/src/native/nn_Context.cpp @@ -0,0 +1,11 @@ +#include "carbon.hpp" +#include "org_neoflock_NeoNucleus_Context.h" +#include + +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_Context_free + (JNIEnv * env, jobject obj) { + void* ptr = Carbon::PointerBacked::GetPointer(env, obj); + NULLPTR_CHECKNR(ptr, nn_Context); + free(ptr); + Carbon::PointerBacked::ResetPointer(env, obj); // lets not be having accidental double frees here +} \ No newline at end of file diff --git a/src/native/nn_EEPROM.cpp b/src/native/nn_EEPROM.cpp new file mode 100644 index 0000000..81a00c5 --- /dev/null +++ b/src/native/nn_EEPROM.cpp @@ -0,0 +1,36 @@ +#include "org_neoflock_NeoNucleus_nn_EEPROM.h" +#include +#include +#include "carbon.hpp" +// FIXME: doesn't check for null +#define GET_LONG_FIELD(name) eepromNative->name = (uint64_t) env->GetLongField(eeprom, env->GetFieldID(clazz, #name, "J")); +#define GET_DOUBLE_FIELD(name) eepromNative->name = (double) env->GetDoubleField(eeprom, env->GetFieldID(clazz, #name, "D")); + +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_nn_1EEPROM_allocate + (JNIEnv * env, jobject eeprom) { + jclass clazz = env->GetObjectClass(eeprom); + //jfieldID fid = env->GetFieldID(clazz, "ptr", "J"); + //jlong size = env->GetLongField(eeprom, fid); + nn_EEPROM * eepromNative = (nn_EEPROM*)malloc(sizeof(nn_EEPROM)); + GET_LONG_FIELD(size); + GET_LONG_FIELD(dataSize); + GET_DOUBLE_FIELD(readEnergyCost); + GET_DOUBLE_FIELD(readDataEnergyCost); + GET_DOUBLE_FIELD(writeEnergyCost); + GET_DOUBLE_FIELD(writeDataEnergyCost); + GET_DOUBLE_FIELD(writeDelay); + GET_DOUBLE_FIELD(writeDelay); + //printf("double_size=%d, %d %d %lf %lf %lf\n", sizeof(double), eepromNative->size, eepromNative->dataSize, eepromNative->readEnergyCost, eepromNative->readDataEnergyCost, eepromNative->writeEnergyCost); + return Carbon::PointerBacked::SetPointer(env, eeprom, eepromNative); +} +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_nn_1EEPROM_free + (JNIEnv * env, jobject eeprom) { + if (Carbon::PointerBacked::GetPointer(env, eeprom) == NULL) { + NULLPTR_THROW(nn_EEPROM); + return false; + } + // assuming free(NULL) does nothing, this should be fine + free(Carbon::PointerBacked::GetPointer(env, eeprom)); + Carbon::PointerBacked::ResetPointer(env, eeprom); + return true; +} \ No newline at end of file diff --git a/src/native/org_neoflock_NeoNucleus_Context.h b/src/native/org_neoflock_NeoNucleus_Context.h new file mode 100644 index 0000000..2a4d726 --- /dev/null +++ b/src/native/org_neoflock_NeoNucleus_Context.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_neoflock_NeoNucleus_Context */ + +#ifndef _Included_org_neoflock_NeoNucleus_Context +#define _Included_org_neoflock_NeoNucleus_Context +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_neoflock_NeoNucleus_Context + * Method: free + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_Context_free + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/native/org_neoflock_NeoNucleus_NativeBindings.h b/src/native/org_neoflock_NeoNucleus_NativeBindings.h new file mode 100644 index 0000000..bbf9d54 --- /dev/null +++ b/src/native/org_neoflock_NeoNucleus_NativeBindings.h @@ -0,0 +1,405 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_neoflock_NeoNucleus_NativeBindings */ + +#ifndef _Included_org_neoflock_NeoNucleus_NativeBindings +#define _Included_org_neoflock_NeoNucleus_NativeBindings +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_initContext + * Signature: ()Lorg/neoflock/NeoNucleus/nn_Context; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1initContext + (JNIEnv *, jclass); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_initPalettes + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1initPalettes + (JNIEnv *, jclass); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_createUniverse + * Signature: (Lorg/neoflock/NeoNucleus/nn_Context;)Lorg/neoflock/NeoNucleus/nn_Universe; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1createUniverse + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_createComponent + * Signature: (Lorg/neoflock/NeoNucleus/nn_Universe;Ljava/lang/String;Ljava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Component; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1createComponent + (JNIEnv *, jclass, jobject, jstring, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setComponentMethods + * Signature: (Lorg/neoflock/NeoNucleus/nn_Component;[Lorg/neoflock/NeoNucleus/nn_Method;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setComponentMethods + (JNIEnv *, jclass, jobject, jobjectArray); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setComponentHandler + * Signature: (Lorg/neoflock/NeoNucleus/nn_Component;Lorg/neoflock/NeoNucleus/ComponentHandler;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setComponentHandler + (JNIEnv *, jclass, jobject, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_mountComponent + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Lorg/neoflock/NeoNucleus/nn_Component;IZ)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1mountComponent + (JNIEnv *, jclass, jobject, jobject, jint, jboolean); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_createEEPROM + * Signature: (Lorg/neoflock/NeoNucleus/nn_Universe;Ljava/lang/String;Lorg/neoflock/NeoNucleus/nn_EEPROM;Ljava/lang/String;Z)Lorg/neoflock/NeoNucleus/nn_Component; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1createEEPROM + (JNIEnv *, jclass, jobject, jstring, jobject, jstring, jboolean); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_createComputer + * Signature: (Lorg/neoflock/NeoNucleus/nn_Universe;Ljava/lang/String;JJJ)Lorg/neoflock/NeoNucleus/nn_Computer; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1createComputer + (JNIEnv *, jclass, jobject, jstring, jlong, jlong, jlong); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setComputerEnvironment + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Lorg/neoflock/NeoNucleus/nn_Environment;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setComputerEnvironment + (JNIEnv *, jclass, jobject, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setCallBudget + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;D)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setCallBudget + (JNIEnv *, jclass, jobject, jdouble); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setTotalEnergy + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;D)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setTotalEnergy + (JNIEnv *, jclass, jobject, jdouble); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setArchitecture + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Lorg/neoflock/NeoNucleus/nn_Architecture;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setArchitecture + (JNIEnv *, jclass, jobject, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_addSupportedArchitecture + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Lorg/neoflock/NeoNucleus/nn_Architecture;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1addSupportedArchitecture + (JNIEnv *, jclass, jobject, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_setTmpAddress + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1setTmpAddress + (JNIEnv *, jclass, jobject, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getComponentAddress + * Signature: (Lorg/neoflock/NeoNucleus/nn_Component;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getComponentAddress + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_addCommonDeviceInfo + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;Lorg/neoflock/NeoNucleus/nn_CommonDeviceInfo;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1addCommonDeviceInfo + (JNIEnv *, jclass, jobject, jstring, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getComponentState + * Signature: (Lorg/neoflock/NeoNucleus/nn_Component;)Lorg/neoflock/NeoNucleus/ComponentState; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getComponentState + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_lockScreen + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1lockScreen + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_getScreenViewport + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)Lorg/neoflock/NeoNucleus/Resolution; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1getScreenViewport + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_setScreenViewport + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;JJ)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1setScreenViewport + (JNIEnv *, jclass, jobject, jlong, jlong); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_getScreenFlags + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)Lorg/neoflock/NeoNucleus/ncl_ScreenFlags; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1getScreenFlags + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_getScreenBrightness + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)D + */ +JNIEXPORT jdouble JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1getScreenBrightness + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_getScreenPixel + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;II)Lorg/neoflock/NeoNucleus/ncl_Pixel; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1getScreenPixel + (JNIEnv *, jclass, jobject, jint, jint); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_unlockScreen + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1unlockScreen + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushTouch + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;DDILjava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushTouch + (JNIEnv *, jclass, jobject, jstring, jdouble, jdouble, jint, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushDrop + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;DDILjava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushDrop + (JNIEnv *, jclass, jobject, jstring, jdouble, jdouble, jint, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushDrag + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;DDILjava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushDrag + (JNIEnv *, jclass, jobject, jstring, jdouble, jdouble, jint, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushScroll + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;DDDLjava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushScroll + (JNIEnv *, jclass, jobject, jstring, jdouble, jdouble, jdouble, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getUsedMemory + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)J + */ +JNIEXPORT jlong JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getUsedMemory + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getTotalMemory + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)J + */ +JNIEXPORT jlong JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getTotalMemory + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushClipboard + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushClipboard + (JNIEnv *, jclass, jobject, jstring, jstring, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushKeyDown + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;IILjava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushKeyDown + (JNIEnv *, jclass, jobject, jstring, jint, jint, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_pushKeyUp + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;Ljava/lang/String;IILjava/lang/String;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1pushKeyUp + (JNIEnv *, jclass, jobject, jstring, jint, jint, jstring); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_clearstack + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1clearstack + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_removeEnergy + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;D)Z + */ +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1removeEnergy + (JNIEnv *, jclass, jobject, jdouble); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_tickSynchronized + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1tickSynchronized + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_getScreenEnergyUsage + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)D + */ +JNIEXPORT jdouble JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1getScreenEnergyUsage + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_resetIdleTime + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1resetIdleTime + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_tick + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)Lorg/neoflock/NeoNucleus/nn_Exit; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1tick + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getComputerState + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)Lorg/neoflock/NeoNucleus/nn_ComputerState; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getComputerState + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getError + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)Ljava/lang/String; + */ +JNIEXPORT jstring JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getError + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_getDesiredArchitecture + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)Lorg/neoflock/NeoNucleus/nn_Architecture; + */ +JNIEXPORT jobject JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1getDesiredArchitecture + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_stopComputer + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1stopComputer + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: ncl_resetScreen + * Signature: (Lorg/neoflock/NeoNucleus/ncl_ScreenState;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_ncl_1resetScreen + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_addIdleTime + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;D)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1addIdleTime + (JNIEnv *, jclass, jobject, jdouble); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_destroyComputer + * Signature: (Lorg/neoflock/NeoNucleus/nn_Computer;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1destroyComputer + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_dropComponent + * Signature: (Lorg/neoflock/NeoNucleus/nn_Component;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1dropComponent + (JNIEnv *, jclass, jobject); + +/* + * Class: org_neoflock_NeoNucleus_NativeBindings + * Method: nn_destroyUniverse + * Signature: (Lorg/neoflock/NeoNucleus/nn_Universe;)V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_NativeBindings_nn_1destroyUniverse + (JNIEnv *, jclass, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/native/org_neoflock_NeoNucleus_nn_Architecture.h b/src/native/org_neoflock_NeoNucleus_nn_Architecture.h new file mode 100644 index 0000000..294f514 --- /dev/null +++ b/src/native/org_neoflock_NeoNucleus_nn_Architecture.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_neoflock_NeoNucleus_nn_Architecture */ + +#ifndef _Included_org_neoflock_NeoNucleus_nn_Architecture +#define _Included_org_neoflock_NeoNucleus_nn_Architecture +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_neoflock_NeoNucleus_nn_Architecture + * Method: allocate + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_nn_1Architecture_allocate + (JNIEnv *, jobject); + +/* + * Class: org_neoflock_NeoNucleus_nn_Architecture + * Method: free + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_nn_1Architecture_free + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/native/org_neoflock_NeoNucleus_nn_Context.h b/src/native/org_neoflock_NeoNucleus_nn_Context.h new file mode 100644 index 0000000..f72236a --- /dev/null +++ b/src/native/org_neoflock_NeoNucleus_nn_Context.h @@ -0,0 +1,21 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_neoflock_NeoNucleus_nn_Context */ + +#ifndef _Included_org_neoflock_NeoNucleus_nn_Context +#define _Included_org_neoflock_NeoNucleus_nn_Context +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_neoflock_NeoNucleus_nn_Context + * Method: free + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_neoflock_NeoNucleus_nn_1Context_free + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/native/org_neoflock_NeoNucleus_nn_EEPROM.h b/src/native/org_neoflock_NeoNucleus_nn_EEPROM.h new file mode 100644 index 0000000..bfa6bf6 --- /dev/null +++ b/src/native/org_neoflock_NeoNucleus_nn_EEPROM.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class org_neoflock_NeoNucleus_nn_EEPROM */ + +#ifndef _Included_org_neoflock_NeoNucleus_nn_EEPROM +#define _Included_org_neoflock_NeoNucleus_nn_EEPROM +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_neoflock_NeoNucleus_nn_EEPROM + * Method: allocate + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_nn_1EEPROM_allocate + (JNIEnv *, jobject); + +/* + * Class: org_neoflock_NeoNucleus_nn_EEPROM + * Method: free + * Signature: ()Z + */ +JNIEXPORT jboolean JNICALL Java_org_neoflock_NeoNucleus_nn_1EEPROM_free + (JNIEnv *, jobject); + +#ifdef __cplusplus +} +#endif +#endif