#ifndef NEONUCLEUS_H #define NEONUCLEUS_H #ifndef NULL #ifdef __cplusplus #define NULL nullptr #else #define NULL ((void *)0) #endif #endif #ifdef NN_BAREMETAL #ifdef NN_BIT32 typedef int nn_intptr_t; typedef unsigned int nn_size_t; #else typedef __INTPTR_TYPE__ nn_intptr_t; typedef __SIZE_TYPE__ nn_size_t; #endif #else #include #include #include typedef intptr_t nn_intptr_t; typedef size_t nn_size_t; #endif #ifdef bool typedef bool nn_bool_t; #else typedef unsigned char nn_bool_t; #define bool nn_bool_t #endif #ifdef true #define NN_TRUE true #else #define NN_TRUE 1 #define true NN_TRUE #endif #ifdef false #define NN_FALSE false #else #define NN_FALSE 0 #define false NN_FALSE #endif typedef long long nn_integer_t; // Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) //define something for Windows (32-bit and 64-bit, this part is common) #ifdef _WIN64 #define NN_WINDOWS #else #error "Windows 32-bit is not supported" #endif #elif __APPLE__ #include #if TARGET_IPHONE_SIMULATOR #error "iPhone Emulators are not supported" #elif TARGET_OS_MACCATALYST // I guess? #define NN_MACOS #elif TARGET_OS_IPHONE #error "iPhone are not supported" #elif TARGET_OS_MAC #define NN_MACOS #else #error "Unknown Apple platform" #endif #elif __ANDROID__ #error "Android is not supported" #elif __linux__ #define NN_LINUX #endif #if __unix__ // all unices not caught above // Unix #define NN_UNIX #define NN_POSIX #elif defined(_POSIX_VERSION) // POSIX #define NN_POSIX #endif #ifdef __cplusplus extern "C" { #endif // The entire C API, in one header // Magic limits // If your component needs more than these, redesign your API. #define NN_MAX_ARGS 32 #define NN_MAX_RETS 32 #define NN_MAX_METHODS 32 #define NN_MAX_USERS 128 #define NN_MAX_ARCHITECTURES 16 #define NN_MAX_SIGNALS 128 #define NN_MAX_SIGNAL_VALS 32 #define NN_MAX_USERDATA 1024 #define NN_MAX_USER_SIZE 128 #define NN_MAX_SIGNAL_SIZE 8192 #define NN_MAX_OPEN_FILES 128 #define NN_MAX_SCREEN_KEYBOARDS 64 #define NN_MAX_PATH 256 #define NN_PORT_MAX 65535 #define NN_MAX_WAKEUPMSG 2048 #define NN_MAX_CHANNEL_SIZE 256 #define NN_TUNNEL_PORT 0 #define NN_PORT_CLOSEALL 0 #define NN_MAX_CONCURRENT_RESOURCES 64 #define NN_NULL_RESOURCE 0 #define NN_OVERHEAT_MIN 100 #define NN_CALL_HEAT 0.05 #define NN_CALL_COST 1 #define NN_LABEL_SIZE 128 #define NN_MAXIMUM_UNICODE_BUFFER 4 #define NN_MAX_ERROR_BUFFER 128 typedef struct nn_guard nn_guard; #ifdef __STDC_NO_ATOMICS__ typedef size_t nn_refc; #else typedef _Atomic(nn_size_t) nn_refc; #endif typedef struct nn_universe nn_universe; typedef struct nn_computer nn_computer; typedef struct nn_component nn_component; typedef struct nn_componentTable nn_componentTable; typedef unsigned long long nn_timestamp_t; // A non-zero malloc is a null ptr, with a 0 oldSize, but a non-0 newSize. // A zero malloc is never called, the proc address itself is returned, which is ignored when freeing. // A free is a non-null ptr, with a non-zero oldSize, but a newSize of 0. // A realloc is a non-null ptr, with a non-zero oldSize, and a non-zero newSize. typedef void *nn_AllocProc(void *userdata, void *ptr, nn_size_t oldSize, nn_size_t newSize, void *extra); typedef struct nn_Alloc { void *userdata; nn_AllocProc *proc; } nn_Alloc; typedef struct nn_architecture { void *userdata; const char *archName; void *(*setup)(nn_computer *computer, void *userdata); void (*teardown)(nn_computer *computer, void *state, void *userdata); nn_size_t (*getMemoryUsage)(nn_computer *computer, void *state, void *userdata); void (*tick)(nn_computer *computer, void *state, void *userdata); /* Pointer returned should be allocated with nn_malloc or nn_realloc, so it can be freed with nn_free */ char *(*serialize)(nn_computer *computer, nn_Alloc *alloc, void *state, void *userdata, nn_size_t *len); void (*deserialize)(nn_computer *computer, const char *data, nn_size_t len, void *state, void *userdata); } nn_architecture; typedef char *nn_address; #define NN_LOCK_DEFAULT 0 #define NN_LOCK_IMMEDIATE 1 #define NN_LOCK_INIT 0 #define NN_LOCK_DEINIT 1 #define NN_LOCK_RETAIN 2 #define NN_LOCK_RELEASE 3 typedef nn_bool_t nn_LockProc(void *userdata, void *lock, int action, int flags); typedef struct nn_LockManager { void *userdata; nn_size_t lockSize; nn_LockProc *proc; } nn_LockManager; typedef double nn_ClockProc(void *userdata); typedef struct nn_Clock { void *userdata; nn_ClockProc *proc; } nn_Clock; typedef nn_size_t nn_RngProc(void *userdata); typedef struct nn_Rng { void *userdata; nn_size_t maximum; nn_RngProc *proc; } nn_Rng; nn_size_t nn_rand(nn_Rng *rng); // returns from 0 to 1 (inclusive) double nn_randf(nn_Rng *rng); // returns from 0 to 1 (exclusive) double nn_randfe(nn_Rng *rng); typedef struct nn_Context { nn_Alloc allocator; nn_LockManager lockManager; nn_Clock clock; nn_Rng rng; } nn_Context; // libc-like utils void nn_memset(void *buf, unsigned char byte, nn_size_t len); void nn_memcpy(void *dest, const void *src, nn_size_t len); char *nn_strcpy(char *dest, const char *src); const char *nn_strchr(const char *str, int ch); int nn_strcmp(const char *a, const char *b); nn_size_t nn_strlen(const char *a); nn_bool_t nn_strbegin(const char *s, const char *prefix); #ifndef NN_BAREMETAL nn_Alloc nn_libcAllocator(void); nn_Clock nn_libcRealTime(void); nn_LockManager nn_libcMutex(void); nn_Rng nn_libcRng(void); nn_Context nn_libcContext(void); #endif nn_LockManager nn_noMutex(void); // Error buffers!!! typedef char nn_errorbuf_t[NN_MAX_ERROR_BUFFER]; nn_bool_t nn_error_isEmpty(nn_errorbuf_t buf); void nn_error_write(nn_errorbuf_t buf, const char *s); void nn_error_clear(nn_errorbuf_t buf); // Values for architectures #define NN_VALUE_INT 0 #define NN_VALUE_NUMBER 1 #define NN_VALUE_BOOL 2 #define NN_VALUE_CSTR 3 #define NN_VALUE_STR 4 #define NN_VALUE_ARRAY 5 #define NN_VALUE_TABLE 6 #define NN_VALUE_NIL 7 #define NN_VALUE_RESOURCE 8 typedef struct nn_string { char *data; nn_size_t len; nn_size_t refc; nn_Alloc alloc; } nn_string; typedef struct nn_array { struct nn_value *values; nn_size_t len; nn_size_t refc; nn_Alloc alloc; } nn_array; typedef struct nn_object { struct nn_pair *pairs; nn_size_t len; nn_size_t refc; nn_Alloc alloc; } nn_table; typedef struct nn_value { nn_size_t tag; union { nn_integer_t integer; double number; nn_bool_t boolean; const char *cstring; nn_string *string; nn_array *array; nn_table *table; nn_size_t resourceID; }; } nn_value; typedef struct nn_pair { nn_value key; nn_value val; } nn_pair; // we expose the allocator because of some utilities void *nn_alloc(nn_Alloc *alloc, nn_size_t size); void *nn_resize(nn_Alloc *alloc, void *memory, nn_size_t oldSize, nn_size_t newSize); void nn_dealloc(nn_Alloc *alloc, void *memory, nn_size_t size); // Utilities, both internal and external char *nn_strdup(nn_Alloc *alloc, const char *s); void *nn_memdup(nn_Alloc *alloc, const void *buf, nn_size_t len); void nn_deallocStr(nn_Alloc *alloc, char *s); nn_address nn_randomUUID(nn_Context *ctx); nn_bool_t nn_path_hasSlash(const char *path); nn_size_t nn_path_firstSlash(const char *path); nn_size_t nn_path_lastSlash(const char *path); // returns whether it is the last name nn_bool_t nn_path_firstName(const char path[NN_MAX_PATH], char firstDirectory[NN_MAX_PATH], char subpath[NN_MAX_PATH]); // returns whether it is the only name nn_bool_t nn_path_lastName(const char path[NN_MAX_PATH], char name[NN_MAX_PATH], char parent[NN_MAX_PATH]); // returns whether the path is valid nn_bool_t nn_path_isValid(const char *path); // writes to canonical the standard form of the path // returns whether the path is so horribly bad it cannot be converted in canonical form. nn_bool_t nn_path_canonical(const char path[NN_MAX_PATH], char canonical[NN_MAX_PATH]); nn_guard *nn_newGuard(nn_Context *context); void nn_lock(nn_Context *context, nn_guard *guard); nn_bool_t nn_tryLock(nn_Context *context, nn_guard *guard); void nn_unlock(nn_Context *context, nn_guard *guard); void nn_deleteGuard(nn_Context *context, nn_guard *guard); void nn_addRef(nn_refc *refc, nn_size_t count); void nn_incRef(nn_refc *refc); /* Returns true if the object should be freed */ nn_bool_t nn_removeRef(nn_refc *refc, nn_size_t count); /* Returns true if the object should be freed */ nn_bool_t nn_decRef(nn_refc *refc); // Unicode (more specifically, UTF-8) stuff nn_bool_t nn_unicode_validate(const char *s); // expects NULL terminator nn_bool_t nn_unicode_isValidCodepoint(const char *s); // returned string must be nn_deallocStr()'d char *nn_unicode_char(nn_Alloc *alloc, unsigned int *codepoints, nn_size_t codepointCount); // returned array must be nn_dealloc()'d unsigned int *nn_unicode_codepoints(nn_Alloc *alloc, const char *s, nn_size_t *len); nn_size_t nn_unicode_len(const char *s); unsigned int nn_unicode_codepointAt(const char *s, nn_size_t byteOffset); nn_size_t nn_unicode_codepointSize(unsigned int codepoint); void nn_unicode_codepointToChar(char buffer[NN_MAXIMUM_UNICODE_BUFFER], unsigned int codepoint, nn_size_t *len); nn_size_t nn_unicode_charWidth(unsigned int codepoint); nn_size_t nn_unicode_wlen(const char *s); unsigned int nn_unicode_upperCodepoint(unsigned int codepoint); // returned string must be nn_deallocStr()'d char *nn_unicode_upper(nn_Alloc *alloc, const char *s); unsigned int nn_unicode_lowerCodepoint(unsigned int codepoint); // returned string must be nn_deallocStr()'d char *nn_unicode_lower(nn_Alloc *alloc, const char *s); // permissive means it allows invalid UTF-8, in which case each byte is treated as a codepoint // it will return the codepoint starting at byte *index, but will also set *index to the byte afterward it // since it is permissive, it supports invalid UTF-8 unsigned int nn_unicode_nextCodepointPermissive(const char *s, nn_size_t *index); nn_size_t nn_unicode_lenPermissive(const char *s); nn_size_t nn_unicode_wlenPermissive(const char *s); // if not found, it will return -1. This is why it is an nn_intptr_t nn_intptr_t nn_unicode_indexPermissive(const char *s, nn_size_t codepointIndex); // Data card stuff // Hashing void nn_data_crc32(const char *inBuf, nn_size_t buflen, char outBuf[4]); void nn_data_md5(const char *inBuf, nn_size_t buflen, char outBuf[16]); void nn_data_sha256(const char *inBuf, nn_size_t buflen, char outBuf[32]); // Base64 // The initial value of *len is the size of buf, with the new value being the length of the returned buffer. char *nn_data_decode64(nn_Alloc *alloc, const char *buf, nn_size_t *len); char *nn_data_encode64(nn_Alloc *alloc, const char *buf, nn_size_t *len); // Deflate/inflate char *nn_data_deflate(nn_Alloc *alloc, const char *buf, nn_size_t *len); char *nn_data_inflate(nn_Alloc *alloc, const char *buf, nn_size_t *len); // AES char *nn_data_aes_encrypt(nn_Alloc *alloc, const char *buf, nn_size_t *len, const char key[16], const char iv[16]); char *nn_data_aes_decrypt(nn_Alloc *alloc, const char *buf, nn_size_t *len, const char key[16], const char iv[16]); // ECDH // if longKeys is on, instead of taking 32 bytes, the keys take up 48 bytes. nn_size_t nn_data_ecdh_keylen(nn_bool_t longKeys); // use nn_data_ecdh_keylen to figure out the expected length for the buffers void nn_data_ecdh_generateKeyPair(nn_Context *context, nn_bool_t longKeys, char *publicKey, char *privateKey); nn_bool_t nn_data_ecdsa_check(nn_bool_t longKeys, const char *buf, nn_size_t buflen, const char *sig, nn_size_t siglen); char *nn_data_ecdsa_sign(nn_Alloc *alloc, const char *buf, nn_size_t *buflen, const char *key, nn_bool_t longKeys); char *nn_data_ecdh_getSharedKey(nn_Alloc *alloc, nn_size_t *len, const char *privateKey, const char *publicKey, nn_bool_t longKeys); // ECC char *nn_data_hamming_encode(nn_Alloc *alloc, const char *buf, nn_size_t *len); char *nn_data_hamming_decode(nn_Alloc *alloc, const char *buf, nn_size_t *len); // Universe stuff nn_universe *nn_newUniverse(nn_Context context); nn_Context *nn_getContext(nn_universe *universe); nn_Alloc *nn_getAllocator(nn_universe *universe); nn_Clock *nn_getClock(nn_universe *universe); nn_LockManager *nn_getLockManager(nn_universe *universe); nn_Rng *nn_getRng(nn_universe *universe); void nn_unsafeDeleteUniverse(nn_universe *universe); void *nn_queryUserdata(nn_universe *universe, const char *name); void nn_storeUserdata(nn_universe *universe, const char *name, void *data); double nn_getTime(nn_universe *universe); // Device info typedef struct nn_deviceInfoList_t nn_deviceInfoList_t; typedef struct nn_deviceInfo_t nn_deviceInfo_t; // Common / standard keys #define NN_DEVICEINFO_KEY_CLASS "class" #define NN_DEVICEINFO_KEY_VENDOR "vendor" #define NN_DEVICEINFO_KEY_PRODUCT "product" #define NN_DEVICEINFO_KEY_CAPACITY "capacity" #define NN_DEVICEINFO_KEY_CLOCK "clock" #define NN_DEVICEINFO_KEY_DESCRIPTION "description" // Common / standard values #define NN_DEVICEINFO_CLASS_INPUT "input" #define NN_DEVICEINFO_CLASS_RAM "memory" #define NN_DEVICEINFO_CLASS_ROM "memory" // not a mistake, they both use memory #define NN_DEVICEINFO_CLASS_CPU "processor" // also used by data card #define NN_DEVICEINFO_CLASS_DATA "processor" // also used by data card #define NN_DEVICEINFO_CLASS_GPU "display" // not a mistake, it and screen have the same class #define NN_DEVICEINFO_CLASS_SCREEN "display" #define NN_DEVICEINFO_CLASS_COMPUTER "system" #define NN_DEVICEINFO_CLASS_STORAGE "volume" #define NN_DEVICEINFO_CLASS_INTERNET "communication" #define NN_DEVICEINFO_CLASS_REDSTONE "communication" // why do they use the same one? idfk #define NN_DEVICEINFO_CLASS_MODEM "network" #define NN_DEVICEINFO_CLASS_TUNNEL "network" #define NN_DEVICEINFO_CLASS_GENERIC "generic" nn_deviceInfoList_t *nn_newDeviceInfoList(nn_Context *ctx, nn_size_t preallocate); void nn_deleteDeviceInfoList(nn_deviceInfoList_t *deviceInfoList); nn_deviceInfo_t *nn_addDeviceInfo(nn_deviceInfoList_t *list, nn_address address, nn_size_t maxKeys); void nn_removeDeviceInfo(nn_deviceInfoList_t *list, const char *address); nn_bool_t nn_registerDeviceKey(nn_deviceInfo_t *deviceInfo, const char *key, const char *value); nn_deviceInfo_t *nn_getDeviceInfoAt(nn_deviceInfoList_t *list, nn_size_t idx); nn_size_t nn_getDeviceCount(nn_deviceInfoList_t *list); const char *nn_getDeviceInfoAddress(nn_deviceInfo_t *deviceInfo); const char *nn_iterateDeviceInfoKeys(nn_deviceInfo_t *deviceInfo, nn_size_t idx, const char **value); nn_size_t nn_getDeviceKeyCount(nn_deviceInfo_t *deviceInfo); // Computer running states nn_computer *nn_newComputer(nn_universe *universe, nn_address address, nn_architecture *arch, void *userdata, nn_size_t memoryLimit, nn_size_t componentLimit); nn_universe *nn_getUniverse(nn_computer *computer); int nn_tickComputer(nn_computer *computer); double nn_getUptime(nn_computer *computer); nn_size_t nn_getComputerMemoryUsed(nn_computer *computer); nn_size_t nn_getComputerMemoryTotal(nn_computer *computer); void *nn_getComputerUserData(nn_computer *computer); void nn_addSupportedArchitecture(nn_computer *computer, nn_architecture *arch); nn_architecture *nn_getSupportedArchitecture(nn_computer *computer, nn_size_t idx); nn_architecture *nn_getArchitecture(nn_computer *computer); nn_architecture *nn_getNextArchitecture(nn_computer *computer); void nn_setNextArchitecture(nn_computer *computer, nn_architecture *arch); void nn_deleteComputer(nn_computer *computer); const char *nn_pushSignal(nn_computer *computer, nn_value *values, nn_size_t len); nn_value nn_fetchSignalValue(nn_computer *computer, nn_size_t index); nn_size_t nn_signalSize(nn_computer *computer); void nn_popSignal(nn_computer *computer); const char *nn_addUser(nn_computer *computer, const char *name); void nn_deleteUser(nn_computer *computer, const char *name); const char *nn_indexUser(nn_computer *computer, nn_size_t idx); nn_bool_t nn_isUser(nn_computer *computer, const char *name); void nn_setCallBudget(nn_computer *computer, double callBudget); double nn_getCallBudget(nn_computer *computer); void nn_callCost(nn_computer *computer, double cost); double nn_getCallCost(nn_computer *computer); nn_bool_t nn_isOverworked(nn_computer *computer); void nn_triggerIndirect(nn_computer *computer); nn_deviceInfoList_t *nn_getComputerDeviceInfoList(nn_computer *computer); /* The memory returned can be freed with nn_dealloc() */ char *nn_serializeProgram(nn_computer *computer, nn_Alloc *alloc, nn_size_t *len); void nn_deserializeProgram(nn_computer *computer, const char *memory, nn_size_t len); nn_Context *nn_getComputerContext(nn_computer *computer); nn_guard *nn_getComputerLock(nn_computer *computer); /// This means the computer has not yet started. #define NN_STATE_SETUP 0 /// This means the computer is running. There is no matching off-state, as the computer is /// only off when it is deleted. #define NN_STATE_RUNNING 1 /// This means a component's invocation could not be done due to a crucial resource being busy. /// The sandbox should yield, then *invoke the component method again.* #define NN_STATE_BUSY 2 /// This state occurs when a call to removeEnergy has consumed all the energy left. /// The sandbox should yield, and the runner should shut down the computer. /// No error is set, the sandbox can set it if it wanted to. #define NN_STATE_BLACKOUT 3 /// This state only indicates that the runner should turn off the computer, but not due to a blackout. /// The runner need not bring it back. #define NN_STATE_CLOSING 4 /// This state indicates that the runner should turn off the computer, but not due to a blackout. /// The runner should bring it back. /// By "bring it back", we mean delete the computer, then recreate the entire state. #define NN_STATE_REPEAT 5 /// This state indciates that the runner should turn off the computer, to switch architectures. /// The architecture is returned by getNextArchitecture. #define NN_STATE_SWITCH 6 /// The machine is overworked. #define NN_STATE_OVERWORKED 7 int nn_getState(nn_computer *computer); void nn_setState(nn_computer *computer, int state); void nn_computer_clearBeep(nn_computer *computer); void nn_computer_setBeep(nn_computer *computer, double frequency, double duration, double volume); nn_bool_t nn_computer_getBeep(nn_computer *computer, double *frequency, double *duration, double *volume); void nn_setEnergyInfo(nn_computer *computer, double energy, double capacity); double nn_getEnergy(nn_computer *computer); double nn_getMaxEnergy(nn_computer *computer); void nn_removeEnergy(nn_computer *computer, double energy); void nn_addEnergy(nn_computer *computer, double amount); double nn_getTemperature(nn_computer *computer); double nn_getThermalCoefficient(nn_computer *computer); double nn_getRoomTemperature(nn_computer *computer); void nn_setTemperature(nn_computer *computer, double temperature); void nn_setTemperatureCoefficient(nn_computer *computer, double coefficient); void nn_setRoomTemperature(nn_computer *computer, double roomTemperature); void nn_addHeat(nn_computer *computer, double heat); void nn_removeHeat(nn_computer *computer, double heat); /* Checks against NN_OVERHEAT_MIN */ nn_bool_t nn_isOverheating(nn_computer *computer); // NULL if there is no error. const char *nn_getError(nn_computer *computer); void nn_clearError(nn_computer *computer); void nn_setError(nn_computer *computer, const char *err); // this version does NOT allocate a copy of err, thus err should come from the data // segment or memory with the same lifetime as the computer. This may not be possible // in garbage-collected languages using this API, and thus should be avoided. // This can be used by low-level implementations of architectures such that any // internal out-of-memory errors can be reported. The normal setError would report // no error if allocating the copy failed, and would clear any previous error. void nn_setCError(nn_computer *computer, const char *err); // Component stuff nn_component *nn_newComponent(nn_computer *computer, nn_address address, int slot, nn_componentTable *table, void *userdata); void nn_setTmpAddress(nn_computer *computer, nn_address tmp); nn_address nn_getComputerAddress(nn_computer *computer); nn_address nn_getTmpAddress(nn_computer *computer); void nn_removeComponent(nn_computer *computer, nn_address address); void nn_destroyComponent(nn_component *component); nn_computer *nn_getComputerOfComponent(nn_component *component); nn_address nn_getComponentAddress(nn_component *component); int nn_getComponentSlot(nn_component *component); nn_componentTable *nn_getComponentTable(nn_component *component); const char *nn_getComponentType(nn_componentTable *table); void *nn_getComponentUserdata(nn_component *component); nn_component *nn_findComponent(nn_computer *computer, nn_address address); // the internal index is not the array index, but rather an index into // an internal structure. YOU SHOULD NOT ADD OR REMOVE COMPONENTS WHILE ITERATING. // the internalIndex SHOULD BE INITIALIZED TO 0. // Returns NULL at the end nn_component *nn_iterComponent(nn_computer *computer, nn_size_t *internalIndex); // Component VTable stuff typedef void *nn_componentConstructor(void *tableUserdata, void *componentUserdata); typedef void *nn_componentDestructor(void *tableUserdata, nn_component *component, void *componentUserdata); typedef void nn_componentMethod(void *componentUserdata, void *methodUserdata, nn_component *component, nn_computer *computer); typedef nn_bool_t nn_componentMethodCondition_t(void *componentUserdata, void *methodUserdata); typedef struct nn_method_t nn_method_t; nn_componentTable *nn_newComponentTable(nn_Alloc *alloc, const char *typeName, void *userdata, nn_componentConstructor *constructor, nn_componentDestructor *destructor); void nn_destroyComponentTable(nn_componentTable *table); nn_method_t *nn_defineMethod(nn_componentTable *table, const char *methodName, nn_componentMethod *methodFunc, const char *methodDoc); void nn_method_setDirect(nn_method_t *method, nn_bool_t direct); void nn_method_setUserdata(nn_method_t *method, void *userdata); void nn_method_setCondition(nn_method_t *method, nn_componentMethodCondition_t *condition); const char *nn_getTableMethod(nn_componentTable *table, nn_size_t idx, nn_bool_t *outDirect); const char *nn_methodDoc(nn_componentTable *table, const char *methodName); nn_bool_t nn_isMethodEnabled(nn_component *component, const char *methodName); // Resource stuff typedef struct nn_resourceTable_t nn_resourceTable_t; typedef struct nn_resourceMethod_t nn_resourceMethod_t; typedef void nn_resourceDestructor_t(void *userdata); typedef void nn_resourceMethodCallback_t(void *userdata, void *methodUserdata, nn_computer *computer); typedef nn_bool_t nn_resourceMethodCondition_t(void *userdata, void *methodUserdata); nn_resourceTable_t *nn_resource_newTable(nn_Context *ctx, nn_resourceDestructor_t *dtor); nn_resourceMethod_t *nn_resource_addMethod(nn_resourceTable_t *table, const char *methodName, nn_resourceMethodCallback_t *method, const char *doc); void nn_resource_setUserdata(nn_resourceMethod_t *method, void *methodUserdata); void nn_resource_setCondition(nn_resourceMethod_t *method, nn_resourceMethodCondition_t *methodCondition); nn_bool_t nn_resource_invoke(nn_computer *computer, nn_size_t resourceID, const char *method); // returns the name, and NULL for out of bounds const char *nn_resource_nextMethodInfo(nn_computer *computer, nn_size_t id, const char **doc, nn_size_t *idx); nn_resourceTable_t *nn_resource_fetchTable(nn_computer *computer, nn_size_t resourceID); nn_size_t nn_resource_allocate(nn_computer *computer, void *userdata, nn_resourceTable_t *table); void nn_resource_release(nn_computer *computer, nn_size_t id); // Component calling /* Returns false if the method does not exist */ nn_bool_t nn_invokeComponentMethod(nn_component *component, const char *name); void nn_simulateBufferedIndirect(nn_component *component, double amount, double amountPerTick); void nn_resetCall(nn_computer *computer); void nn_addArgument(nn_computer *computer, nn_value arg); void nn_return(nn_computer *computer, nn_value val); nn_value nn_getArgument(nn_computer *computer, nn_size_t idx); nn_value nn_getReturn(nn_computer *computer, nn_size_t idx); nn_size_t nn_getArgumentCount(nn_computer *computer); nn_size_t nn_getReturnCount(nn_computer *computer); // Value stuff nn_value nn_values_nil(void); nn_value nn_values_integer(nn_integer_t integer); nn_value nn_values_number(double num); nn_value nn_values_boolean(nn_bool_t boolean); nn_value nn_values_cstring(const char *string); nn_value nn_values_string(nn_Alloc *alloc, const char *string, nn_size_t len); nn_value nn_values_array(nn_Alloc *alloc, nn_size_t len); nn_value nn_values_table(nn_Alloc *alloc, nn_size_t pairCount); nn_value nn_values_resource(nn_size_t id); void nn_return_nil(nn_computer *computer); void nn_return_integer(nn_computer *computer, nn_integer_t integer); void nn_return_number(nn_computer *computer, double number); void nn_return_boolean(nn_computer *computer, nn_bool_t boolean); void nn_return_cstring(nn_computer *computer, const char *cstr); void nn_return_string(nn_computer *computer, const char *str, nn_size_t len); nn_value nn_return_array(nn_computer *computer, nn_size_t len); nn_value nn_return_table(nn_computer *computer, nn_size_t len); void nn_return_resource(nn_computer *computer, nn_size_t userdata); nn_size_t nn_values_getType(nn_value val); nn_value nn_values_retain(nn_value val); void nn_values_drop(nn_value val); void nn_values_dropAll(nn_value *values, nn_size_t len); void nn_values_set(nn_value arr, nn_size_t idx, nn_value val); nn_value nn_values_get(nn_value arr, nn_size_t idx); void nn_values_setPair(nn_value obj, nn_size_t idx, nn_value key, nn_value val); nn_pair nn_values_getPair(nn_value obj, nn_size_t idx); nn_integer_t nn_toInt(nn_value val); double nn_toNumber(nn_value val); nn_bool_t nn_toBoolean(nn_value val); const char *nn_toCString(nn_value val); const char *nn_toString(nn_value val, nn_size_t *len); nn_integer_t nn_toIntOr(nn_value val, nn_integer_t defaultVal); double nn_toNumberOr(nn_value val, double defaultVal); nn_bool_t nn_toBooleanOr(nn_value val, nn_bool_t defaultVal); /* * Computes the "packet size" of the values, using the same algorithm as OC. * This is used by pushSignal to check the size */ nn_size_t nn_measurePacketSize(nn_value *vals, nn_size_t len); // COMPONENTS /* Loads the vtables for the default implementations of those components */ void nn_loadCoreComponentTables(nn_universe *universe); // loading each component void nn_loadEepromTable(nn_universe *universe); void nn_loadFilesystemTable(nn_universe *universe); void nn_loadDriveTable(nn_universe *universe); void nn_loadScreenTable(nn_universe *universe); void nn_loadGraphicsCardTable(nn_universe *universe); void nn_loadKeyboardTable(nn_universe *universe); void nn_loadModemTable(nn_universe *universe); void nn_loadTunnelTable(nn_universe *universe); void nn_loadDiskDriveTable(nn_universe *universe); void nn_loadExternalComputerTable(nn_universe *universe); nn_component *nn_mountKeyboard(nn_computer *computer, nn_address address, int slot); // the helpers // EEPROM typedef struct nn_eepromControl { double readHeatPerByte; double writeHeatPerByte; double readEnergyCostPerByte; double writeEnergyCostPerByte; double bytesReadPerTick; double bytesWrittenPerTick; } nn_eepromControl; typedef struct nn_eepromTable { void *userdata; void (*deinit)(void *userdata); // methods nn_size_t size; nn_size_t dataSize; void (*getLabel)(void *userdata, char *buf, nn_size_t *buflen, nn_errorbuf_t error); nn_size_t (*setLabel)(void *userdata, const char *buf, nn_size_t buflen, nn_errorbuf_t error); nn_size_t (*get)(void *userdata, char *buf, nn_errorbuf_t error); nn_bool_t (*set)(void *userdata, const char *buf, nn_size_t len, nn_errorbuf_t error); nn_size_t (*getData)(void *userdata, char *buf, nn_errorbuf_t error); nn_bool_t (*setData)(void *userdata, const char *buf, nn_size_t len, nn_errorbuf_t error); // allocate the string with alloc. We recommend using nn_strdup() char *(*getArchitecture)(nn_Alloc *alloc, void *userdata, nn_errorbuf_t error); void (*setArchitecture)(void *userdata, const char *buf, nn_errorbuf_t error); nn_bool_t (*isReadonly)(void *userdata, nn_errorbuf_t error); nn_bool_t (*makeReadonly)(void *userdata, nn_errorbuf_t error); } nn_eepromTable; typedef struct nn_eeprom nn_eeprom; typedef struct nn_veepromOptions { const char *code; nn_size_t len; nn_size_t size; const char *data; nn_size_t dataLen; nn_size_t dataSize; char label[NN_LABEL_SIZE]; nn_size_t labelLen; nn_bool_t isReadOnly; } nn_veepromOptions; nn_eeprom *nn_newEEPROM(nn_Context *context, nn_eepromTable table, nn_eepromControl control); nn_eeprom *nn_volatileEEPROM(nn_Context *context, nn_veepromOptions opts, nn_eepromControl control); nn_guard *nn_getEEPROMLock(nn_eeprom *eeprom); void nn_retainEEPROM(nn_eeprom *eeprom); nn_bool_t nn_destroyEEPROM(nn_eeprom *eeprom); nn_component *nn_addEEPROM(nn_computer *computer, nn_address address, int slot, nn_eeprom *eeprom); // FileSystem typedef struct nn_filesystemControl { double readBytesPerTick; double writeBytesPerTick; double removeFilesPerTick; double createFilesPerTick; double readHeatPerByte; double writeHeatPerByte; double removeHeat; double createHeat; double readEnergyPerByte; double writeEnergyPerByte; double removeEnergy; double createEnergy; } nn_filesystemControl; typedef struct nn_filesystemTable { void *userdata; void (*deinit)(void *userdata); void (*getLabel)(void *userdata, char *buf, nn_size_t *buflen, nn_errorbuf_t err); nn_size_t (*setLabel)(void *userdata, const char *buf, nn_size_t buflen, nn_errorbuf_t err); nn_size_t (*spaceUsed)(void *userdata); nn_size_t spaceTotal; nn_bool_t (*isReadOnly)(void *userdata, nn_errorbuf_t err); // general operations nn_size_t (*size)(void *userdata, const char *path, nn_errorbuf_t err); nn_size_t (*remove)(void *userdata, const char *path, nn_errorbuf_t err); nn_timestamp_t (*lastModified)(void *userdata, const char *path, nn_errorbuf_t err); nn_size_t (*rename)(void *userdata, const char *from, const char *to, nn_errorbuf_t err); nn_bool_t (*exists)(void *userdata, const char *path, nn_errorbuf_t err); // directory operations nn_bool_t (*isDirectory)(void *userdata, const char *path, nn_errorbuf_t err); nn_bool_t (*makeDirectory)(void *userdata, const char *path, nn_errorbuf_t err); // The returned array should be allocated with the supplied allocator. // The strings should be null terminated. Use nn_strdup for the allocation to guarantee nn_deallocStr deallocates it correctly. // For the array, the *exact* size of the allocation should be sizeof(char *) * (*len), // If it is not, the behavior is undefined. // We recommend first computing len then allocating, though if that is not doable or practical, // consider nn_resize()ing it to the correct size to guarantee a correct deallocation. char **(*list)(nn_Alloc *alloc, void *userdata, const char *path, nn_size_t *len, nn_errorbuf_t err); // file operations void *(*open)(void *userdata, const char *path, const char *mode, nn_errorbuf_t err); nn_bool_t (*close)(void *userdata, void *fd, nn_errorbuf_t err); nn_bool_t (*write)(void *userdata, void *fd, const char *buf, nn_size_t len, nn_errorbuf_t err); nn_size_t (*read)(void *userdata, void *fd, char *buf, nn_size_t required, nn_errorbuf_t err); nn_size_t (*seek)(void *userdata, void *fd, const char *whence, int off, nn_errorbuf_t err); } nn_filesystemTable; typedef struct nn_filesystem nn_filesystem; typedef struct nn_vfilesystemImageNode { const char *name; // if NULL, the node is a directory const char *data; // if it is a directory, this is the amount of entries encoded afterwards nn_size_t len; } nn_vfilesystemImageNode; typedef struct nn_vfilesystemOptions { // used to compute lastModified nn_timestamp_t creationTime; nn_size_t maxDirEntries; nn_size_t capacity; nn_bool_t isReadOnly; char label[NN_LABEL_SIZE]; nn_size_t labelLen; // loading the files into the tmpfs nn_vfilesystemImageNode *image; nn_size_t rootEntriesInImage; } nn_vfilesystemOptions; nn_filesystem *nn_newFilesystem(nn_Context *context, nn_filesystemTable table, nn_filesystemControl control); nn_filesystem *nn_volatileFilesystem(nn_Context *context, nn_vfilesystemOptions opts, nn_filesystemControl control); nn_guard *nn_getFilesystemLock(nn_filesystem *fs); void nn_retainFilesystem(nn_filesystem *fs); nn_bool_t nn_destroyFilesystem(nn_filesystem *fs); nn_component *nn_addFileSystem(nn_computer *computer, nn_address address, int slot, nn_filesystem *filesystem); // Drive typedef struct nn_driveControl { double readSectorsPerTick; double writeSectorsPerTick; // Set it to 0 to disable seek latency. double seekSectorsPerTick; double readHeatPerSector; double writeHeatPerSector; double motorHeatPerSector; double readEnergyPerSector; double writeEnergyPerSector; double motorEnergyPerSector; // if not, seeking *backwards* will cost as much as a full spin. nn_bool_t reversable; } nn_driveControl; typedef struct nn_driveTable { void *userdata; void (*deinit)(void *userdata); void (*getLabel)(void *userdata, char *buf, nn_size_t *buflen); nn_size_t (*setLabel)(void *userdata, const char *buf, nn_size_t buflen); nn_size_t platterCount; nn_size_t capacity; nn_size_t sectorSize; // sectors start at 1 as per OC. void (*readSector)(void *userdata, int sector, char *buf); void (*writeSector)(void *userdata, int sector, const char *buf); // readByte and writeByte will internally use readSector and writeSector. This is to ensure they are handled *consistently.* // Also makes the interface less redundant } nn_driveTable; typedef struct nn_vdriveOptions { nn_size_t sectorSize; nn_size_t platterCount; nn_size_t capacity; const char *data; char label[NN_LABEL_SIZE]; nn_size_t labelLen; } nn_vdriveOptions; typedef struct nn_drive nn_drive; nn_drive *nn_newDrive(nn_Context *context, nn_driveTable table, nn_driveControl control); nn_drive *nn_volatileDrive(nn_Context *context, nn_vdriveOptions opts, nn_driveControl control); nn_guard *nn_getDriveLock(nn_drive *drive); void nn_retainDrive(nn_drive *drive); nn_bool_t nn_destroyDrive(nn_drive *drive); nn_component *nn_addDrive(nn_computer *computer, nn_address address, int slot, nn_drive *drive); // Screens and GPUs typedef struct nn_screen nn_screen; typedef struct nn_scrchr_t { unsigned int codepoint; int fg; int bg; nn_bool_t isFgPalette; nn_bool_t isBgPalette; } nn_scrchr_t; nn_screen *nn_newScreen(nn_Context *context, int maxWidth, int maxHeight, int maxDepth, int editableColors, int paletteColors); nn_componentTable *nn_getScreenTable(nn_universe *universe); void nn_retainScreen(nn_screen *screen); void nn_destroyScreen(nn_screen *screen); void nn_lockScreen(nn_screen *screen); void nn_unlockScreen(nn_screen *screen); void nn_getResolution(nn_screen *screen, int *width, int *height); void nn_maxResolution(nn_screen *screen, int *width, int *height); void nn_setResolution(nn_screen *screen, int width, int height); // changes the maximum resolution // DOES NOT USE THE LOCK AND THUS MAY CAUSE RACE CONDITIONS AND SEGFAULTS!!!!! nn_bool_t nn_unsafeReallocateScreenBuffer(nn_screen *screen, int maxWidth, int maxHeight); void nn_getViewport(nn_screen *screen, int *width, int *height); void nn_setViewport(nn_screen *screen, int width, int height); void nn_getAspectRatio(nn_screen *screen, int *width, int *height); void nn_setAspectRatio(nn_screen *screen, int width, int height); void nn_addKeyboard(nn_screen *screen, nn_address address); void nn_removeKeyboard(nn_screen *screen, nn_address address); nn_address nn_getKeyboard(nn_screen *screen, nn_size_t idx); nn_size_t nn_getKeyboardCount(nn_screen *screen); void nn_setEditableColors(nn_screen *screen, int count); int nn_getEditableColors(nn_screen *screen); void nn_setPaletteColor(nn_screen *screen, int idx, int color); int nn_getPaletteColor(nn_screen *screen, int idx); int nn_getPaletteCount(nn_screen *screen); int nn_maxDepth(nn_screen *screen); int nn_getDepth(nn_screen *screen); void nn_setDepth(nn_screen *screen, int depth); const char *nn_depthName(int depth); double nn_colorDistance(int colorA, int colorB); int nn_mapColor(int color, int *palette, int paletteSize); int nn_mapDepth(int color, int depth, nn_bool_t legacy); void nn_getStd4BitPalette(int color[16]); void nn_getStd8BitPalette(int color[256]); // Std4bit uses actual MC dye colors, except for white and black // Legacy uses OC's versions that were brightened void nn_getLegacy4BitPalette(int color[16]); void nn_setPixel(nn_screen *screen, int x, int y, nn_scrchr_t pixel); nn_scrchr_t nn_getPixel(nn_screen *screen, int x, int y); nn_bool_t nn_isDirty(nn_screen *screen); void nn_setDirty(nn_screen *screen, nn_bool_t dirty); nn_bool_t nn_isPrecise(nn_screen *screen); void nn_setPrecise(nn_screen *screen, nn_bool_t precise); nn_bool_t nn_isTouchModeInverted(nn_screen *screen); void nn_setTouchModeInverted(nn_screen *screen, nn_bool_t touchModeInverted); nn_bool_t nn_isOn(nn_screen *buffer); void nn_setOn(nn_screen *buffer, nn_bool_t on); nn_component *nn_addScreen(nn_computer *computer, nn_address address, int slot, nn_screen *screen); typedef struct nn_gpuControl { // VRAM Buffers int totalVRAM; int maximumBufferCount; int defaultBufferWidth; int defaultBufferHeight; // Calls per tick, only applicable to screens double screenCopyPerTick; double screenFillPerTick; double screenSetsPerTick; double bitbltPerTick; // for bitblit // Heat double heatPerPixelChange; double heatPerPixelReset; double heatPerVRAMChange; // Energy double energyPerPixelChange; double energyPerPixelReset; double energyPerVRAMChange; } nn_gpuControl; // the control is COPIED. nn_component *nn_addGPU(nn_computer *computer, nn_address address, int slot, nn_gpuControl *control); typedef struct nn_networkControl { double packetBytesPerTick; double heatPerFullPacket; double energyPerFullPacket; } nn_networkControl; nn_bool_t nn_wakeupMatches(nn_value *values, nn_size_t valueLen, const char *wakeUp, nn_bool_t fuzzy); // NULL on success, error string on failure // this *retains* all of those values, meaning you must drop them after call this function const char *nn_pushNetworkMessage(nn_computer *computer, nn_address receiver, nn_address sender, nn_size_t port, double distance, nn_value *values, nn_size_t valueLen); typedef struct nn_modemTable { void *userdata; void (*deinit)(void *userdata); // basic limits nn_bool_t wireless; nn_size_t maxValues; nn_size_t maxPacketSize; nn_size_t maxOpenPorts; // ports nn_bool_t (*isOpen)(void *userdata, nn_size_t port, nn_errorbuf_t err); nn_bool_t (*open)(void *userdata, nn_size_t port, nn_errorbuf_t err); // port NN_PORT_CLOSEALL means close all nn_bool_t (*close)(void *userdata, nn_size_t port, nn_errorbuf_t err); nn_size_t (*getPorts)(void *userdata, nn_size_t *ports, nn_errorbuf_t err); // messages // Address is NULL if broadcasting nn_bool_t (*send)(void *userdata, nn_address address, nn_size_t port, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err); // signal strength double maxStrength; double (*getStrength)(void *userdata, nn_errorbuf_t err); double (*setStrength)(void *userdata, double strength, nn_errorbuf_t err); // wake message nn_size_t (*getWakeMessage)(void *userdata, char *buf, nn_errorbuf_t err); nn_size_t (*setWakeMessage)(void *userdata, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err); } nn_modemTable; typedef struct nn_modem nn_modem; typedef struct nn_debugLoopbackNetworkOpts { nn_computer *computer; nn_address address; nn_size_t maxValues; nn_size_t maxPacketSize; nn_size_t maxOpenPorts; double maxStrength; nn_bool_t isWireless; } nn_debugLoopbackNetworkOpts; nn_modem *nn_newModem(nn_Context *context, nn_modemTable table, nn_networkControl control); nn_modem *nn_debugLoopbackModem(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control); nn_guard *nn_getModemLock(nn_modem *modem); void nn_retainModem(nn_modem *modem); nn_bool_t nn_destroyModem(nn_modem *modem); nn_component *nn_addModem(nn_computer *computer, nn_address address, int slot, nn_modem *modem); typedef struct nn_tunnelTable { void *userdata; void (*deinit)(void *userdata); nn_size_t maxValues; nn_size_t maxPacketSize; void (*send)(void *userdata, nn_value *values, nn_size_t valueCount, nn_errorbuf_t err); nn_size_t (*getChannel)(void *userdata, char *buf, nn_errorbuf_t err); nn_size_t (*getWakeMessage)(void *userdata, char *buf, nn_errorbuf_t err); nn_size_t (*setWakeMessage)(void *userdata, const char *buf, nn_size_t buflen, nn_bool_t fuzzy, nn_errorbuf_t err); } nn_tunnelTable; typedef struct nn_tunnel nn_tunnel; nn_tunnel *nn_newTunnel(nn_Context *context, nn_tunnelTable table, nn_networkControl control); nn_tunnel *nn_debugLoopbackTunnel(nn_Context *context, nn_debugLoopbackNetworkOpts opts, nn_networkControl control); nn_guard *nn_getTunnelLock(nn_tunnel *tunnel); void nn_retainTunnel(nn_tunnel *tunnel); nn_bool_t nn_destroyTunnel(nn_tunnel *tunnel); nn_component *nn_addTunnel(nn_computer *computer, nn_address address, int slot, nn_tunnel *tunnel); typedef struct nn_diskDriveTable { void *userdata; void (*deinit)(void *userdata); // velocity is 0 or less for "default" void (*eject)(void *userdata, double velocity, nn_errorbuf_t err); nn_bool_t (*isEmpty)(void *userdata); nn_address (*media)(void *userdata, nn_Alloc *alloc, nn_errorbuf_t err); } nn_diskDriveTable; typedef struct nn_diskDrive nn_diskDrive; nn_diskDrive *nn_newDiskDrive(nn_Context *context, nn_diskDriveTable table); nn_guard *nn_getDiskDriveLock(nn_diskDrive *diskDrive); void nn_retainDiskDrive(nn_diskDrive *diskDrive); nn_bool_t nn_destroyDiskDrive(nn_diskDrive *diskDrive); nn_component *nn_addDiskDrive(nn_computer *computer, nn_address address, int slot, nn_diskDrive *diskDrive); typedef struct nn_hologram nn_hologram; nn_hologram *nn_newHologram(nn_Context *context, int pallette_len, int width_x, int width_z, int height, int depth); nn_guard *nn_getHologramLock(nn_hologram *hologram); void nn_retainHologram(nn_hologram *hologram); nn_bool_t nn_destroyHologram(nn_hologram *hologram); nn_component *nn_addHologram(nn_computer *computer, nn_address address, int slot, nn_hologram *hologram); void nn_hologram_clear(nn_hologram *hologram); int nn_hologram_get(nn_hologram *hologram, int x, int y, int z); void nn_hologram_set(nn_hologram *hologram, int x, int y, int z, int value); void nn_hologram_fill(nn_hologram *hologram, int x, int z, int minY, int maxY, int value); void nn_hologram_copy(nn_hologram *hologram, int x, int z, int sx, int sz, int tx, int tz); float nn_hologram_getScale(nn_hologram *hologram); void nn_hologram_setScale(nn_hologram *hologram, float value); void nn_hologram_getTranslation(nn_hologram *hologram, double *x, double *y, double *z); void nn_hologram_setTranslation(nn_hologram *hologram, double x, double y, double z); int nn_hologram_maxDepth(nn_hologram *hologram); int nn_hologram_getPaletteColor(nn_hologram *hologram, int index); int nn_hologram_setPaletteColor(nn_hologram *hologram, int index, int value); typedef struct nn_externalComputerTable_t { void *userdata; void (*deinit)(void *userdata); nn_bool_t (*start)(void *userdata, nn_computer *requester, nn_errorbuf_t err); nn_bool_t (*stop)(void *userdata, nn_computer *requester, nn_errorbuf_t err); nn_bool_t (*isRunning)(void *userdata, nn_computer *requester, nn_errorbuf_t err); void (*beep)(void *userdata, nn_computer *requester, double freq, double duration, double volume, nn_errorbuf_t err); void (*crash)(void *userdata, nn_computer *requester, nn_errorbuf_t err); nn_architecture *(*getArchitecture)(void *userdata, nn_computer *requester, nn_errorbuf_t err); void (*getDeviceInfo)(void *userdata, nn_deviceInfoList_t *list, nn_computer *requester, nn_errorbuf_t err); nn_bool_t (*isRobot)(void *userdata, nn_computer *requester, nn_errorbuf_t err); } nn_externalComputerTable_t; typedef struct nn_externalComputer_t nn_externalComputer_t; // An external computer is a computer component. // It may refer to the current computer (counter-intuitively) // It may exist when the computer it is refering too has no running state (aka is powered off) nn_externalComputer_t *nn_newExternalComputer(nn_Context *ctx, nn_externalComputerTable_t table); nn_guard *nn_externalComputer_getLock(nn_externalComputer_t *external); void nn_externalComputer_retain(nn_externalComputer_t *external); nn_bool_t nn_externalComputer_destroy(nn_externalComputer_t *external); nn_component *nn_externalComputer_addTo(nn_computer *computer, nn_address address, int slot, nn_externalComputer_t *external); #ifdef __cplusplus // c++ sucks } #endif #endif