merge
This commit is contained in:
44
CODING.md
Normal file
44
CODING.md
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
# Coding style
|
||||||
|
|
||||||
|
NN and NCL have a very specific API and code style.
|
||||||
|
This is meant to be consistent everywhere to keep the API and codebase easy to learn, navigate, use and maintain.
|
||||||
|
|
||||||
|
## Separation of files
|
||||||
|
|
||||||
|
- `src/neonucleus.h` for the header to NeoNucleus, the engine. This provides the systems everything interfaces with.
|
||||||
|
- `src/neonucleus.c`, the implementation of stuff defined in its header file.
|
||||||
|
- `src/ncomplib.h` for the header to the NeoNucleus Component Library, providing complete implementations for various components.
|
||||||
|
- `src/ncomplib.c` for the implementation of stuff defined in its header file.
|
||||||
|
- `src/main.c` for the test emulator used purely to try out the engine and see if it works.
|
||||||
|
|
||||||
|
The rationale for single C-file libraries is that they are easy to download and compile. This can simplify the process of updating and compiling into projects.
|
||||||
|
The reason for using this over shared libraries is that NN makes no ABI stability guarantees. It also makes it super easy to vendor the library,
|
||||||
|
meaning you can trivially pin to a specific version, and even a trivial build system can work. The reason for not going full STB single-file is that
|
||||||
|
LSPs can struggle to give diagnostics for the implementation side.
|
||||||
|
|
||||||
|
## General coding rules
|
||||||
|
|
||||||
|
- For actual source code, aim for 80 cols max on 4-space indentation. This isn't a hard rule, but breaking it should be avoided.
|
||||||
|
- `goto` should be used for trivial cases, such as the classic `goto fail;` pattern, but also to loop (`goto retry;`) or to skip (`goto found;`)
|
||||||
|
- While commenting is fine, comments should not be needed most of the time. Avoid writing code which needs to be explained through
|
||||||
|
comments, aim to ensure the code is readable.
|
||||||
|
- Prefer fixed capacities. This is useful to reduce the effect of memory hogging. If the fixed capacity is small enough to not use much memory,
|
||||||
|
prefer pre-allocating the maximum capacity to simplify the code and reduce places that can OOM.
|
||||||
|
|
||||||
|
## Component classes/implementations
|
||||||
|
|
||||||
|
- NN should provide component classes. These are component instances with a `void *` state specified to them, which stores its data in `classState`.
|
||||||
|
Their job is to define the methods and its docs and validate the methods to convert them into requests specific to the component type, and
|
||||||
|
send those requests to a handler (a simple function pointer). They should not have their own locks, they should not store their own state.
|
||||||
|
- NCL should provide component implementations. These do not have custom state or handlers, as they are *complete*. Prefer functions which set
|
||||||
|
internal state over many parameters in the constructors. NCL should handle the locking, as all components must be thread-safe, in which case
|
||||||
|
you should avoid wrapping entire handlers in the locks, and instead prefer locking as late as possible and unlocking as early as possible.
|
||||||
|
This is to give threads exclusive ownership over the potentially shared component for as little time as possible.
|
||||||
|
- Assume the component user is experimenting, a hacker, or has an RCE in their code. Make sure everything is validated, from internal state to
|
||||||
|
arguments. Do not trust the user, we do not have the JVM to save us here.
|
||||||
|
- Assume anything that can error will error at least once and ensure recovery with an exit code and error message, never panic or forcefully crash.
|
||||||
|
Assume memory allocation will eventually fail. Assume the filesystem will eventually break. Do not abort/exit/panic.
|
||||||
|
- Use an enum to keep track of method indexes and method count.
|
||||||
|
- Avoid separate functions for each method handler. Inline them into the single handler that handles dispatch. This not only net-shrinks the codebase,
|
||||||
|
but also means that navigating by searching for `== NN_<COMPONENT>_<REQUEST>)` (in NCL) or `== NN_<COMPONENT>NUM_<METHOD>)` (in NN) will show the
|
||||||
|
dispatch logic and implementation.
|
||||||
@@ -63,7 +63,10 @@ fn compileRaylib(b: *std.Build, os: std.Target.Os.Tag, c: *std.Build.Step.Compil
|
|||||||
c.linkSystemLibrary("raylib");
|
c.linkSystemLibrary("raylib");
|
||||||
if (os == .windows) {
|
if (os == .windows) {
|
||||||
c.linkSystemLibrary("WinMM");
|
c.linkSystemLibrary("WinMM");
|
||||||
c.linkSystemLibrary("GDI32");
|
c.linkSystemLibrary("GDI32"); // <---
|
||||||
|
c.linkSystemLibrary("User32"); // ^ Windows can't just rely on GDI
|
||||||
|
c.linkSystemLibrary("Shell32");
|
||||||
|
c.linkSystemLibrary("OpenGL32");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Submodule foreign/lua52 deleted from 4324904b60
@@ -285,7 +285,7 @@ static int luaArch_component_list(lua_State *L) {
|
|||||||
lua_createtable(L, 0, 0);
|
lua_createtable(L, 0, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const char *comps[len];
|
NN_VLA(const char *, comps, len);
|
||||||
nn_getComponents(arch->computer, comps);
|
nn_getComponents(arch->computer, comps);
|
||||||
for(size_t i = 0; i < len; i++) {
|
for(size_t i = 0; i < len; i++) {
|
||||||
nn_Component *c = nn_getComponent(arch->computer, comps[i]);
|
nn_Component *c = nn_getComponent(arch->computer, comps[i]);
|
||||||
@@ -385,7 +385,7 @@ static int luaArch_component_methods(lua_State *L) {
|
|||||||
lua_createtable(L, 0, 0);
|
lua_createtable(L, 0, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const char *methods[methodLen];
|
NN_VLA(const char *, methods, methodLen);
|
||||||
nn_getComponentMethods(c, methods, &methodLen);
|
nn_getComponentMethods(c, methods, &methodLen);
|
||||||
lua_createtable(L, 0, methodLen);
|
lua_createtable(L, 0, methodLen);
|
||||||
for(size_t i = 0; i < methodLen; i++) {
|
for(size_t i = 0; i < methodLen; i++) {
|
||||||
@@ -414,7 +414,8 @@ static int luaArch_component_fields(lua_State *L) {
|
|||||||
lua_createtable(L, 0, 0);
|
lua_createtable(L, 0, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
const char *methods[methodLen];
|
// const char *methods[methodLen];
|
||||||
|
NN_VLA(const char *, methods, methodLen);
|
||||||
nn_getComponentMethods(c, methods, &methodLen);
|
nn_getComponentMethods(c, methods, &methodLen);
|
||||||
lua_createtable(L, 0, methodLen);
|
lua_createtable(L, 0, methodLen);
|
||||||
for(size_t i = 0; i < methodLen; i++) {
|
for(size_t i = 0; i < methodLen; i++) {
|
||||||
@@ -587,15 +588,15 @@ static void luaArch_loadEnv(lua_State *L) {
|
|||||||
lua_createtable(L, 0, 10);
|
lua_createtable(L, 0, 10);
|
||||||
int unicode = lua_gettop(L);
|
int unicode = lua_gettop(L);
|
||||||
lua_pushcfunction(L, luaArch_unicode_char);
|
lua_pushcfunction(L, luaArch_unicode_char);
|
||||||
lua_setfield(L, component, "char");
|
lua_setfield(L, unicode, "char");
|
||||||
lua_pushcfunction(L, luaArch_unicode_len);
|
lua_pushcfunction(L, luaArch_unicode_len);
|
||||||
lua_setfield(L, component, "len");
|
lua_setfield(L, unicode, "len");
|
||||||
lua_pushcfunction(L, luaArch_unicode_sub);
|
lua_pushcfunction(L, luaArch_unicode_sub);
|
||||||
lua_setfield(L, component, "sub");
|
lua_setfield(L, unicode, "sub");
|
||||||
lua_pushcfunction(L, luaArch_unicode_len);
|
lua_pushcfunction(L, luaArch_unicode_len);
|
||||||
lua_setfield(L, component, "wlen");
|
lua_setfield(L, unicode, "wlen");
|
||||||
lua_pushcfunction(L, luaArch_unicode_wtrunc);
|
lua_pushcfunction(L, luaArch_unicode_wtrunc);
|
||||||
lua_setfield(L, component, "wtrunc");
|
lua_setfield(L, unicode, "wtrunc");
|
||||||
lua_setglobal(L, "unicode");
|
lua_setglobal(L, "unicode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
214
src/main.c
214
src/main.c
@@ -12,6 +12,11 @@
|
|||||||
#include <raylib.h>
|
#include <raylib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#ifdef NN_WINDOWS
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
nn_Architecture getLuaArch();
|
nn_Architecture getLuaArch();
|
||||||
|
|
||||||
static const char minBIOS[] = {
|
static const char minBIOS[] = {
|
||||||
@@ -323,8 +328,213 @@ double ne_energy_accumulator(void *state, nn_Computer *c, double n) {
|
|||||||
return nn_getTotalEnergy(c);
|
return nn_getTotalEnergy(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef NN_WINDOWS
|
||||||
|
// Quick self-tests for Windows-specific fixes
|
||||||
|
// These run before anything else so failures are caught early
|
||||||
|
|
||||||
|
static jmp_buf nn_test_jmpbuf;
|
||||||
|
static volatile int nn_test_caught;
|
||||||
|
|
||||||
|
static void nn_test_crash_handler(int sig) {
|
||||||
|
nn_test_caught = 1;
|
||||||
|
longjmp(nn_test_jmpbuf, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nn_test_try(void (*func)(void *), void *arg) {
|
||||||
|
nn_test_caught = 0;
|
||||||
|
signal(SIGSEGV, nn_test_crash_handler);
|
||||||
|
signal(SIGABRT, nn_test_crash_handler);
|
||||||
|
if(setjmp(nn_test_jmpbuf) == 0) {
|
||||||
|
func(arg);
|
||||||
|
}
|
||||||
|
signal(SIGSEGV, SIG_DFL);
|
||||||
|
signal(SIGABRT, SIG_DFL);
|
||||||
|
return nn_test_caught;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nn_test_failed = 0;
|
||||||
|
static int nn_test_passed = 0;
|
||||||
|
|
||||||
|
static void nn_test_report(const char *name, int crashed, int expected_crash) {
|
||||||
|
if(crashed && !expected_crash) {
|
||||||
|
printf("[CRASH] %s\n", name);
|
||||||
|
nn_test_failed++;
|
||||||
|
} else if(!crashed && expected_crash) {
|
||||||
|
printf("[FAIL] %s (expected crash)\n", name);
|
||||||
|
nn_test_failed++;
|
||||||
|
} else {
|
||||||
|
printf("[OK] %s\n", name);
|
||||||
|
nn_test_passed++;
|
||||||
|
}
|
||||||
|
fflush(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- realloc tests ---
|
||||||
|
|
||||||
|
static void nn_test_realloc_null(void *arg) {
|
||||||
|
nn_Context *ctx = arg;
|
||||||
|
void *p = nn_realloc(ctx, NULL, 0, 64);
|
||||||
|
if(p == NULL) { nn_test_failed++; return; }
|
||||||
|
nn_free(ctx, p, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_realloc_grow(void *arg) {
|
||||||
|
nn_Context *ctx = arg;
|
||||||
|
void *a = nn_alloc(ctx, 64);
|
||||||
|
if(a == NULL) return;
|
||||||
|
void *b = nn_realloc(ctx, a, 64, 128);
|
||||||
|
if(b != NULL) nn_free(ctx, b, 128);
|
||||||
|
else nn_free(ctx, a, 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_realloc_free(void *arg) {
|
||||||
|
nn_Context *ctx = arg;
|
||||||
|
void *c = nn_alloc(ctx, 64);
|
||||||
|
if(c == NULL) return;
|
||||||
|
nn_realloc(ctx, c, 64, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- lock tests ---
|
||||||
|
|
||||||
|
static void nn_test_lock_create_destroy(void *arg) {
|
||||||
|
nn_Context *ctx = arg;
|
||||||
|
nn_Lock *lock = nn_createLock(ctx);
|
||||||
|
if(lock == NULL) { nn_test_failed++; return; }
|
||||||
|
nn_destroyLock(ctx, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_lock_cycle(void *arg) {
|
||||||
|
nn_Context *ctx = arg;
|
||||||
|
nn_Lock *lock = nn_createLock(ctx);
|
||||||
|
if(lock == NULL) { nn_test_failed++; return; }
|
||||||
|
// lock and unlock 100 times to stress it
|
||||||
|
for(int i = 0; i < 100; i++) {
|
||||||
|
nn_lock(ctx, lock);
|
||||||
|
nn_unlock(ctx, lock);
|
||||||
|
}
|
||||||
|
nn_destroyLock(ctx, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_lock_two(void *arg) {
|
||||||
|
nn_Context *ctx = arg;
|
||||||
|
// two locks at the same time, make sure they dont interfere
|
||||||
|
nn_Lock *a = nn_createLock(ctx);
|
||||||
|
nn_Lock *b = nn_createLock(ctx);
|
||||||
|
if(a == NULL || b == NULL) { nn_test_failed++; return; }
|
||||||
|
nn_lock(ctx, a);
|
||||||
|
nn_lock(ctx, b);
|
||||||
|
nn_unlock(ctx, b);
|
||||||
|
nn_unlock(ctx, a);
|
||||||
|
nn_destroyLock(ctx, a);
|
||||||
|
nn_destroyLock(ctx, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- VFS tests ---
|
||||||
|
|
||||||
|
static void nn_test_vfs_stat(void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
// stat current directory, should always work
|
||||||
|
ncl_Stat s;
|
||||||
|
bool ok = ncl_stat(ncl_defaultFS, ".", &s);
|
||||||
|
if(!ok) nn_test_failed++;
|
||||||
|
if(!s.isDirectory) nn_test_failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_vfs_dir(void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
void *dir = ncl_opendir(ncl_defaultFS, ".");
|
||||||
|
if(dir == NULL) { nn_test_failed++; return; }
|
||||||
|
char name[NN_MAX_PATH];
|
||||||
|
// just read one entry, dont care what it is
|
||||||
|
ncl_readdir(ncl_defaultFS, dir, name);
|
||||||
|
ncl_closedir(ncl_defaultFS, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_vfs_mkdir_remove(void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
const char *testdir = "nn_test_tmpdir";
|
||||||
|
ncl_mkdir(ncl_defaultFS, testdir);
|
||||||
|
ncl_Stat s;
|
||||||
|
bool ok = ncl_stat(ncl_defaultFS, testdir, &s);
|
||||||
|
if(!ok || !s.isDirectory) nn_test_failed++;
|
||||||
|
ncl_remove(ncl_defaultFS, testdir);
|
||||||
|
// should be gone now
|
||||||
|
ok = ncl_stat(ncl_defaultFS, testdir, &s);
|
||||||
|
if(ok) nn_test_failed++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_test_vfs_seek(void *arg) {
|
||||||
|
(void)arg;
|
||||||
|
// write a small file, seek around, read back
|
||||||
|
const char *path = "nn_test_seekfile";
|
||||||
|
const char *data = "abcdefghij";
|
||||||
|
void *f = ncl_openfile(ncl_defaultFS, path, "w");
|
||||||
|
if(f == NULL) { nn_test_failed++; return; }
|
||||||
|
ncl_writefile(ncl_defaultFS, f, data, 10);
|
||||||
|
ncl_closefile(ncl_defaultFS, f);
|
||||||
|
|
||||||
|
f = ncl_openfile(ncl_defaultFS, path, "r");
|
||||||
|
if(f == NULL) { nn_test_failed++; ncl_remove(ncl_defaultFS, path); return; }
|
||||||
|
// seek to offset 5 from start
|
||||||
|
int off = 5;
|
||||||
|
bool ok = ncl_seekfile(ncl_defaultFS, f, NN_SEEK_SET, &off);
|
||||||
|
if(!ok || off != 5) nn_test_failed++;
|
||||||
|
// read from there
|
||||||
|
char buf[5];
|
||||||
|
size_t len = 5;
|
||||||
|
ok = ncl_readfile(ncl_defaultFS, f, buf, &len);
|
||||||
|
if(!ok || len != 5) nn_test_failed++;
|
||||||
|
// should be "fghij"
|
||||||
|
if(buf[0] != 'f' || buf[4] != 'j') nn_test_failed++;
|
||||||
|
ncl_closefile(ncl_defaultFS, f);
|
||||||
|
ncl_remove(ncl_defaultFS, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nn_run_selftests(nn_Context *ctx) {
|
||||||
|
printf("--- nn self tests ---\n");
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
nn_test_report("realloc(NULL)",
|
||||||
|
nn_test_try(nn_test_realloc_null, ctx), 0);
|
||||||
|
nn_test_report("realloc(ptr, grow)",
|
||||||
|
nn_test_try(nn_test_realloc_grow, ctx), 0);
|
||||||
|
nn_test_report("realloc(ptr, free)",
|
||||||
|
nn_test_try(nn_test_realloc_free, ctx), 0);
|
||||||
|
|
||||||
|
nn_test_report("lock create/destroy",
|
||||||
|
nn_test_try(nn_test_lock_create_destroy, ctx), 0);
|
||||||
|
nn_test_report("lock 100 cycles",
|
||||||
|
nn_test_try(nn_test_lock_cycle, ctx), 0);
|
||||||
|
nn_test_report("two locks interleaved",
|
||||||
|
nn_test_try(nn_test_lock_two, ctx), 0);
|
||||||
|
|
||||||
|
nn_test_report("vfs stat cwd",
|
||||||
|
nn_test_try(nn_test_vfs_stat, NULL), 0);
|
||||||
|
nn_test_report("vfs readdir cwd",
|
||||||
|
nn_test_try(nn_test_vfs_dir, NULL), 0);
|
||||||
|
nn_test_report("vfs mkdir/remove",
|
||||||
|
nn_test_try(nn_test_vfs_mkdir_remove, NULL), 0);
|
||||||
|
nn_test_report("vfs seek",
|
||||||
|
nn_test_try(nn_test_vfs_seek, NULL), 0);
|
||||||
|
|
||||||
|
printf("--- %d passed, %d failed ---\n\n", nn_test_passed, nn_test_failed);
|
||||||
|
fflush(stdout);
|
||||||
|
|
||||||
|
if(nn_test_failed > 0) {
|
||||||
|
printf("self tests failed, aborting\n");
|
||||||
|
fflush(stdout);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
const char *player = getenv("USER");
|
const char *player = getenv("USER");
|
||||||
|
#ifdef NN_WINDOWS
|
||||||
|
if(player == NULL) player = getenv("USERNAME");
|
||||||
|
#endif
|
||||||
if(player == NULL) player = "me";
|
if(player == NULL) player = "me";
|
||||||
|
|
||||||
bool sandboxMem = getenv("NN_MEMSAND") != NULL;
|
bool sandboxMem = getenv("NN_MEMSAND") != NULL;
|
||||||
@@ -336,7 +546,9 @@ int main(int argc, char **argv) {
|
|||||||
nn_Context ctx;
|
nn_Context ctx;
|
||||||
nn_initContext(&ctx);
|
nn_initContext(&ctx);
|
||||||
nn_initPalettes();
|
nn_initPalettes();
|
||||||
|
#ifdef NN_WINDOWS
|
||||||
|
nn_run_selftests(&ctx);
|
||||||
|
#endif
|
||||||
ne_memSand sand;
|
ne_memSand sand;
|
||||||
sand.buf = NULL;
|
sand.buf = NULL;
|
||||||
|
|
||||||
|
|||||||
118
src/ncomplib.c
118
src/ncomplib.c
@@ -17,8 +17,19 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
// Read me all my rights
|
||||||
#elif defined(NN_WINDOWS)
|
#elif defined(NN_WINDOWS)
|
||||||
#error "Windows is not supported yet"
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <direct.h>
|
||||||
|
|
||||||
|
typedef struct ncl_WinDir {
|
||||||
|
HANDLE handle;
|
||||||
|
WIN32_FIND_DATAA findData;
|
||||||
|
bool isFirst;
|
||||||
|
} ncl_WinDir;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
||||||
@@ -54,11 +65,47 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
|||||||
if(wanted == NN_SEEK_SET) whence = SEEK_SET;
|
if(wanted == NN_SEEK_SET) whence = SEEK_SET;
|
||||||
if(wanted == NN_SEEK_CUR) whence = SEEK_CUR;
|
if(wanted == NN_SEEK_CUR) whence = SEEK_CUR;
|
||||||
if(wanted == NN_SEEK_END) whence = SEEK_END;
|
if(wanted == NN_SEEK_END) whence = SEEK_END;
|
||||||
if(fseek(f, whence, request->seek.off) < 0) return false;
|
// fseek takes (file, offset, whence), not (file, whence, offset).
|
||||||
|
// The original code had these two arguments swapped, which caused
|
||||||
|
// every seek to go to the wrong position or fail entirely.
|
||||||
|
/*
|
||||||
|
* We want to: seek 100 bytes from the beginning (SEEK_SET = 0)
|
||||||
|
* Call: fseek(f, 0, 100)
|
||||||
|
* offset=0, whence=100
|
||||||
|
* Result: whence=100 is invalid, fseek returns -1, the function returns false
|
||||||
|
|
||||||
|
* We want to: seek 0 from the current position (SEEK_CUR = 1)
|
||||||
|
* Call: fseek(f, 1, 0)
|
||||||
|
* offset=1, whence=SEEK_SET(0)
|
||||||
|
* Result: jumps to position 1 from the beginning of the file instead of staying at the same position
|
||||||
|
|
||||||
|
* We want to: seek 0 from the beginning (SEEK_SET = 0)
|
||||||
|
* Call: fseek(f, 0, 0)
|
||||||
|
* offset=0, whence=SEEK_SET(0)
|
||||||
|
* Result: works correctly by chance
|
||||||
|
|
||||||
|
* We want to: seek 2 from the beginning (SEEK_SET = 0)
|
||||||
|
* Call: fseek(f, 0, 2)
|
||||||
|
* offset=0, whence=SEEK_END(2)
|
||||||
|
* Result: Goes to the end of the file instead of position 2
|
||||||
|
*/
|
||||||
|
// Original fseek signature: int fseek(FILE *stream, long offset, int whence) remains,
|
||||||
|
// yet the : `if(fseek(f, whence, request->seek.off) < 0) return false;`
|
||||||
|
// ...variant was wrong
|
||||||
|
if(fseek(f, request->seek.off, whence) < 0) return false;
|
||||||
|
|
||||||
request->seek.off = ftell(f);
|
request->seek.off = ftell(f);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(request->action == NCL_VFS_REMOVE) {
|
if(request->action == NCL_VFS_REMOVE) {
|
||||||
|
// On Windows, remove() cannot delete directories.
|
||||||
|
// We need to check if the path is a directory and use _rmdir instead.
|
||||||
|
#ifdef NN_WINDOWS
|
||||||
|
DWORD attrs = GetFileAttributesA(request->remove);
|
||||||
|
if(attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
return _rmdir(request->remove) == 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return remove(request->remove) == 0;
|
return remove(request->remove) == 0;
|
||||||
}
|
}
|
||||||
#ifdef NN_POSIX
|
#ifdef NN_POSIX
|
||||||
@@ -102,8 +149,73 @@ bool ncl_defaultHandler(ncl_VFSRequest *request) {
|
|||||||
if(request->action == NCL_VFS_MKDIR) {
|
if(request->action == NCL_VFS_MKDIR) {
|
||||||
return mkdir(request->mkdir, 0777) == 0;
|
return mkdir(request->mkdir, 0777) == 0;
|
||||||
}
|
}
|
||||||
|
#elif defined(NN_WINDOWS)
|
||||||
|
if(request->action == NCL_VFS_OPENDIR) {
|
||||||
|
// FindFirstFileA needs a glob pattern, so we append "\\*" to the path.
|
||||||
|
// We allocate the wrapper struct on the heap because FindFirstFileA
|
||||||
|
// fills in the first result immediately and we need to remember that.
|
||||||
|
ncl_WinDir *dir = malloc(sizeof(ncl_WinDir));
|
||||||
|
if(dir == NULL) return false;
|
||||||
|
char searchPath[NN_MAX_PATH + 4];
|
||||||
|
snprintf(searchPath, sizeof(searchPath), "%s\\*", request->opendir.path);
|
||||||
|
dir->handle = FindFirstFileA(searchPath, &dir->findData);
|
||||||
|
if(dir->handle == INVALID_HANDLE_VALUE) {
|
||||||
|
free(dir);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
dir->isFirst = true;
|
||||||
|
request->opendir.dir = dir;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_CLOSEDIR) {
|
||||||
|
ncl_WinDir *dir = request->closedir;
|
||||||
|
FindClose(dir->handle);
|
||||||
|
free(dir);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_READDIR) {
|
||||||
|
// Mirrors the POSIX readdir loop: skip "." and "..",
|
||||||
|
// copy name into the caller buffer, signal end with NULL.
|
||||||
|
ncl_WinDir *dir = request->readdir.dir;
|
||||||
|
while(1) {
|
||||||
|
if(!dir->isFirst) {
|
||||||
|
if(!FindNextFileA(dir->handle, &dir->findData)) {
|
||||||
|
request->readdir.name = NULL;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir->isFirst = false;
|
||||||
|
if(strcmp(dir->findData.cFileName, ".") == 0 ||
|
||||||
|
strcmp(dir->findData.cFileName, "..") == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
strncpy(request->readdir.name, dir->findData.cFileName, NN_MAX_PATH - 1);
|
||||||
|
request->readdir.name[NN_MAX_PATH - 1] = '\0';
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_STAT) {
|
||||||
|
// Windows does not have st_blocks, so we approximate disk size
|
||||||
|
// as the file size itself. This is less accurate than the POSIX
|
||||||
|
// version but avoids needing the full Win32 file information API.
|
||||||
|
struct _stat s;
|
||||||
|
if(_stat(request->stat.path, &s) != 0) {
|
||||||
|
request->stat.path = NULL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ncl_Stat *st = request->stat.stat;
|
||||||
|
st->isDirectory = (s.st_mode & _S_IFDIR) != 0;
|
||||||
|
st->diskSize = s.st_size;
|
||||||
|
st->size = st->isDirectory ? 0 : s.st_size;
|
||||||
|
st->lastModified = s.st_mtime;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(request->action == NCL_VFS_MKDIR) {
|
||||||
|
// _mkdir on Windows does not take a permissions argument.
|
||||||
|
return _mkdir(request->mkdir) == 0;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return false; // not supported
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -15,15 +15,16 @@
|
|||||||
// to use the numerical accuracy better
|
// to use the numerical accuracy better
|
||||||
#define NN_COMPONENT_CALLBUDGET 10000
|
#define NN_COMPONENT_CALLBUDGET 10000
|
||||||
|
|
||||||
|
// NN_ATOMIC_NONE accepts 1 args, others 2
|
||||||
#ifdef NN_ATOMIC_NONE
|
#ifdef NN_ATOMIC_NONE
|
||||||
typedef size_t nn_refc_t;
|
typedef size_t nn_refc_t;
|
||||||
|
|
||||||
void nn_incRef(nn_refc_t *refc) {
|
void nn_incRef(nn_refc_t *refc, size_t n) {
|
||||||
(*refc)++;
|
(*refc) += n;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool nn_decRef(nn_refc_t *refc) {
|
bool nn_decRef(nn_refc_t *refc, size_t n) {
|
||||||
(*refc)--;
|
(*refc) -= n;
|
||||||
return (*refc) == 0;
|
return (*refc) == 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -87,8 +88,13 @@ 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) {
|
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize) {
|
||||||
if(memory == NULL) return nn_alloc(memory, newSize);
|
// nn_realloc passed memory (which is NULL here) as first argument
|
||||||
if(memory == ctx->alloc) return nn_alloc(memory, newSize);
|
// to nn_alloc instead of ctx. nn_alloc dereferences it as a context
|
||||||
|
// struct to call ctx->alloc(), so this is a NULL pointer dereference.
|
||||||
|
// Confirmed by test_realloc crashing on nn_realloc(&ctx, NULL, 0, 64).
|
||||||
|
// Original: if(memory == NULL) return nn_alloc(memory, newSize); if(memory == ctx->alloc) return nn_alloc(memory, newSize);
|
||||||
|
if(memory == NULL) return nn_alloc(ctx, newSize);
|
||||||
|
if(memory == ctx->alloc) return nn_alloc(ctx, newSize);
|
||||||
if(newSize == 0) {
|
if(newSize == 0) {
|
||||||
nn_free(ctx, memory, oldSize);
|
nn_free(ctx, memory, oldSize);
|
||||||
return ctx->alloc;
|
return ctx->alloc;
|
||||||
@@ -570,7 +576,8 @@ static void nn_defaultLock(void *state, nn_LockRequest *req) {
|
|||||||
#elif defined(NN_THREAD_WINDOWS)
|
#elif defined(NN_THREAD_WINDOWS)
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_LOCK_CREATE:;
|
case NN_LOCK_CREATE:;
|
||||||
req->lock = CreateMutex(NULL, false, NULL);
|
req->lock = CreateMutex(NULL, FALSE, NULL);
|
||||||
|
return; // don't fall into destroy
|
||||||
case NN_LOCK_DESTROY:;
|
case NN_LOCK_DESTROY:;
|
||||||
CloseHandle(req->lock);
|
CloseHandle(req->lock);
|
||||||
return;
|
return;
|
||||||
@@ -2226,8 +2233,8 @@ nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount) {
|
|||||||
// todo: everything
|
// todo: everything
|
||||||
|
|
||||||
const nn_EEPROM nn_defaultEEPROMs[4] = {
|
const nn_EEPROM nn_defaultEEPROMs[4] = {
|
||||||
(nn_EEPROM) {
|
NN_INIT(nn_EEPROM) {
|
||||||
.size = 4 * NN_KiB,
|
.size = 4 * NN_KiB,
|
||||||
.dataSize = 256,
|
.dataSize = 256,
|
||||||
.readEnergyCost = 1,
|
.readEnergyCost = 1,
|
||||||
.writeEnergyCost = 100,
|
.writeEnergyCost = 100,
|
||||||
@@ -2236,7 +2243,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeDelay = 2,
|
.writeDelay = 2,
|
||||||
.writeDataDelay = 1,
|
.writeDataDelay = 1,
|
||||||
},
|
},
|
||||||
(nn_EEPROM) {
|
NN_INIT(nn_EEPROM) {
|
||||||
.size = 8 * NN_KiB,
|
.size = 8 * NN_KiB,
|
||||||
.dataSize = 1 * NN_KiB,
|
.dataSize = 1 * NN_KiB,
|
||||||
.readEnergyCost = 2,
|
.readEnergyCost = 2,
|
||||||
@@ -2246,7 +2253,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeDelay = 2,
|
.writeDelay = 2,
|
||||||
.writeDataDelay = 1,
|
.writeDataDelay = 1,
|
||||||
},
|
},
|
||||||
(nn_EEPROM) {
|
NN_INIT(nn_EEPROM) {
|
||||||
.size = 16 * NN_KiB,
|
.size = 16 * NN_KiB,
|
||||||
.dataSize = 2 * NN_KiB,
|
.dataSize = 2 * NN_KiB,
|
||||||
.readEnergyCost = 4,
|
.readEnergyCost = 4,
|
||||||
@@ -2256,7 +2263,7 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
.writeDelay = 1,
|
.writeDelay = 1,
|
||||||
.writeDataDelay = 0.5,
|
.writeDataDelay = 0.5,
|
||||||
},
|
},
|
||||||
(nn_EEPROM) {
|
NN_INIT(nn_EEPROM) {
|
||||||
.size = 32 * NN_KiB,
|
.size = 32 * NN_KiB,
|
||||||
.dataSize = 4 * NN_KiB,
|
.dataSize = 4 * NN_KiB,
|
||||||
.readEnergyCost = 8,
|
.readEnergyCost = 8,
|
||||||
@@ -2269,25 +2276,25 @@ const nn_EEPROM nn_defaultEEPROMs[4] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nn_Filesystem nn_defaultFilesystems[4] = {
|
const nn_Filesystem nn_defaultFilesystems[4] = {
|
||||||
(nn_Filesystem) {
|
NN_INIT(nn_Filesystem) {
|
||||||
.spaceTotal = 1 * NN_MiB,
|
.spaceTotal = 1 * NN_MiB,
|
||||||
.readsPerTick = 4,
|
.readsPerTick = 4,
|
||||||
.writesPerTick = 2,
|
.writesPerTick = 2,
|
||||||
.dataEnergyCost = 256.0 / NN_MiB,
|
.dataEnergyCost = 256.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
(nn_Filesystem) {
|
NN_INIT(nn_Filesystem) {
|
||||||
.spaceTotal = 2 * NN_MiB,
|
.spaceTotal = 2 * NN_MiB,
|
||||||
.readsPerTick = 4,
|
.readsPerTick = 4,
|
||||||
.writesPerTick = 2,
|
.writesPerTick = 2,
|
||||||
.dataEnergyCost = 512.0 / NN_MiB,
|
.dataEnergyCost = 512.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
(nn_Filesystem) {
|
NN_INIT(nn_Filesystem) {
|
||||||
.spaceTotal = 4 * NN_MiB,
|
.spaceTotal = 4 * NN_MiB,
|
||||||
.readsPerTick = 7,
|
.readsPerTick = 7,
|
||||||
.writesPerTick = 3,
|
.writesPerTick = 3,
|
||||||
.dataEnergyCost = 1024.0 / NN_MiB,
|
.dataEnergyCost = 1024.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
(nn_Filesystem) {
|
NN_INIT(nn_Filesystem) {
|
||||||
.spaceTotal = 8 * NN_MiB,
|
.spaceTotal = 8 * NN_MiB,
|
||||||
.readsPerTick = 13,
|
.readsPerTick = 13,
|
||||||
.writesPerTick = 5,
|
.writesPerTick = 5,
|
||||||
@@ -2296,14 +2303,14 @@ const nn_Filesystem nn_defaultFilesystems[4] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const nn_Filesystem nn_defaultFloppy = (nn_Filesystem) {
|
const nn_Filesystem nn_defaultFloppy = NN_INIT(nn_Filesystem) {
|
||||||
.spaceTotal = 512 * NN_KiB,
|
.spaceTotal = 512 * NN_KiB,
|
||||||
.readsPerTick = 1,
|
.readsPerTick = 1,
|
||||||
.writesPerTick = 1,
|
.writesPerTick = 1,
|
||||||
.dataEnergyCost = 8.0 / NN_MiB,
|
.dataEnergyCost = 8.0 / NN_MiB,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nn_Filesystem nn_defaultTmpFS = (nn_Filesystem) {
|
const nn_Filesystem nn_defaultTmpFS = NN_INIT(nn_Filesystem) {
|
||||||
.spaceTotal = 64 * NN_KiB,
|
.spaceTotal = 64 * NN_KiB,
|
||||||
.readsPerTick = 1024,
|
.readsPerTick = 1024,
|
||||||
.writesPerTick = 512,
|
.writesPerTick = 512,
|
||||||
@@ -2311,7 +2318,7 @@ const nn_Filesystem nn_defaultTmpFS = (nn_Filesystem) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nn_Drive nn_defaultDrives[4] = {
|
const nn_Drive nn_defaultDrives[4] = {
|
||||||
(nn_Drive) {
|
NN_INIT(nn_Drive) {
|
||||||
.capacity = 1 * NN_MiB,
|
.capacity = 1 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 2,
|
.platterCount = 2,
|
||||||
@@ -2321,7 +2328,7 @@ const nn_Drive nn_defaultDrives[4] = {
|
|||||||
.onlySpinForwards = false,
|
.onlySpinForwards = false,
|
||||||
.dataEnergyCost = 256.0 / NN_MiB,
|
.dataEnergyCost = 256.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
(nn_Drive) {
|
NN_INIT(nn_Drive) {
|
||||||
.capacity = 2 * NN_MiB,
|
.capacity = 2 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 4,
|
.platterCount = 4,
|
||||||
@@ -2331,7 +2338,7 @@ const nn_Drive nn_defaultDrives[4] = {
|
|||||||
.onlySpinForwards = false,
|
.onlySpinForwards = false,
|
||||||
.dataEnergyCost = 512.0 / NN_MiB,
|
.dataEnergyCost = 512.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
(nn_Drive) {
|
NN_INIT(nn_Drive) {
|
||||||
.capacity = 4 * NN_MiB,
|
.capacity = 4 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 8,
|
.platterCount = 8,
|
||||||
@@ -2341,7 +2348,7 @@ const nn_Drive nn_defaultDrives[4] = {
|
|||||||
.onlySpinForwards = false,
|
.onlySpinForwards = false,
|
||||||
.dataEnergyCost = 1024.0 / NN_MiB,
|
.dataEnergyCost = 1024.0 / NN_MiB,
|
||||||
},
|
},
|
||||||
(nn_Drive) {
|
NN_INIT(nn_Drive) {
|
||||||
.capacity = 8 * NN_MiB,
|
.capacity = 8 * NN_MiB,
|
||||||
.sectorSize = 512,
|
.sectorSize = 512,
|
||||||
.platterCount = 16,
|
.platterCount = 16,
|
||||||
@@ -2366,7 +2373,7 @@ const nn_Drive nn_floppyDrive = {
|
|||||||
|
|
||||||
|
|
||||||
const nn_ScreenConfig nn_defaultScreens[4] = {
|
const nn_ScreenConfig nn_defaultScreens[4] = {
|
||||||
(nn_ScreenConfig) {
|
NN_INIT(nn_ScreenConfig) {
|
||||||
.maxWidth = 50,
|
.maxWidth = 50,
|
||||||
.maxHeight = 16,
|
.maxHeight = 16,
|
||||||
.maxDepth = 1,
|
.maxDepth = 1,
|
||||||
@@ -2375,7 +2382,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
|||||||
.editableColors = 0,
|
.editableColors = 0,
|
||||||
.features = NN_SCRF_NONE,
|
.features = NN_SCRF_NONE,
|
||||||
},
|
},
|
||||||
(nn_ScreenConfig) {
|
NN_INIT(nn_ScreenConfig) {
|
||||||
.maxWidth = 80,
|
.maxWidth = 80,
|
||||||
.maxHeight = 25,
|
.maxHeight = 25,
|
||||||
.maxDepth = 4,
|
.maxDepth = 4,
|
||||||
@@ -2384,7 +2391,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
|||||||
.editableColors = 0,
|
.editableColors = 0,
|
||||||
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED,
|
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED,
|
||||||
},
|
},
|
||||||
(nn_ScreenConfig) {
|
NN_INIT(nn_ScreenConfig) {
|
||||||
.maxWidth = 160,
|
.maxWidth = 160,
|
||||||
.maxHeight = 50,
|
.maxHeight = 50,
|
||||||
.maxDepth = 8,
|
.maxDepth = 8,
|
||||||
@@ -2393,7 +2400,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
|||||||
.editableColors = 16,
|
.editableColors = 16,
|
||||||
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS,
|
.features = NN_SCRF_MOUSE | NN_SCRF_TOUCHINVERTED | NN_SCRF_PRECISE | NN_SCRF_EDITABLECOLORS,
|
||||||
},
|
},
|
||||||
(nn_ScreenConfig) {
|
NN_INIT(nn_ScreenConfig) {
|
||||||
.maxWidth = 240,
|
.maxWidth = 240,
|
||||||
.maxHeight = 80,
|
.maxHeight = 80,
|
||||||
.maxDepth = 16,
|
.maxDepth = 16,
|
||||||
@@ -2405,7 +2412,7 @@ const nn_ScreenConfig nn_defaultScreens[4] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const nn_GPU nn_defaultGPUs[4] = {
|
const nn_GPU nn_defaultGPUs[4] = {
|
||||||
(nn_GPU) {
|
NN_INIT(nn_GPU) {
|
||||||
.maxWidth = 50,
|
.maxWidth = 50,
|
||||||
.maxHeight = 16,
|
.maxHeight = 16,
|
||||||
.maxDepth = 1,
|
.maxDepth = 1,
|
||||||
@@ -2418,7 +2425,7 @@ const nn_GPU nn_defaultGPUs[4] = {
|
|||||||
.energyPerWrite = 0.0002,
|
.energyPerWrite = 0.0002,
|
||||||
.energyPerClear = 0.0001,
|
.energyPerClear = 0.0001,
|
||||||
},
|
},
|
||||||
(nn_GPU) {
|
NN_INIT(nn_GPU) {
|
||||||
.maxWidth = 80,
|
.maxWidth = 80,
|
||||||
.maxHeight = 25,
|
.maxHeight = 25,
|
||||||
.maxDepth = 4,
|
.maxDepth = 4,
|
||||||
@@ -2431,7 +2438,7 @@ const nn_GPU nn_defaultGPUs[4] = {
|
|||||||
.energyPerWrite = 0.001,
|
.energyPerWrite = 0.001,
|
||||||
.energyPerClear = 0.0005,
|
.energyPerClear = 0.0005,
|
||||||
},
|
},
|
||||||
(nn_GPU) {
|
NN_INIT(nn_GPU) {
|
||||||
.maxWidth = 160,
|
.maxWidth = 160,
|
||||||
.maxHeight = 50,
|
.maxHeight = 50,
|
||||||
.maxDepth = 8,
|
.maxDepth = 8,
|
||||||
@@ -2444,7 +2451,7 @@ const nn_GPU nn_defaultGPUs[4] = {
|
|||||||
.energyPerWrite = 0.002,
|
.energyPerWrite = 0.002,
|
||||||
.energyPerClear = 0.001,
|
.energyPerClear = 0.001,
|
||||||
},
|
},
|
||||||
(nn_GPU) {
|
NN_INIT(nn_GPU) {
|
||||||
.maxWidth = 240,
|
.maxWidth = 240,
|
||||||
.maxHeight = 80,
|
.maxHeight = 80,
|
||||||
.maxDepth = 16,
|
.maxDepth = 16,
|
||||||
@@ -3049,10 +3056,12 @@ nn_Exit nn_pushDrop(nn_Computer *computer, const char *screenAddress, double x,
|
|||||||
return nn_pushSignal(computer, 6);
|
return nn_pushSignal(computer, 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the value is not returned for all execution paths - not a windows bug probably, need tests on *nix
|
||||||
nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player) {
|
nn_Exit nn_pushScroll(nn_Computer *computer, const char *screenAddress, double x, double y, double direction, const char *player) {
|
||||||
if(!nn_hasUser(computer, player)) return NN_OK;
|
if(!nn_hasUser(computer, player)) return NN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the value is not returned for all execution paths - not a windows bug probably, need tests on *nix
|
||||||
nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player) {
|
nn_Exit nn_pushWalk(nn_Computer *computer, const char *screenAddress, double x, double y, const char *player) {
|
||||||
if(!nn_hasUser(computer, player)) return NN_OK;
|
if(!nn_hasUser(computer, player)) return NN_OK;
|
||||||
}
|
}
|
||||||
@@ -3149,7 +3158,7 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
|
|||||||
}
|
}
|
||||||
if(method == NN_EENUM_GET) {
|
if(method == NN_EENUM_GET) {
|
||||||
ereq.action = NN_EEPROM_GET;
|
ereq.action = NN_EEPROM_GET;
|
||||||
char buf[eeprom.size];
|
NN_VLA(char, buf, eeprom.size);
|
||||||
ereq.buf = buf;
|
ereq.buf = buf;
|
||||||
ereq.buflen = eeprom.size;
|
ereq.buflen = eeprom.size;
|
||||||
e = state->handler(&ereq);
|
e = state->handler(&ereq);
|
||||||
@@ -3159,7 +3168,7 @@ static nn_Exit nn_eepromHandler(nn_ComponentRequest *req) {
|
|||||||
}
|
}
|
||||||
if(method == NN_EENUM_GETDATA) {
|
if(method == NN_EENUM_GETDATA) {
|
||||||
ereq.action = NN_EEPROM_GETDATA;
|
ereq.action = NN_EEPROM_GETDATA;
|
||||||
char buf[eeprom.size];
|
NN_VLA(char, buf, eeprom.size);
|
||||||
ereq.buf = buf;
|
ereq.buf = buf;
|
||||||
ereq.buflen = eeprom.size;
|
ereq.buflen = eeprom.size;
|
||||||
e = state->handler(&ereq);
|
e = state->handler(&ereq);
|
||||||
|
|||||||
@@ -9,21 +9,43 @@ extern "C" {
|
|||||||
// Used internally as well.
|
// Used internally as well.
|
||||||
// Based off https://stackoverflow.com/questions/5919996/how-to-detect-reliably-mac-os-x-ios-linux-windows-in-c-preprocessor
|
// 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__)
|
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
|
||||||
//define something for Windows (32-bit and 64-bit, this part is common)
|
#ifndef NN_WINDOWS
|
||||||
#define NN_WINDOWS
|
#define NN_WINDOWS
|
||||||
|
#endif
|
||||||
#elif __APPLE__
|
#elif __APPLE__
|
||||||
|
#ifndef NN_MACOS
|
||||||
#define NN_MACOS
|
#define NN_MACOS
|
||||||
|
#endif
|
||||||
#elif __linux__
|
#elif __linux__
|
||||||
|
#ifndef NN_LINUX
|
||||||
#define NN_LINUX
|
#define NN_LINUX
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __unix__ // all unices not caught above
|
#if __unix__
|
||||||
// Unix
|
#ifndef NN_UNIX
|
||||||
#define NN_UNIX
|
#define NN_UNIX
|
||||||
|
#endif
|
||||||
|
#ifndef NN_POSIX
|
||||||
#define NN_POSIX
|
#define NN_POSIX
|
||||||
|
#endif
|
||||||
#elif defined(_POSIX_VERSION)
|
#elif defined(_POSIX_VERSION)
|
||||||
// POSIX
|
#ifndef NN_POSIX
|
||||||
#define NN_POSIX
|
#define NN_POSIX
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) && !defined(__cplusplus)
|
||||||
|
#ifndef NN_ATOMIC_NONE
|
||||||
|
#define NN_ATOMIC_NONE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define NN_INIT(type)
|
||||||
|
#else
|
||||||
|
#define NN_INIT(type) (type)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// every C standard header we depend on, conveniently put here
|
// every C standard header we depend on, conveniently put here
|
||||||
@@ -31,6 +53,21 @@ extern "C" {
|
|||||||
#include <stdint.h> // for intptr_t
|
#include <stdint.h> // for intptr_t
|
||||||
#include <stdbool.h> // for true, false and bool
|
#include <stdbool.h> // 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 <malloc.h>, 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
|
// 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
|
// The entire NeoNucleus API, in one header file
|
||||||
@@ -40,7 +77,8 @@ extern "C" {
|
|||||||
#define NN_KiB (1024)
|
#define NN_KiB (1024)
|
||||||
#define NN_MiB (1024 * NN_KiB)
|
#define NN_MiB (1024 * NN_KiB)
|
||||||
#define NN_GiB (1024 * NN_MiB)
|
#define NN_GiB (1024 * NN_MiB)
|
||||||
#define NN_TiB (1024 * NN_TiB)
|
// probably recursive: #define NN_TiB (1024 * NN_TiB)
|
||||||
|
#define NN_TiB ((size_t)1024 * NN_GiB)
|
||||||
|
|
||||||
// the alignment an allocation should have
|
// the alignment an allocation should have
|
||||||
#define NN_ALLOC_ALIGN 16
|
#define NN_ALLOC_ALIGN 16
|
||||||
|
|||||||
BIN
src/neonucleus.obj
Normal file
BIN
src/neonucleus.obj
Normal file
Binary file not shown.
Reference in New Issue
Block a user