reworking a large part of the architecture
This commit is contained in:
8
TODO.md
8
TODO.md
@@ -1,6 +1,8 @@
|
|||||||
# For MVP functionality
|
# For MVP functionality
|
||||||
|
|
||||||
- stop doing linear scans and do hashmaps smh (ls /dev is awfully slow and this MIGHT be why)
|
- new component interface
|
||||||
|
- `ncomplib` with reference component implementations (depends on libc)
|
||||||
|
- the new component classes and implementations
|
||||||
- volatile filesystem
|
- volatile filesystem
|
||||||
- device info
|
- device info
|
||||||
|
|
||||||
@@ -44,7 +46,7 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
|
|||||||
- `speaker` component, allows playing audio by asking for binary samples and pushing a signal when it needs more
|
- `speaker` component, allows playing audio by asking for binary samples and pushing a signal when it needs more
|
||||||
- `microphone` component, allows reading audio from nearby sources
|
- `microphone` component, allows reading audio from nearby sources
|
||||||
- `tape_drive` component, compatible with Computronics
|
- `tape_drive` component, compatible with Computronics
|
||||||
- `cd_reader` and `cd_writer` components, to work with CDs
|
- `cd_drive` to work with CDs (can be read-only, write-only or read-write)
|
||||||
- `serial` component, for serial communications with other devices (USB?)
|
- `serial` component, for serial communications with other devices (USB?)
|
||||||
- `iron_noteblock` component
|
- `iron_noteblock` component
|
||||||
- `colorful_lamp` component
|
- `colorful_lamp` component
|
||||||
@@ -61,4 +63,4 @@ Not everything OC has (as a few of them are really MC-centered) but most of it.
|
|||||||
NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs.
|
NOTE: we're mostly bottlenecked by the architecture (typically a Lua VM) and the intentional bottlenecking from call costs.
|
||||||
|
|
||||||
- make signals use a circular buffer instead of a simple array
|
- make signals use a circular buffer instead of a simple array
|
||||||
- use a hashmap for components (and device info), this may require reworking how iterating over them is handled
|
- use more arenas if possible
|
||||||
|
|||||||
41
build.zig
41
build.zig
@@ -10,43 +10,20 @@ const LibBuildOpts = struct {
|
|||||||
|
|
||||||
fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
|
fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
|
||||||
const dataMod = b.createModule(.{
|
const dataMod = b.createModule(.{
|
||||||
.root_source_file = b.path("src/data.zig"),
|
|
||||||
.target = opts.target,
|
.target = opts.target,
|
||||||
.optimize = opts.optimize,
|
.optimize = opts.optimize,
|
||||||
.strip = if (opts.optimize == .Debug) false else true,
|
.strip = if (opts.optimize == .Debug) false else true,
|
||||||
.unwind_tables = if (opts.optimize == .Debug) null else .none,
|
.unwind_tables = if (opts.optimize == .Debug) null else .none,
|
||||||
.pic = true,
|
.pic = true,
|
||||||
|
.sanitize_c = .full,
|
||||||
});
|
});
|
||||||
|
|
||||||
const strict = opts.optimize != .Debug;
|
const strict = opts.optimize != .Debug;
|
||||||
|
|
||||||
dataMod.addCSourceFiles(.{
|
dataMod.addCSourceFiles(.{
|
||||||
.files = &[_][]const u8{
|
.files = &[_][]const u8{
|
||||||
"src/lock.c",
|
"src/neonucleus.c",
|
||||||
"src/utils.c",
|
"src/ncomplib.h",
|
||||||
"src/value.c",
|
|
||||||
"src/resource.c",
|
|
||||||
"src/component.c",
|
|
||||||
"src/computer.c",
|
|
||||||
"src/deviceInfo.c",
|
|
||||||
"src/universe.c",
|
|
||||||
"src/unicode.c",
|
|
||||||
// components
|
|
||||||
"src/components/eeprom.c",
|
|
||||||
"src/components/volatileEeprom.c",
|
|
||||||
"src/components/filesystem.c",
|
|
||||||
"src/components/volatileFilesystem.c",
|
|
||||||
"src/components/drive.c",
|
|
||||||
"src/components/volatileDrive.c",
|
|
||||||
"src/components/screen.c",
|
|
||||||
"src/components/gpu.c",
|
|
||||||
"src/components/keyboard.c",
|
|
||||||
"src/components/modem.c",
|
|
||||||
"src/components/loopbackModem.c",
|
|
||||||
"src/components/tunnel.c",
|
|
||||||
"src/components/loopbackTunnel.c",
|
|
||||||
"src/components/diskDrive.c",
|
|
||||||
"src/components/externalComputer.c",
|
|
||||||
},
|
},
|
||||||
.flags = &.{
|
.flags = &.{
|
||||||
if (opts.baremetal) "-DNN_BAREMETAL" else "",
|
if (opts.baremetal) "-DNN_BAREMETAL" else "",
|
||||||
@@ -61,14 +38,6 @@ fn addEngineSources(b: *std.Build, opts: LibBuildOpts) *std.Build.Module {
|
|||||||
|
|
||||||
if (!opts.baremetal) {
|
if (!opts.baremetal) {
|
||||||
dataMod.link_libc = true; // we need a libc
|
dataMod.link_libc = true; // we need a libc
|
||||||
dataMod.addCSourceFiles(.{
|
|
||||||
.files = &.{
|
|
||||||
"src/tinycthread.c",
|
|
||||||
},
|
|
||||||
.flags = &.{
|
|
||||||
"-fPIE",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dataMod.addIncludePath(b.path("src"));
|
dataMod.addIncludePath(b.path("src"));
|
||||||
@@ -213,8 +182,8 @@ pub fn build(b: *std.Build) !void {
|
|||||||
const luaVer = b.option(LuaVersion, "lua", "The version of Lua to use.") orelse LuaVersion.lua53;
|
const luaVer = b.option(LuaVersion, "lua", "The version of Lua to use.") orelse LuaVersion.lua53;
|
||||||
emulator.addCSourceFiles(.{
|
emulator.addCSourceFiles(.{
|
||||||
.files = &.{
|
.files = &.{
|
||||||
"src/testLuaArch.c",
|
"src/luaarch.c",
|
||||||
"src/emulator.c",
|
"src/main.c",
|
||||||
},
|
},
|
||||||
.flags = &.{
|
.flags = &.{
|
||||||
if (opts.baremetal) "-DNN_BAREMETAL" else "",
|
if (opts.baremetal) "-DNN_BAREMETAL" else "",
|
||||||
|
|||||||
@@ -280,9 +280,15 @@ fail:
|
|||||||
static int luaArch_component_list(lua_State *L) {
|
static int luaArch_component_list(lua_State *L) {
|
||||||
luaArch *arch = luaArch_from(L);
|
luaArch *arch = luaArch_from(L);
|
||||||
lua_createtable(L, 64, 0);
|
lua_createtable(L, 64, 0);
|
||||||
for(const char *addr = nn_getNextComponent(arch->computer, NULL); addr != NULL; addr = nn_getNextComponent(arch->computer, addr)) {
|
size_t len = nn_countComponents(arch->computer);
|
||||||
lua_pushstring(L, nn_getComponentType(arch->computer, addr));
|
const char *comps[len];
|
||||||
lua_setfield(L, -2, addr);
|
nn_getComponents(arch->computer, comps);
|
||||||
|
for(size_t i = 0; i < len; i++) {
|
||||||
|
nn_Component *c = nn_getComponent(arch->computer, comps[i]);
|
||||||
|
if(c != NULL) {
|
||||||
|
lua_pushstring(L, nn_getComponentType(nn_getComponent(arch->computer, comps[i])));
|
||||||
|
lua_setfield(L, -2, comps[i]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -293,22 +299,11 @@ static int luaArch_component_invoke(lua_State *L) {
|
|||||||
const char *method = luaL_checkstring(L, 2);
|
const char *method = luaL_checkstring(L, 2);
|
||||||
size_t argc = lua_gettop(L);
|
size_t argc = lua_gettop(L);
|
||||||
|
|
||||||
if(!nn_hasComponent(arch->computer, address)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, "no such component");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
if(!nn_hasMethod(arch->computer, address, method)) {
|
|
||||||
lua_pushnil(L);
|
|
||||||
lua_pushstring(L, "no such method");
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
nn_clearstack(arch->computer);
|
nn_clearstack(arch->computer);
|
||||||
for(size_t i = 3; i <= argc; i++) {
|
for(size_t i = 3; i <= argc; i++) {
|
||||||
luaArch_luaToNN(arch, L, i);
|
luaArch_luaToNN(arch, L, i);
|
||||||
}
|
}
|
||||||
nn_Exit err = nn_call(arch->computer, address, method);
|
nn_Exit err = nn_invokeComponent(arch->computer, address, method);
|
||||||
if(err != NN_OK) {
|
if(err != NN_OK) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, nn_getError(arch->computer));
|
lua_pushstring(L, nn_getError(arch->computer));
|
||||||
@@ -326,12 +321,13 @@ static int luaArch_component_type(lua_State *L) {
|
|||||||
luaArch *arch = luaArch_from(L);
|
luaArch *arch = luaArch_from(L);
|
||||||
const char *address = luaL_checkstring(L, 1);
|
const char *address = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
if(!nn_hasComponent(arch->computer, address)) {
|
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||||
|
if(c == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "no such component");
|
lua_pushstring(L, "no such component");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
lua_pushstring(L, nn_getComponentType(arch->computer, address));
|
lua_pushstring(L, nn_getComponentType(c));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -340,17 +336,19 @@ static int luaArch_component_doc(lua_State *L) {
|
|||||||
const char *address = luaL_checkstring(L, 1);
|
const char *address = luaL_checkstring(L, 1);
|
||||||
const char *method = luaL_checkstring(L, 2);
|
const char *method = luaL_checkstring(L, 2);
|
||||||
|
|
||||||
if(!nn_hasComponent(arch->computer, address)) {
|
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||||
|
if(c == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "no such component");
|
lua_pushstring(L, "no such component");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if(!nn_hasMethod(arch->computer, address, method)) {
|
const char *doc = nn_getComponentDoc(c, method);
|
||||||
|
if(doc == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "no such method");
|
lua_pushstring(L, "no such method");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
lua_pushstring(L, nn_getComponentDoc(arch->computer, address, method));
|
lua_pushstring(L, doc);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +356,7 @@ static int luaArch_component_slot(lua_State *L) {
|
|||||||
luaArch *arch = luaArch_from(L);
|
luaArch *arch = luaArch_from(L);
|
||||||
const char *address = luaL_checkstring(L, 1);
|
const char *address = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
if(!nn_hasComponent(arch->computer, address)) {
|
if(nn_getComponent(arch->computer, address) == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "no such component");
|
lua_pushstring(L, "no such component");
|
||||||
return 2;
|
return 2;
|
||||||
@@ -371,18 +369,23 @@ static int luaArch_component_methods(lua_State *L) {
|
|||||||
luaArch *arch = luaArch_from(L);
|
luaArch *arch = luaArch_from(L);
|
||||||
const char *address = luaL_checkstring(L, 1);
|
const char *address = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
if(!nn_hasComponent(arch->computer, address)) {
|
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||||
|
|
||||||
|
if(c == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "no such component");
|
lua_pushstring(L, "no such component");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
const nn_Method *method = nn_nextComponentMethod(arch->computer, address, NULL);
|
size_t methodLen = nn_countComponentMethods(c);
|
||||||
lua_createtable(L, 0, 0);
|
const char *methods[methodLen];
|
||||||
for(; method != NULL; method = nn_nextComponentMethod(arch->computer, address, method)) {
|
nn_getComponentMethods(c, methods, &methodLen);
|
||||||
if(method->flags & NN_FIELD_MASK) continue; // skip
|
lua_createtable(L, 0, methodLen);
|
||||||
|
for(size_t i = 0; i < methodLen; i++) {
|
||||||
|
nn_MethodFlags flags = nn_getComponentMethodFlags(c, methods[i]);
|
||||||
|
if(flags & NN_FIELD_MASK) continue; // skip
|
||||||
|
|
||||||
lua_pushboolean(L, (method->flags & NN_DIRECT) != 0);
|
lua_pushboolean(L, (flags & NN_DIRECT) != 0);
|
||||||
lua_setfield(L, -2, method->name);
|
lua_setfield(L, -2, methods[i]);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -391,24 +394,29 @@ static int luaArch_component_fields(lua_State *L) {
|
|||||||
luaArch *arch = luaArch_from(L);
|
luaArch *arch = luaArch_from(L);
|
||||||
const char *address = luaL_checkstring(L, 1);
|
const char *address = luaL_checkstring(L, 1);
|
||||||
|
|
||||||
if(!nn_hasComponent(arch->computer, address)) {
|
nn_Component *c = nn_getComponent(arch->computer, address);
|
||||||
|
|
||||||
|
if(c == NULL) {
|
||||||
lua_pushnil(L);
|
lua_pushnil(L);
|
||||||
lua_pushstring(L, "no such component");
|
lua_pushstring(L, "no such component");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
const nn_Method *method = nn_nextComponentMethod(arch->computer, address, NULL);
|
size_t methodLen = nn_countComponentMethods(c);
|
||||||
lua_createtable(L, 0, 0);
|
const char *methods[methodLen];
|
||||||
for(; method != NULL; method = nn_nextComponentMethod(arch->computer, address, method)) {
|
nn_getComponentMethods(c, methods, &methodLen);
|
||||||
if((method->flags & NN_FIELD_MASK) == 0) continue; // skip
|
lua_createtable(L, 0, methodLen);
|
||||||
|
for(size_t i = 0; i < methodLen; i++) {
|
||||||
|
nn_MethodFlags flags = nn_getComponentMethodFlags(c, methods[i]);
|
||||||
|
if((flags & NN_FIELD_MASK) == 0) continue; // skip
|
||||||
|
|
||||||
lua_createtable(L, 0, 3);
|
lua_createtable(L, 0, 3);
|
||||||
lua_pushboolean(L, (method->flags & NN_DIRECT) != 0);
|
lua_pushboolean(L, (flags & NN_DIRECT) != 0);
|
||||||
lua_setfield(L, -2, "direct");
|
lua_setfield(L, -2, "direct");
|
||||||
lua_pushboolean(L, (method->flags & NN_GETTER) != 0);
|
lua_pushboolean(L, (flags & NN_GETTER) != 0);
|
||||||
lua_setfield(L, -2, "getter");
|
lua_setfield(L, -2, "getter");
|
||||||
lua_pushboolean(L, (method->flags & NN_SETTER) != 0);
|
lua_pushboolean(L, (flags & NN_SETTER) != 0);
|
||||||
lua_setfield(L, -2, "setter");
|
lua_setfield(L, -2, "setter");
|
||||||
lua_setfield(L, -2, method->name);
|
lua_setfield(L, -2, methods[i]);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@@ -593,7 +601,11 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
|||||||
arch = nn_alloc(ctx, sizeof(*arch));
|
arch = nn_alloc(ctx, sizeof(*arch));
|
||||||
arch->freeMem = nn_getTotalMemory(computer) * nn_getMemoryScale(computer);
|
arch->freeMem = nn_getTotalMemory(computer) * nn_getMemoryScale(computer);
|
||||||
arch->computer = computer;
|
arch->computer = computer;
|
||||||
|
#if LUA_VERSION_NUM >= 505L
|
||||||
|
lua_State *L = lua_newstate(luaArch_alloc, arch, rand());
|
||||||
|
#else
|
||||||
lua_State *L = lua_newstate(luaArch_alloc, arch);
|
lua_State *L = lua_newstate(luaArch_alloc, arch);
|
||||||
|
#endif
|
||||||
arch->L = L;
|
arch->L = L;
|
||||||
req->localState = arch;
|
req->localState = arch;
|
||||||
luaL_openlibs(L);
|
luaL_openlibs(L);
|
||||||
@@ -613,7 +625,12 @@ static nn_Exit luaArch_handler(nn_ArchitectureRequest *req) {
|
|||||||
case NN_ARCH_TICK:;
|
case NN_ARCH_TICK:;
|
||||||
lua_settop(arch->L, 1);
|
lua_settop(arch->L, 1);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
#if LUA_VERSION_NUM >= 504L
|
||||||
int res = lua_resume(arch->L, NULL, 0, &ret);
|
int res = lua_resume(arch->L, NULL, 0, &ret);
|
||||||
|
#else
|
||||||
|
int res = lua_resume(arch->L, NULL, 0);
|
||||||
|
ret = lua_gettop(arch->L);
|
||||||
|
#endif
|
||||||
//printf("res: %d\n", res);
|
//printf("res: %d\n", res);
|
||||||
if(res == LUA_OK) {
|
if(res == LUA_OK) {
|
||||||
// halted, fuck
|
// halted, fuck
|
||||||
|
|||||||
777
src/main.c
777
src/main.c
@@ -4,7 +4,6 @@
|
|||||||
// Error handling has been omitted in most places.
|
// Error handling has been omitted in most places.
|
||||||
|
|
||||||
#include "neonucleus.h"
|
#include "neonucleus.h"
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -13,57 +12,6 @@
|
|||||||
|
|
||||||
nn_Architecture getLuaArch();
|
nn_Architecture getLuaArch();
|
||||||
|
|
||||||
#if defined(NN_WINDOWS)
|
|
||||||
#define NE_PATHSEP '\\'
|
|
||||||
#include <windows.h>
|
|
||||||
#error "Windows is not supported yet"
|
|
||||||
#elif defined(NN_POSIX)
|
|
||||||
#define NE_PATHSEP '/'
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
typedef DIR ne_dir;
|
|
||||||
|
|
||||||
ne_dir *ne_opendir(const char *path) {
|
|
||||||
return opendir(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ne_closedir(ne_dir *dir) {
|
|
||||||
closedir(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ne_readdir(ne_dir *dir, char path[NN_MAX_PATH]) {
|
|
||||||
struct dirent *ent = readdir(dir);
|
|
||||||
if(ent == NULL) return true;
|
|
||||||
strncpy(path, ent->d_name, NN_MAX_PATH-1);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ne_exists(const char *path) {
|
|
||||||
return access(path, F_OK) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ne_sizeAt(const char *path) {
|
|
||||||
struct stat buf;
|
|
||||||
if(stat(path, &buf) != 0) return 0;
|
|
||||||
if(S_ISDIR(buf.st_mode)) return 0;
|
|
||||||
return buf.st_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ne_isDirectory(const char *path) {
|
|
||||||
struct stat buf;
|
|
||||||
if(stat(path, &buf) != 0) return false;
|
|
||||||
return S_ISDIR(buf.st_mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t ne_lastModified(const char *path) {
|
|
||||||
struct stat buf;
|
|
||||||
if(stat(path, &buf) != 0) return 0;
|
|
||||||
return buf.st_mtime;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static const char minBIOS[] = {
|
static const char minBIOS[] = {
|
||||||
#embed "minBIOS.lua"
|
#embed "minBIOS.lua"
|
||||||
,'\0'
|
,'\0'
|
||||||
@@ -72,11 +20,7 @@ static const char minBIOS[] = {
|
|||||||
static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
||||||
nn_Computer *c = req->computer;
|
nn_Computer *c = req->computer;
|
||||||
switch(req->action) {
|
switch(req->action) {
|
||||||
case NN_COMP_INIT:
|
case NN_COMP_INVOKE:
|
||||||
return NN_OK;
|
|
||||||
case NN_COMP_DEINIT:
|
|
||||||
return NN_OK;
|
|
||||||
case NN_COMP_CALL:
|
|
||||||
if(nn_getstacksize(c) != 1) {
|
if(nn_getstacksize(c) != 1) {
|
||||||
nn_setError(c, "bad argument count");
|
nn_setError(c, "bad argument count");
|
||||||
return NN_EBADCALL;
|
return NN_EBADCALL;
|
||||||
@@ -84,657 +28,12 @@ static nn_Exit sandbox_handler(nn_ComponentRequest *req) {
|
|||||||
const char *s = nn_tostring(c, 0);
|
const char *s = nn_tostring(c, 0);
|
||||||
puts(s);
|
puts(s);
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
case NN_COMP_ENABLED:
|
case NN_COMP_CHECKMETHOD:
|
||||||
req->methodEnabled = true; // all methods always enabled
|
req->methodEnabled = true; // all methods always enabled
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
case NN_COMP_FREETYPE:
|
case NN_COMP_DROP:
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
case NN_COMP_SIGNAL:
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct ne_FsState {
|
|
||||||
char path[NN_MAX_PATH];
|
|
||||||
bool isReadonly;
|
|
||||||
FILE *files[NN_MAX_OPENFILES];
|
|
||||||
ne_dir *dir;
|
|
||||||
} ne_FsState;
|
|
||||||
|
|
||||||
void ne_fsState_truepath(ne_FsState *state, char truepath[NN_MAX_PATH], const char *path) {
|
|
||||||
snprintf(truepath, sizeof(char) * NN_MAX_PATH, "%s%c%s", state->path, NE_PATHSEP, path);
|
|
||||||
for(size_t i = 0; truepath[i] != 0; i++) {
|
|
||||||
if(truepath[i] == '/') truepath[i] = NE_PATHSEP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nn_Exit ne_fsState_handler(nn_FilesystemRequest *req) {
|
|
||||||
nn_Computer *C = req->computer;
|
|
||||||
ne_FsState *state = req->instance;
|
|
||||||
FILE *f;
|
|
||||||
char truepath[NN_MAX_PATH];
|
|
||||||
|
|
||||||
switch(req->action) {
|
|
||||||
case NN_FS_FREE:
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_DROP:
|
|
||||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
|
||||||
if(state->files[i] != NULL) fclose(state->files[i]);
|
|
||||||
}
|
|
||||||
if(state->dir != NULL) {
|
|
||||||
ne_closedir(state->dir);
|
|
||||||
}
|
|
||||||
free(state);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_SPACEUSED:
|
|
||||||
req->size = 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_GETLABEL:
|
|
||||||
req->strarg1 = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_SETLABEL:
|
|
||||||
req->strarg1 = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_OPEN:;
|
|
||||||
req->fd = NN_MAX_OPENFILES;
|
|
||||||
|
|
||||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
|
||||||
if(state->files[i] == NULL) {
|
|
||||||
req->fd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(req->fd == NN_MAX_OPENFILES) {
|
|
||||||
nn_setError(C, "too many open handles");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *path = req->strarg1;
|
|
||||||
const char *mode = req->strarg2;
|
|
||||||
switch(mode[0]) {
|
|
||||||
case 'r':
|
|
||||||
mode = "rb";
|
|
||||||
break;
|
|
||||||
case 'w':
|
|
||||||
mode = "wb";
|
|
||||||
break;
|
|
||||||
case 'a':
|
|
||||||
mode = "ab";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mode = "rb";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ne_fsState_truepath(state, truepath, path);
|
|
||||||
|
|
||||||
f = fopen(truepath, mode);
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_setError(C, strerror(errno));
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
state->files[req->fd] = f;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_CLOSE:
|
|
||||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
f = state->files[req->fd];
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
fclose(f);
|
|
||||||
state->files[req->fd] = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_READ:
|
|
||||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
f = state->files[req->fd];
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
if(feof(f)) {
|
|
||||||
req->strarg1 = NULL;
|
|
||||||
} else {
|
|
||||||
req->strarg1len = fread(req->strarg1, sizeof(char), req->strarg1len, f);
|
|
||||||
}
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_WRITE:
|
|
||||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
f = state->files[req->fd];
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
fwrite(req->strarg1, sizeof(char), req->strarg1len, f);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_SEEK:
|
|
||||||
if(req->fd < 0 || req->fd >= NN_MAX_OPENFILES) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
f = state->files[req->fd];
|
|
||||||
if(f == NULL) {
|
|
||||||
nn_setError(C, "bad file descriptor");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
int whence = SEEK_SET;
|
|
||||||
if(req->whence == NN_SEEK_CUR) {
|
|
||||||
whence = SEEK_CUR;
|
|
||||||
} else if(req->whence == NN_SEEK_END) {
|
|
||||||
whence = SEEK_END;
|
|
||||||
}
|
|
||||||
fseek(f, req->off, whence);
|
|
||||||
req->off = ftell(f);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_OPENDIR:
|
|
||||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
|
||||||
state->dir = ne_opendir(truepath);
|
|
||||||
if(state->dir == NULL) {
|
|
||||||
nn_setError(C, strerror(errno));
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_READDIR:;
|
|
||||||
char ent[NN_MAX_PATH];
|
|
||||||
if(ne_readdir(state->dir, ent)) {
|
|
||||||
req->strarg1 = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
strcpy(req->strarg1, ent);
|
|
||||||
req->strarg1len = strlen(ent);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_CLOSEDIR:
|
|
||||||
ne_closedir(state->dir);
|
|
||||||
state->dir = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_EXISTS:
|
|
||||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
|
||||||
req->size = ne_exists(truepath) ? 1 : 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_SIZE:
|
|
||||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
|
||||||
if(!ne_exists(truepath)) {
|
|
||||||
nn_setError(C, "no such file or directory");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
req->size = ne_sizeAt(truepath);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_LASTMODIFIED:
|
|
||||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
|
||||||
if(!ne_exists(truepath)) {
|
|
||||||
nn_setError(C, "no such file or directory");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
req->size = ne_lastModified(truepath);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_ISREADONLY:
|
|
||||||
req->size = state->isReadonly ? 1 : 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_FS_ISDIRECTORY:
|
|
||||||
ne_fsState_truepath(state, truepath, req->strarg1);
|
|
||||||
if(!ne_exists(truepath)) {
|
|
||||||
nn_setError(C, "no such file or directory");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
req->size = ne_isDirectory(truepath) ? 1 : 0;
|
|
||||||
return NN_OK;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
nn_setError(C, "not implemented");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
|
|
||||||
ne_FsState *ne_newFS(const char *path, bool readonly) {
|
|
||||||
ne_FsState *fs = malloc(sizeof(*fs));
|
|
||||||
for(size_t i = 0; i < NN_MAX_OPENFILES; i++) {
|
|
||||||
fs->files[i] = NULL;
|
|
||||||
}
|
|
||||||
sprintf(fs->path, "data%c%s", NE_PATHSEP, path);
|
|
||||||
fs->isReadonly = readonly;
|
|
||||||
fs->dir = NULL;
|
|
||||||
return fs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this struct is quite wasteful and could be made like 10x better
|
|
||||||
// for performance. But like, this test emulator is ahh anyways
|
|
||||||
typedef struct ne_Pixel {
|
|
||||||
int fg;
|
|
||||||
int bg;
|
|
||||||
int truefg;
|
|
||||||
int truebg;
|
|
||||||
nn_codepoint codepoint;
|
|
||||||
bool isFgPalette;
|
|
||||||
bool isBgPalette;
|
|
||||||
} ne_Pixel;
|
|
||||||
|
|
||||||
typedef struct ne_ScreenBuffer {
|
|
||||||
int maxWidth;
|
|
||||||
int maxHeight;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
char depth;
|
|
||||||
char maxDepth;
|
|
||||||
ne_Pixel *pixels;
|
|
||||||
int maxPalette;
|
|
||||||
int editableColors;
|
|
||||||
int *virtualPalette;
|
|
||||||
int *mappedPalette;
|
|
||||||
const char *keyboard;
|
|
||||||
} ne_ScreenBuffer;
|
|
||||||
|
|
||||||
bool ne_ocCompatibleColors = true;
|
|
||||||
|
|
||||||
void ne_remapScreen(ne_ScreenBuffer *buf) {
|
|
||||||
int depth = buf->depth;
|
|
||||||
|
|
||||||
for(int i = 0; i < buf->maxPalette; i++) {
|
|
||||||
buf->mappedPalette[i] = nn_mapDepth(buf->virtualPalette[i], depth, ne_ocCompatibleColors);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int y = 0; y < buf->height; y++) {
|
|
||||||
for(int x = 0; x < buf->width; x++) {
|
|
||||||
ne_Pixel *pixel = &buf->pixels[y * buf->maxWidth + x];
|
|
||||||
int virtfg = pixel->fg, virtbg = pixel->bg;
|
|
||||||
if(pixel->isFgPalette) virtfg = buf->mappedPalette[virtfg];
|
|
||||||
else virtfg = nn_mapDepth(virtfg, depth, ne_ocCompatibleColors);
|
|
||||||
if(pixel->isBgPalette) virtbg = buf->mappedPalette[virtbg];
|
|
||||||
else virtbg = nn_mapDepth(virtbg, depth, ne_ocCompatibleColors);
|
|
||||||
|
|
||||||
pixel->truefg = virtfg;
|
|
||||||
pixel->truebg = virtbg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ne_ScreenBuffer *ne_newScreenBuf(nn_ScreenConfig conf, const char *keyboard) {
|
|
||||||
ne_ScreenBuffer *buf = malloc(sizeof(*buf));
|
|
||||||
buf->maxWidth = conf.maxWidth;
|
|
||||||
buf->maxHeight = conf.maxHeight;
|
|
||||||
buf->width = buf->maxWidth;
|
|
||||||
buf->height = buf->maxHeight;
|
|
||||||
buf->maxDepth = conf.maxDepth;
|
|
||||||
buf->depth = buf->maxDepth;
|
|
||||||
buf->maxPalette = conf.paletteColors;
|
|
||||||
buf->pixels = malloc(sizeof(ne_Pixel) * conf.maxWidth * conf.maxHeight);
|
|
||||||
buf->virtualPalette = malloc(sizeof(int) * conf.paletteColors);
|
|
||||||
memset(buf->virtualPalette, 0, sizeof(int) * buf->maxPalette);
|
|
||||||
buf->mappedPalette = malloc(sizeof(int) * conf.paletteColors);
|
|
||||||
buf->keyboard = keyboard;
|
|
||||||
|
|
||||||
int *palette = NULL;
|
|
||||||
if(buf->maxDepth == 4) {
|
|
||||||
palette = nn_mcpalette4;
|
|
||||||
}
|
|
||||||
if(buf->maxDepth == 8) {
|
|
||||||
palette = nn_ocpalette8;
|
|
||||||
}
|
|
||||||
if(palette) memcpy(buf->virtualPalette, palette, sizeof(int) * buf->maxPalette);
|
|
||||||
memcpy(buf->mappedPalette, buf->virtualPalette, sizeof(int) * buf->maxPalette);
|
|
||||||
|
|
||||||
for(int y = 0; y < buf->height; y++) {
|
|
||||||
for(int x = 0; x < buf->width; x++) {
|
|
||||||
buf->pixels[y * buf->width + x] = (ne_Pixel) {
|
|
||||||
.fg = 0xFFFFFF,
|
|
||||||
.bg = 0x000000,
|
|
||||||
.isFgPalette = false,
|
|
||||||
.isBgPalette = false,
|
|
||||||
.codepoint = ' ',
|
|
||||||
.truefg = 0xFFFFFF,
|
|
||||||
.truebg = 0x000000,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ne_dropScreenBuf(ne_ScreenBuffer *buf) {
|
|
||||||
free(buf->pixels);
|
|
||||||
free(buf->mappedPalette);
|
|
||||||
free(buf->virtualPalette);
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
ne_Pixel defaultPixel = {
|
|
||||||
.codepoint = ' ',
|
|
||||||
.fg = 0xFFFFFF,
|
|
||||||
.bg = 0x000000,
|
|
||||||
.isFgPalette = false,
|
|
||||||
.isBgPalette = false,
|
|
||||||
.truefg = 0xFFFFFF,
|
|
||||||
.truebg = 0x000000,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool ne_inScreenBuf(ne_ScreenBuffer *buf, int x, int y) {
|
|
||||||
return x > 0 && y > 0 && x <= buf->width && y <= buf->height;
|
|
||||||
}
|
|
||||||
|
|
||||||
ne_Pixel ne_getPixel(ne_ScreenBuffer *buf, int x, int y) {
|
|
||||||
if(!ne_inScreenBuf(buf, x, y)) return defaultPixel;
|
|
||||||
x--;
|
|
||||||
y--;
|
|
||||||
return buf->pixels[y * buf->maxWidth + x];
|
|
||||||
}
|
|
||||||
|
|
||||||
void ne_setPixel(ne_ScreenBuffer *buf, int x, int y, ne_Pixel pixel) {
|
|
||||||
if(!ne_inScreenBuf(buf, x, y)) return;
|
|
||||||
x--;
|
|
||||||
y--;
|
|
||||||
buf->pixels[y * buf->maxWidth + x] = pixel;
|
|
||||||
}
|
|
||||||
|
|
||||||
nn_Exit ne_screen_handler(nn_ScreenRequest *req) {
|
|
||||||
ne_ScreenBuffer *buf = req->instance;
|
|
||||||
switch(req->action) {
|
|
||||||
case NN_SCR_DROP:
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_FREE:
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_GETASPECTRATIO:
|
|
||||||
req->w = 1;
|
|
||||||
req->h = 1;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_GETKEYBOARD:
|
|
||||||
if(buf->keyboard == NULL) {
|
|
||||||
req->keyboard = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
if(req->h != 0) {
|
|
||||||
req->keyboard = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
size_t keylen = strlen(buf->keyboard);
|
|
||||||
if(keylen > req->w) keylen = req->w;
|
|
||||||
memcpy(req->keyboard, buf->keyboard, keylen);
|
|
||||||
req->w = keylen;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_ISON:
|
|
||||||
req->w = 1;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_TURNON:
|
|
||||||
req->w = 1;
|
|
||||||
req->h = 1;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_TURNOFF:
|
|
||||||
req->w = 1;
|
|
||||||
req->h = 1;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_ISPRECISE:
|
|
||||||
req->w = 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_SETPRECISE:
|
|
||||||
req->w = 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_ISTOUCHINVERTED:
|
|
||||||
req->w = 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_SCR_SETTOUCHINVERTED:
|
|
||||||
req->w = 0;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define NE_MAX_VRAMBUF 16
|
|
||||||
|
|
||||||
typedef struct ne_GPUState {
|
|
||||||
ne_ScreenBuffer *screenBuf;
|
|
||||||
int currentFg;
|
|
||||||
int currentBg;
|
|
||||||
bool isFgPalette;
|
|
||||||
bool isBgPalette;
|
|
||||||
int usedMemory;
|
|
||||||
int activeBuffer;
|
|
||||||
int scrAddrLen;
|
|
||||||
char scrAddr[NN_MAX_ADDRESS];
|
|
||||||
ne_ScreenBuffer *vramBufs[NE_MAX_VRAMBUF];
|
|
||||||
} ne_GPUState;
|
|
||||||
|
|
||||||
ne_GPUState *ne_newGPU() {
|
|
||||||
ne_GPUState *state = malloc(sizeof(*state));
|
|
||||||
state->screenBuf = NULL;
|
|
||||||
state->currentFg = 0xFFFFFF;
|
|
||||||
state->currentBg = 0x000000;
|
|
||||||
state->isFgPalette = false;
|
|
||||||
state->isBgPalette = false;
|
|
||||||
state->activeBuffer = 0;
|
|
||||||
state->usedMemory = 0;
|
|
||||||
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
|
||||||
state->vramBufs[i] = NULL;
|
|
||||||
}
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
ne_ScreenBuffer *ne_gpu_currentBuffer(ne_GPUState *state) {
|
|
||||||
if(state->activeBuffer == 0) return state->screenBuf;
|
|
||||||
return state->vramBufs[state->activeBuffer - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
nn_Exit ne_gpu_handler(nn_GPURequest *req) {
|
|
||||||
// completely rewrite this buggy and incomplete mess.
|
|
||||||
nn_Computer *C = req->computer;
|
|
||||||
ne_GPUState *state = req->instance;
|
|
||||||
|
|
||||||
int maxWidth = req->gpuConf->maxWidth;
|
|
||||||
int maxHeight = req->gpuConf->maxHeight;
|
|
||||||
int maxDepth = req->gpuConf->maxDepth;
|
|
||||||
|
|
||||||
ne_ScreenBuffer *activeBuf = state == NULL ? NULL : ne_gpu_currentBuffer(state);
|
|
||||||
|
|
||||||
if(state != NULL && state->screenBuf != NULL) {
|
|
||||||
ne_ScreenBuffer *buf = state->screenBuf;
|
|
||||||
if(maxWidth > buf->maxWidth) maxWidth = buf->maxWidth;
|
|
||||||
if(maxHeight > buf->maxHeight) maxHeight = buf->maxHeight;
|
|
||||||
if(maxDepth > buf->maxDepth) maxDepth = buf->maxDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x, y, dx, dy, w, h, fg, bg;
|
|
||||||
ne_Pixel p;
|
|
||||||
|
|
||||||
switch(req->action) {
|
|
||||||
case NN_GPU_DROP:
|
|
||||||
for(int i = 0; i < NE_MAX_VRAMBUF; i++) {
|
|
||||||
ne_ScreenBuffer *buf = state->vramBufs[i];
|
|
||||||
if(buf != NULL) ne_dropScreenBuf(buf);
|
|
||||||
}
|
|
||||||
free(state);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_FREE:
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_BIND:
|
|
||||||
state->screenBuf = nn_getComponentUserdata(C, req->text);
|
|
||||||
memcpy(state->scrAddr, req->text, req->width);
|
|
||||||
state->scrAddrLen = req->width;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_UNBIND:
|
|
||||||
state->screenBuf = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_GETSCREEN:
|
|
||||||
if(state->screenBuf == NULL) {
|
|
||||||
req->text = NULL;
|
|
||||||
return NN_OK;
|
|
||||||
}
|
|
||||||
memcpy(req->text, state->scrAddr, state->scrAddrLen);
|
|
||||||
req->width = state->scrAddrLen;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_GET:
|
|
||||||
if(activeBuf == NULL) {
|
|
||||||
nn_setError(C, "no screen");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
p = ne_getPixel(activeBuf, req->x, req->y);
|
|
||||||
fg = p.fg;
|
|
||||||
bg = p.bg;
|
|
||||||
if(p.isFgPalette) fg = activeBuf->virtualPalette[fg];
|
|
||||||
if(p.isBgPalette) bg = activeBuf->virtualPalette[bg];
|
|
||||||
|
|
||||||
req->codepoint = p.codepoint;
|
|
||||||
req->width = fg;
|
|
||||||
req->height = bg;
|
|
||||||
req->dest = p.isFgPalette ? p.fg : -1;
|
|
||||||
req->src = p.isBgPalette ? p.bg : -1;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_SET:
|
|
||||||
case NN_GPU_SETVERTICAL:
|
|
||||||
if(activeBuf == NULL) {
|
|
||||||
nn_setError(C, "no screen");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
dx = 1;
|
|
||||||
dy = 0;
|
|
||||||
if(req->action == NN_GPU_SETVERTICAL) dx = 0, dy = 1;
|
|
||||||
|
|
||||||
x = req->x;
|
|
||||||
y = req->y;
|
|
||||||
const char *s = req->text;
|
|
||||||
for(int i = 0; i < req->width;) {
|
|
||||||
if(!ne_inScreenBuf(activeBuf, x, y)) break;
|
|
||||||
size_t w = nn_unicode_validateFirstChar(s + i, req->width - i);
|
|
||||||
ne_Pixel p = {
|
|
||||||
.fg = state->currentFg,
|
|
||||||
.bg = state->currentBg,
|
|
||||||
.isFgPalette = state->isFgPalette,
|
|
||||||
.isBgPalette = state->isBgPalette,
|
|
||||||
.codepoint = (unsigned char)s[i],
|
|
||||||
};
|
|
||||||
if(w > 0) {
|
|
||||||
p.codepoint = nn_unicode_firstCodepoint(s + i);
|
|
||||||
i += w;
|
|
||||||
} else i++;
|
|
||||||
ne_setPixel(activeBuf, x, y, p);
|
|
||||||
x += dx;
|
|
||||||
y += dy;
|
|
||||||
}
|
|
||||||
ne_remapScreen(activeBuf);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_FILL:
|
|
||||||
if(activeBuf == NULL) {
|
|
||||||
nn_setError(C, "no screen");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
x = req->x;
|
|
||||||
y = req->y;
|
|
||||||
w = req->width;
|
|
||||||
h = req->height;
|
|
||||||
// prevent CPU DoS
|
|
||||||
if(w > activeBuf->width) w = activeBuf->width;
|
|
||||||
if(h > activeBuf->height) h = activeBuf->height;
|
|
||||||
|
|
||||||
p = (ne_Pixel) {
|
|
||||||
.fg = state->currentFg,
|
|
||||||
.bg = state->currentBg,
|
|
||||||
.isFgPalette = state->isFgPalette,
|
|
||||||
.isBgPalette = state->isBgPalette,
|
|
||||||
.codepoint = req->codepoint,
|
|
||||||
};
|
|
||||||
for(int oy = 0; oy < h; oy++) {
|
|
||||||
for(int ox = 0; ox < w; ox++) {
|
|
||||||
ne_setPixel(activeBuf, x + ox, y + oy, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ne_remapScreen(activeBuf);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_COPY:
|
|
||||||
if(activeBuf == NULL) {
|
|
||||||
nn_setError(C, "no screen");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
x = req->x;
|
|
||||||
y = req->y;
|
|
||||||
w = req->width;
|
|
||||||
h = req->height;
|
|
||||||
// prevent CPU DoS
|
|
||||||
if(w >= activeBuf->width) w = activeBuf->width - 1;
|
|
||||||
if(h >= activeBuf->height) h = activeBuf->height - 1;
|
|
||||||
|
|
||||||
ne_Pixel *buf = malloc(sizeof(*buf) * w * h);
|
|
||||||
if(buf == NULL) return NN_ENOMEM;
|
|
||||||
|
|
||||||
for(int oy = 0; oy < h; oy++) {
|
|
||||||
for(int ox = 0; ox < w; ox++) {
|
|
||||||
buf[oy * w + ox] = ne_getPixel(activeBuf, x + ox, y + oy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int oy = 0; oy < h; oy++) {
|
|
||||||
for(int ox = 0; ox < w; ox++) {
|
|
||||||
p = buf[oy * w + ox];
|
|
||||||
ne_setPixel(activeBuf, x + ox + req->tx, y + oy + req->ty, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(buf);
|
|
||||||
ne_remapScreen(activeBuf);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_GETDEPTH:
|
|
||||||
if(activeBuf != NULL) {
|
|
||||||
req->x = activeBuf->depth;
|
|
||||||
} else {
|
|
||||||
req->x = req->gpuConf->maxDepth;
|
|
||||||
}
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_MAXDEPTH:
|
|
||||||
req->x = maxDepth;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_GETVIEWPORT:
|
|
||||||
case NN_GPU_GETRESOLUTION:
|
|
||||||
if(activeBuf == NULL) {
|
|
||||||
nn_setError(C, "no screen");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
req->width = activeBuf->width;
|
|
||||||
req->height = activeBuf->height;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_MAXRESOLUTION:
|
|
||||||
req->width = maxWidth;
|
|
||||||
req->height = maxHeight;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_GETFOREGROUND:
|
|
||||||
req->x = state->currentFg;
|
|
||||||
req->y = state->isFgPalette ? 1 : 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_GETBACKGROUND:
|
|
||||||
req->x = state->currentBg;
|
|
||||||
req->y = state->isBgPalette ? 1 : 0;
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_SETFOREGROUND:
|
|
||||||
x = req->x;
|
|
||||||
y = req->y;
|
|
||||||
if(y != 0) {
|
|
||||||
// validate the palette index
|
|
||||||
if(activeBuf == NULL || x < 0 || x >= activeBuf->maxPalette) {
|
|
||||||
nn_setError(C, "invalid palette index");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req->x = state->currentFg;
|
|
||||||
req->y = state->isFgPalette ? 1 : 0;
|
|
||||||
state->currentFg = x;
|
|
||||||
state->isFgPalette = y != 0;
|
|
||||||
ne_remapScreen(activeBuf);
|
|
||||||
return NN_OK;
|
|
||||||
case NN_GPU_SETBACKGROUND:
|
|
||||||
x = req->x;
|
|
||||||
y = req->y;
|
|
||||||
if(y != 0) {
|
|
||||||
// validate the palette index
|
|
||||||
if(activeBuf == NULL || x < 0 || x >= activeBuf->maxPalette) {
|
|
||||||
nn_setError(C, "invalid palette index");
|
|
||||||
return NN_EBADCALL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
req->x = state->currentBg;
|
|
||||||
req->y = state->isBgPalette ? 1 : 0;
|
|
||||||
state->currentBg = x;
|
|
||||||
state->isBgPalette = y != 0;
|
|
||||||
ne_remapScreen(activeBuf);
|
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
}
|
}
|
||||||
return NN_OK;
|
return NN_OK;
|
||||||
@@ -1059,12 +358,14 @@ int main(int argc, char **argv) {
|
|||||||
nn_Architecture arch = getLuaArch();
|
nn_Architecture arch = getLuaArch();
|
||||||
|
|
||||||
nn_Method sandboxMethods[] = {
|
nn_Method sandboxMethods[] = {
|
||||||
{"log", "log(msg: string) - Log to stdout", true},
|
{"log", "log(msg: string) - Log to stdout", NN_DIRECT},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
nn_ComponentState *sandstate = nn_createComponentState(u, "ocelot", NULL, sandboxMethods, sandbox_handler);
|
nn_Component *ocelotCard = nn_createComponent(u, "ocelot", "ocelot");
|
||||||
|
nn_setComponentMethods(ocelotCard, sandboxMethods);
|
||||||
|
nn_setComponentHandler(ocelotCard, sandbox_handler);
|
||||||
|
|
||||||
nn_VEEPROM veeprom = {
|
const nn_VEEPROM veeprom = {
|
||||||
.code = minBIOS,
|
.code = minBIOS,
|
||||||
.codelen = strlen(minBIOS),
|
.codelen = strlen(minBIOS),
|
||||||
.data = NULL,
|
.data = NULL,
|
||||||
@@ -1075,15 +376,7 @@ int main(int argc, char **argv) {
|
|||||||
.isReadonly = false,
|
.isReadonly = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
nn_ComponentState *etype = nn_createVEEPROM(u, &nn_defaultEEPROMs[3], &veeprom);
|
nn_Component *eepromCard = nn_createVEEPROM(u, "eeprom", &veeprom, &nn_defaultEEPROMs[3]);
|
||||||
nn_ComponentState *fstype[5];
|
|
||||||
fstype[0] = nn_createFilesystem(u, &nn_defaultFloppy, ne_fsState_handler, NULL);
|
|
||||||
for(size_t i = 1; i < 5; i++) {
|
|
||||||
fstype[i] = nn_createFilesystem(u, &nn_defaultFilesystems[i-1], ne_fsState_handler, NULL);
|
|
||||||
}
|
|
||||||
nn_ComponentState *scrtype = nn_createScreen(u, ne_screen_handler, NULL);
|
|
||||||
nn_ComponentState *keytype = nn_createKeyboard(u);
|
|
||||||
nn_ComponentState *gputype = nn_createGPU(u, &nn_defaultGPUs[3], ne_gpu_handler, NULL);
|
|
||||||
|
|
||||||
size_t ramTotal = 0;
|
size_t ramTotal = 0;
|
||||||
ramTotal += nn_ramSizes[5];
|
ramTotal += nn_ramSizes[5];
|
||||||
@@ -1100,17 +393,8 @@ int main(int argc, char **argv) {
|
|||||||
nn_setArchitecture(c, &arch);
|
nn_setArchitecture(c, &arch);
|
||||||
nn_addSupportedArchitecture(c, &arch);
|
nn_addSupportedArchitecture(c, &arch);
|
||||||
|
|
||||||
nn_addComponent(c, sandstate, "sandbox", -1, NULL);
|
nn_mountComponent(c, ocelotCard, -1);
|
||||||
nn_addComponent(c, etype, "eeprom", 0, etype);
|
nn_mountComponent(c, eepromCard, 0);
|
||||||
|
|
||||||
nn_addComponent(c, fstype[4], "mainFS", 2, ne_newFS(mainDir, false));
|
|
||||||
|
|
||||||
nn_addComponent(c, keytype, "mainKB", 4, NULL);
|
|
||||||
ne_ScreenBuffer *scrbuf = ne_newScreenBuf(nn_defaultScreens[2], "mainKB");
|
|
||||||
nn_addComponent(c, scrtype, "mainScreen", -1, scrbuf);
|
|
||||||
|
|
||||||
ne_GPUState *gpu = ne_newGPU();
|
|
||||||
nn_addComponent(c, gputype, "mainGPU", 3, gpu);
|
|
||||||
|
|
||||||
const char *driveData = "error('unmanaged drive')";
|
const char *driveData = "error('unmanaged drive')";
|
||||||
nn_VDrive vdrive = {
|
nn_VDrive vdrive = {
|
||||||
@@ -1120,9 +404,6 @@ int main(int argc, char **argv) {
|
|||||||
.labellen = 0,
|
.labellen = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
nn_ComponentState *vdriveState = nn_createVDrive(u, &nn_defaultDrives[3], &vdrive);
|
|
||||||
nn_addComponent(c, vdriveState, "mainDrive", 4, NULL);
|
|
||||||
|
|
||||||
SetExitKey(KEY_NULL);
|
SetExitKey(KEY_NULL);
|
||||||
|
|
||||||
Font font = LoadFont("unscii-16-full.ttf");
|
Font font = LoadFont("unscii-16-full.ttf");
|
||||||
@@ -1144,30 +425,6 @@ int main(int argc, char **argv) {
|
|||||||
BeginDrawing();
|
BeginDrawing();
|
||||||
ClearBackground(BLACK);
|
ClearBackground(BLACK);
|
||||||
|
|
||||||
int scrW = scrbuf->width;
|
|
||||||
int scrH = scrbuf->height;
|
|
||||||
|
|
||||||
int pixelHeight = GetScreenHeight() / scrH;
|
|
||||||
float spacing = (float)pixelHeight/10;
|
|
||||||
int pixelWidth = MeasureTextEx(font, "A", pixelHeight, spacing).x;
|
|
||||||
|
|
||||||
int depth = scrbuf->depth;
|
|
||||||
|
|
||||||
int offX = (GetScreenWidth() - scrW * pixelWidth) / 2;
|
|
||||||
int offY = (GetScreenHeight() - scrH * pixelHeight) / 2;
|
|
||||||
|
|
||||||
for(int y = 0; y < scrH; y++) {
|
|
||||||
for(int x = 0; x < scrW; x++) {
|
|
||||||
ne_Pixel p = ne_getPixel(scrbuf, x+1, y+1);
|
|
||||||
|
|
||||||
Color fgColor = ne_processColor(p.truefg);
|
|
||||||
Color bgColor = ne_processColor(p.truebg);
|
|
||||||
|
|
||||||
DrawRectangle(x * pixelWidth + offX, y * pixelHeight + offY, pixelWidth, pixelHeight, bgColor);
|
|
||||||
DrawTextCodepoint(font, p.codepoint, (Vector2) {x * pixelWidth + offX, y * pixelHeight + offY}, pixelHeight, fgColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int statY = 10;
|
int statY = 10;
|
||||||
if(sand.buf != NULL) {
|
if(sand.buf != NULL) {
|
||||||
DrawText(TextFormat("mem used: %.2f%%", (double)sand.used / sand.cap * 100), 10, statY, 20, YELLOW);
|
DrawText(TextFormat("mem used: %.2f%%", (double)sand.used / sand.cap * 100), 10, statY, 20, YELLOW);
|
||||||
@@ -1261,14 +518,8 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
cleanup:;
|
cleanup:;
|
||||||
nn_destroyComputer(c);
|
nn_destroyComputer(c);
|
||||||
nn_destroyComponentState(sandstate);
|
nn_dropComponent(ocelotCard);
|
||||||
nn_destroyComponentState(etype);
|
nn_dropComponent(eepromCard);
|
||||||
nn_destroyComponentState(gputype);
|
|
||||||
nn_destroyComponentState(scrtype);
|
|
||||||
nn_destroyComponentState(keytype);
|
|
||||||
nn_destroyComponentState(vdriveState);
|
|
||||||
for(size_t i = 0; i < 5; i++) nn_destroyComponentState(fstype[i]);
|
|
||||||
ne_dropScreenBuf(scrbuf);
|
|
||||||
// rip the universe
|
// rip the universe
|
||||||
nn_destroyUniverse(u);
|
nn_destroyUniverse(u);
|
||||||
UnloadFont(font);
|
UnloadFont(font);
|
||||||
|
|||||||
116
src/ncomplib.c
Normal file
116
src/ncomplib.c
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#include "neonucleus.h"
|
||||||
|
#include "ncomplib.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
typedef struct ncl_ScreenPixel {
|
||||||
|
nn_codepoint codepoint;
|
||||||
|
int storedFg;
|
||||||
|
int storedBg;
|
||||||
|
// if negative, its in palette
|
||||||
|
int realFg;
|
||||||
|
// if negative, its in palette
|
||||||
|
int realBg;
|
||||||
|
} ncl_ScreenPixel;
|
||||||
|
|
||||||
|
typedef struct ncl_ScreenState {
|
||||||
|
nn_Context *ctx;
|
||||||
|
nn_ScreenConfig conf;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int viewportWidth;
|
||||||
|
int viewportHeight;
|
||||||
|
char depth;
|
||||||
|
int *palette;
|
||||||
|
int *resolvedPalette;
|
||||||
|
ncl_ScreenPixel *pixels;
|
||||||
|
} ncl_ScreenState;
|
||||||
|
|
||||||
|
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs);
|
||||||
|
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive);
|
||||||
|
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
|
||||||
|
|
||||||
|
static ncl_ScreenPixel ncl_getRealScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
||||||
|
if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
|
||||||
|
return (ncl_ScreenPixel) {
|
||||||
|
.codepoint = ' ',
|
||||||
|
.storedFg = 0xFFFFFF,
|
||||||
|
.storedBg = 0x000000,
|
||||||
|
.realFg = 0xFFFFFF,
|
||||||
|
.realBg = 0x000000,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it 0-indexed
|
||||||
|
x--;
|
||||||
|
y--;
|
||||||
|
|
||||||
|
return state->pixels[x + y * state->conf.maxWidth];
|
||||||
|
}
|
||||||
|
|
||||||
|
static ncl_ScreenPixel *ncl_getRealScreenPixelPointer(const ncl_ScreenState *state, int x, int y) {
|
||||||
|
if(x < 1 || y < 1 || x >= state->width || y >= state->height) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make it 0-indexed
|
||||||
|
x--;
|
||||||
|
y--;
|
||||||
|
|
||||||
|
return &state->pixels[x + y * state->conf.maxWidth];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ncl_setRealScreenPixel(const ncl_ScreenState *state, int x, int y, ncl_ScreenPixel pixel) {
|
||||||
|
if(x < 1 || y < 1 || x >= state->width || y >= state->height) return;
|
||||||
|
x--;
|
||||||
|
y--;
|
||||||
|
|
||||||
|
state->pixels[x + y * state->conf.maxWidth] = pixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ncl_recomputeScreen(const ncl_ScreenState *state) {
|
||||||
|
for(int y = 1; y <= state->height; y++) {
|
||||||
|
for(int x = 1; x <= state->width; x++) {
|
||||||
|
ncl_ScreenPixel *pixel = ncl_getRealScreenPixelPointer(state, x, y);
|
||||||
|
if(pixel == NULL) continue;
|
||||||
|
if(pixel->realFg >= 0) {
|
||||||
|
pixel->realFg = nn_mapDepth(pixel->storedFg, state->depth);
|
||||||
|
}
|
||||||
|
if(pixel->realBg >= 0) {
|
||||||
|
pixel->realBg = nn_mapDepth(pixel->storedBg, state->depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < state->conf.paletteColors; i++) {
|
||||||
|
state->resolvedPalette[i] = nn_mapDepth(state->palette[i], state->depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
||||||
|
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
||||||
|
|
||||||
|
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height) {
|
||||||
|
*width = state->width;
|
||||||
|
*height = state->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height) {
|
||||||
|
*width = state->viewportWidth;
|
||||||
|
*height = state->viewportHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y) {
|
||||||
|
ncl_ScreenPixel p = ncl_getRealScreenPixel(state, x, y);
|
||||||
|
return (ncl_Pixel) {
|
||||||
|
.codepoint = p.codepoint,
|
||||||
|
.fgColor = p.realFg < 0 ? state->resolvedPalette[p.storedFg] : p.realFg,
|
||||||
|
.bgColor = p.realBg < 0 ? state->resolvedPalette[p.storedBg] : p.realBg,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// all of these are encoding states
|
||||||
|
|
||||||
|
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
|
||||||
|
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
||||||
|
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
||||||
39
src/ncomplib.h
Normal file
39
src/ncomplib.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
#ifndef NN_COMPLIB
|
||||||
|
#define NN_COMPLIB
|
||||||
|
|
||||||
|
#include "neonucleus.h"
|
||||||
|
|
||||||
|
typedef struct ncl_EncodedState {
|
||||||
|
char *buf;
|
||||||
|
size_t len;
|
||||||
|
} ncl_EncodedState;
|
||||||
|
|
||||||
|
nn_Exit ncl_encodeComponentState(nn_Universe *universe, nn_Component *comp, ncl_EncodedState *state);
|
||||||
|
void ncl_freeEncodedState(nn_Universe *universe, ncl_EncodedState *state);
|
||||||
|
nn_Exit ncl_loadComponentState(nn_Component *comp, const ncl_EncodedState *state);
|
||||||
|
|
||||||
|
nn_Component *ncl_createFilesystem(nn_Universe *universe, const char *address, const char *path, const nn_Filesystem *fs);
|
||||||
|
nn_Component *ncl_createDrive(nn_Universe *universe, const char *address, const char *path, const nn_Drive *drive);
|
||||||
|
nn_Component *ncl_createEEPROM(nn_Universe *universe, const char *address, const char *codepath, const char *datapath);
|
||||||
|
nn_Component *ncl_createScreen(nn_Universe *universe, const char *address, const nn_ScreenConfig *config);
|
||||||
|
nn_Component *ncl_createGPU(nn_Universe *universe, const char *address, const nn_GPU *gpu);
|
||||||
|
|
||||||
|
// TODO, stuff we could implement:
|
||||||
|
// redstone, hologram, oled, ipu, vt, led, tape_drive, cd_drive, serial, colorful_lamp
|
||||||
|
|
||||||
|
typedef struct ncl_Pixel {
|
||||||
|
// 0xRRGGBB format
|
||||||
|
unsigned int fgColor;
|
||||||
|
// 0xRRGGBB format
|
||||||
|
unsigned int bgColor;
|
||||||
|
// the codepoint
|
||||||
|
nn_codepoint codepoint;
|
||||||
|
} ncl_Pixel;
|
||||||
|
|
||||||
|
typedef struct ncl_ScreenState ncl_ScreenState;
|
||||||
|
|
||||||
|
void ncl_getScreenResolution(const ncl_ScreenState *state, size_t *width, size_t *height);
|
||||||
|
void ncl_getScreenViewport(const ncl_ScreenState *state, size_t *width, size_t *height);
|
||||||
|
ncl_Pixel ncl_getScreenPixel(const ncl_ScreenState *state, int x, int y);
|
||||||
|
|
||||||
|
#endif
|
||||||
2524
src/neonucleus.c
2524
src/neonucleus.c
File diff suppressed because it is too large
Load Diff
705
src/neonucleus.h
705
src/neonucleus.h
@@ -227,6 +227,9 @@ void *nn_alloc(nn_Context *ctx, size_t size);
|
|||||||
void nn_free(nn_Context *ctx, void *memory, 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);
|
void *nn_realloc(nn_Context *ctx, void *memory, size_t oldSize, size_t newSize);
|
||||||
|
|
||||||
|
typedef char nn_uuid[37];
|
||||||
|
void nn_randomUUID(nn_Context *ctx, nn_uuid uuid);
|
||||||
|
|
||||||
// Basic utils
|
// Basic utils
|
||||||
|
|
||||||
// Does canonical path handling. Is used for sandboxing paths.
|
// Does canonical path handling. Is used for sandboxing paths.
|
||||||
@@ -481,24 +484,9 @@ void nn_resetIdleTime(nn_Computer *computer);
|
|||||||
// It also sets the idle timestamp to the current uptime.
|
// It also sets the idle timestamp to the current uptime.
|
||||||
nn_Exit nn_tick(nn_Computer *computer);
|
nn_Exit nn_tick(nn_Computer *computer);
|
||||||
|
|
||||||
typedef struct nn_DeviceInfoEntry {
|
// raw component and methods
|
||||||
const char *name;
|
|
||||||
const char *value;
|
|
||||||
} nn_DeviceInfoEntry;
|
|
||||||
|
|
||||||
typedef struct nn_DeviceInfo {
|
typedef struct nn_Component nn_Component;
|
||||||
const char *address;
|
|
||||||
const nn_DeviceInfoEntry *entries;
|
|
||||||
} nn_DeviceInfo;
|
|
||||||
|
|
||||||
// adds some device information to the computer. This can also be removed.
|
|
||||||
// Entries is terminated by a NULL name, and preferrably also NULL value.
|
|
||||||
// It is perfectly fine to free entries after the call, it is copied.
|
|
||||||
nn_Exit nn_addDeviceInfo(nn_Computer *computer, const char *address, const nn_DeviceInfoEntry entries[]);
|
|
||||||
// Removes info assicated with a device
|
|
||||||
void nn_removeDeviceInfo(nn_Computer *computer, const char *address);
|
|
||||||
// gets the device info array.
|
|
||||||
const nn_DeviceInfo *nn_getDeviceInfo(nn_Computer *computer, size_t *len);
|
|
||||||
|
|
||||||
typedef enum nn_MethodFlags {
|
typedef enum nn_MethodFlags {
|
||||||
// calling will consume the entire call budget
|
// calling will consume the entire call budget
|
||||||
@@ -517,91 +505,108 @@ typedef enum nn_MethodFlags {
|
|||||||
|
|
||||||
typedef struct nn_Method {
|
typedef struct nn_Method {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *docString;
|
const char *doc;
|
||||||
nn_MethodFlags flags;
|
nn_MethodFlags flags;
|
||||||
int idx;
|
|
||||||
} nn_Method;
|
} nn_Method;
|
||||||
|
|
||||||
typedef struct nn_ComponentState nn_ComponentState;
|
// component signals
|
||||||
|
|
||||||
|
// tells the component to reset its state
|
||||||
|
// sent to the components with slot >= 0 and to tmpfs when computer state is dropped
|
||||||
|
#define NN_CSIGRESET "reset"
|
||||||
|
|
||||||
typedef enum nn_ComponentAction {
|
typedef enum nn_ComponentAction {
|
||||||
// create the local state
|
// component dropped
|
||||||
NN_COMP_INIT,
|
NN_COMP_DROP,
|
||||||
// delete the local state
|
// component method invoked
|
||||||
NN_COMP_DEINIT,
|
NN_COMP_INVOKE,
|
||||||
// perform a method call
|
// checking if component method is enabled
|
||||||
NN_COMP_CALL,
|
// (may be locked by tier)
|
||||||
// check if a method is enabled
|
NN_COMP_CHECKMETHOD,
|
||||||
NN_COMP_ENABLED,
|
// signal sent to the machine
|
||||||
// delete the type userdata
|
NN_COMP_SIGNAL,
|
||||||
NN_COMP_FREETYPE,
|
|
||||||
} nn_ComponentAction;
|
} nn_ComponentAction;
|
||||||
|
|
||||||
typedef struct nn_ComponentRequest {
|
typedef struct nn_ComponentRequest {
|
||||||
// the userdata of the component type. This may be an associated VM, for example.
|
nn_Context *ctx;
|
||||||
void *typeUserdata;
|
|
||||||
// the userdata of the component, passed in addComponent. This may be an associated resource, for example.
|
|
||||||
void *compUserdata;
|
|
||||||
// the local state of the component. NN_COMP_INIT should initialize this pointer.
|
|
||||||
void *state;
|
|
||||||
nn_Computer *computer;
|
nn_Computer *computer;
|
||||||
// address of the component
|
void *state;
|
||||||
const char *compAddress;
|
|
||||||
// the action requested
|
|
||||||
nn_ComponentAction action;
|
nn_ComponentAction action;
|
||||||
// for NN_COMP_CALL, it is the idx of the method called.
|
// method index
|
||||||
// for NN_COMP_ENABLED, it is the idx of the method being checked.
|
unsigned int methodIdx;
|
||||||
int methodCalled;
|
|
||||||
union {
|
union {
|
||||||
// for NN_COMP_CALL, it is the amount of return values.
|
// return count
|
||||||
size_t returnCount;
|
size_t returnCount;
|
||||||
// for NN_COMP_ENABLED, it is whether the method is enabled.
|
// method enabled
|
||||||
bool methodEnabled;
|
bool methodEnabled;
|
||||||
|
// signal invocation
|
||||||
|
const char *signal;
|
||||||
};
|
};
|
||||||
} nn_ComponentRequest;
|
} nn_ComponentRequest;
|
||||||
|
|
||||||
typedef nn_Exit nn_ComponentHandler(nn_ComponentRequest *req);
|
typedef nn_Exit (nn_ComponentHandler)(nn_ComponentRequest *request);
|
||||||
|
|
||||||
// Creates a new component type. It is safe to free name and methods afterwards.
|
// creates a blank component.
|
||||||
nn_ComponentState *nn_createComponentState(nn_Universe *universe, const char *name, void *userdata, const nn_Method methods[], nn_ComponentHandler *handler);
|
// It has no methods,
|
||||||
// NOTE: do not destroy this before destroying any components using it, or any computers with components using it.
|
nn_Component *nn_createComponent(nn_Universe *universe, const char *address, const char *type);
|
||||||
// The component type is still used one last time for the destructor of the components.
|
void nn_retainComponent(nn_Component *c);
|
||||||
void nn_destroyComponentState(nn_ComponentState *cstate);
|
void nn_retainComponentN(nn_Component *c, size_t n);
|
||||||
|
void nn_dropComponent(nn_Component *c);
|
||||||
|
void nn_dropComponentN(nn_Component *c, size_t n);
|
||||||
|
|
||||||
// adds a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component added.
|
// configure the state
|
||||||
// You MUST NOT destroy the component type while a component using that type still exists.
|
void nn_setComponentHandler(nn_Component *c, nn_ComponentHandler *handler);
|
||||||
// You can free the address after the call just fine.
|
void nn_setComponentState(nn_Component *c, void *state);
|
||||||
nn_Exit nn_addComponent(nn_Computer *computer, nn_ComponentState *cstate, const char *address, int slot, void *userdata);
|
// sets the methods, same implications as setComponentMethodsArray.
|
||||||
// Checks if a component of that address exists.
|
// methods is NULL-terminated, as in, it is terminated by a method with a NULL name.
|
||||||
bool nn_hasComponent(nn_Computer *computer, const char *address);
|
nn_Exit nn_setComponentMethods(nn_Component *c, const nn_Method *methods);
|
||||||
// Checks if the component has that method.
|
// sets the methods.
|
||||||
// This not only checks if the method exists in the component type,
|
// The memory of the strings is copied, so they can be freed after this returns.
|
||||||
// but also checks if the method is enabled for the component instance.
|
// This operation is NOT atomic, if it fails, it will clear out the previous methods.
|
||||||
bool nn_hasMethod(nn_Computer *computer, const char *address, const char *method);
|
nn_Exit nn_setComponentMethodsArray(nn_Component *c, const nn_Method *methods, size_t count);
|
||||||
// removes a component. Outside of the initialization state (aka after the first tick), it also emits the signal for component removed.
|
// Sets an internal type ID, which is meant to be a more precise typename.
|
||||||
nn_Exit nn_removeComponent(nn_Computer *computer, const char *address);
|
// For example, ncomplib would set ncl-screen for the screen component,
|
||||||
// Gets the name of a type of a component.
|
// so the GPU can confirm it is being bound to a screen it knows how to use.
|
||||||
const char *nn_getComponentType(nn_Computer *computer, const char *address);
|
nn_Exit nn_setComponentTypeID(nn_Component *c, const char *internalTypeID);
|
||||||
// Gets the slot of a component.
|
|
||||||
int nn_getComponentSlot(nn_Computer *computer, const char *address);
|
|
||||||
// Iterates over the methods of a component.
|
|
||||||
// Returns NULL at end of iteration.
|
|
||||||
// name should be NULL at the start.
|
|
||||||
// NOTE: the method pointer MUST be returned by the iterator, as it is offset during iteration.
|
|
||||||
const nn_Method *nn_nextComponentMethod(nn_Computer *computer, const char *address, const nn_Method *old);
|
|
||||||
// iterate over components.
|
|
||||||
// for prev = NULL, returns the first one.
|
|
||||||
// returns NULL at the end of iteration.
|
|
||||||
const char *nn_getNextComponent(nn_Computer *computer, const char *prev);
|
|
||||||
// Returns the doc-string associated with a method.
|
|
||||||
const char *nn_getComponentDoc(nn_Computer *computer, const char *address, const char *method);
|
|
||||||
void *nn_getComponentUserdata(nn_Computer *computer, const char *address);
|
|
||||||
|
|
||||||
// this uses the call stack.
|
// get component state
|
||||||
// Component calls must not call other components, it just doesn't work.
|
void *nn_getComponentState(nn_Component *c);
|
||||||
// The lack of an argument count is because the entire call stack is assumed to be the arguments.
|
// counts how many methods are registered. May return too many if some of them are not enabled.
|
||||||
// In the case of NN_EBUSY, you should call it again with the same arguments later.
|
size_t nn_countComponentMethods(nn_Component *c);
|
||||||
nn_Exit nn_call(nn_Computer *computer, const char *address, const char *method);
|
// 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);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
// If the component already is mounted, an error is returned.
|
||||||
|
nn_Exit nn_mountComponent(nn_Computer *c, nn_Component *comp, int slot);
|
||||||
|
// Removes a component from the computer.
|
||||||
|
// This will also queue a component_removed signal if the computer is in a running state.
|
||||||
|
// If the component is not mounted, no error is returned.
|
||||||
|
nn_Exit nn_unmountComponent(nn_Computer *c, const char *address);
|
||||||
|
// 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.
|
||||||
|
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.
|
// Sets the call budget.
|
||||||
// The default is 1,000.
|
// The default is 1,000.
|
||||||
@@ -806,44 +811,10 @@ nn_Exit nn_pushSignal(nn_Computer *computer, size_t valueCount);
|
|||||||
// If there is no signal, it returns EBADSTATE
|
// If there is no signal, it returns EBADSTATE
|
||||||
nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
|
nn_Exit nn_popSignal(nn_Computer *computer, size_t *valueCount);
|
||||||
|
|
||||||
// The high-level API of the built-in components.
|
// 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.
|
// These components still make no assumptions about the OS, and still require handlers to connect them to the outside work.
|
||||||
|
|
||||||
// TODO: screen, gpu, filesystem, eeprom and the rest of the universe
|
// EEPROM class
|
||||||
|
|
||||||
typedef enum nn_EEPROMAction {
|
|
||||||
// the eeprom instance has been dropped
|
|
||||||
NN_EEPROM_DROP,
|
|
||||||
// the eeprom state has been dropped
|
|
||||||
NN_EEPROM_FREE,
|
|
||||||
NN_EEPROM_GET,
|
|
||||||
NN_EEPROM_SET,
|
|
||||||
NN_EEPROM_GETDATA,
|
|
||||||
NN_EEPROM_SETDATA,
|
|
||||||
NN_EEPROM_GETLABEL,
|
|
||||||
NN_EEPROM_SETLABEL,
|
|
||||||
NN_EEPROM_GETARCH,
|
|
||||||
NN_EEPROM_SETARCH,
|
|
||||||
NN_EEPROM_ISREADONLY,
|
|
||||||
NN_EEPROM_MAKEREADONLY,
|
|
||||||
} nn_EEPROMAction;
|
|
||||||
|
|
||||||
typedef struct nn_EEPROMRequest {
|
|
||||||
// associated userdata
|
|
||||||
void *userdata;
|
|
||||||
// associated component userdata
|
|
||||||
void *instance;
|
|
||||||
// the computer making the request
|
|
||||||
nn_Computer *computer;
|
|
||||||
const struct nn_EEPROM *eepromConf;
|
|
||||||
nn_EEPROMAction action;
|
|
||||||
// all the get* options should set this to the length,
|
|
||||||
// and its initial value is the capacity of [buf].
|
|
||||||
// For ISREADONLY, this should be set to 0 if false and 1 if true.
|
|
||||||
unsigned int buflen;
|
|
||||||
// this may be the buffer length
|
|
||||||
char *buf;
|
|
||||||
} nn_EEPROMRequest;
|
|
||||||
|
|
||||||
// reads and writes are always 1/1
|
// reads and writes are always 1/1
|
||||||
typedef struct nn_EEPROM {
|
typedef struct nn_EEPROM {
|
||||||
@@ -865,6 +836,39 @@ typedef struct nn_EEPROM {
|
|||||||
double writeDataDelay;
|
double writeDataDelay;
|
||||||
} nn_EEPROM;
|
} 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;
|
||||||
|
char *buf;
|
||||||
|
size_t buflen;
|
||||||
|
} nn_EEPROMRequest;
|
||||||
|
|
||||||
|
typedef nn_Exit (nn_EEPROMHandler)(nn_EEPROMRequest *request);
|
||||||
|
|
||||||
// Tier 1 - The normal EEPROM equivalent
|
// Tier 1 - The normal EEPROM equivalent
|
||||||
// Tier 2 - A better EEPROM
|
// Tier 2 - A better EEPROM
|
||||||
// Tier 3 - An even better EEPROM
|
// Tier 3 - An even better EEPROM
|
||||||
@@ -882,147 +886,10 @@ typedef struct nn_VEEPROM {
|
|||||||
bool isReadonly;
|
bool isReadonly;
|
||||||
} nn_VEEPROM;
|
} nn_VEEPROM;
|
||||||
|
|
||||||
typedef nn_Exit nn_EEPROMHandler(nn_EEPROMRequest *request);
|
nn_Component *nn_createEEPROM(nn_Universe *universe, const char *address, const nn_EEPROM *eeprom, void *state, nn_EEPROMHandler *handler);
|
||||||
|
nn_Component *nn_createVEEPROM(nn_Universe *universe, const char *address, const nn_VEEPROM *veeprom, const nn_EEPROM *eeprom);
|
||||||
|
|
||||||
// the userdata passed to the component is the userdata
|
// Filesystem class
|
||||||
// in the handler
|
|
||||||
nn_ComponentState *nn_createEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, nn_EEPROMHandler *handler, void *userdata);
|
|
||||||
nn_ComponentState *nn_createVEEPROM(nn_Universe *universe, const nn_EEPROM *eeprom, const nn_VEEPROM *vmem);
|
|
||||||
|
|
||||||
// Note on paths:
|
|
||||||
// - Paths given always have their length stored, but also have a NULL terminator.
|
|
||||||
// - Paths are validated. They check for illegal characters as per OC's definition.
|
|
||||||
// - Logical paradoxes such as rename("a", "a/b") are automatically checked and handled.
|
|
||||||
// - \ are automatically replaced with /
|
|
||||||
// - .. and leading / is handled automatically. This also improves sandboxing, as ../a.txt would become just a.txt
|
|
||||||
// - For rename, it automatically checks if the destination exists and if so, errors out.
|
|
||||||
typedef enum nn_FilesystemAction {
|
|
||||||
// the filesystem instance has been dropped.
|
|
||||||
// This is just for computer-local state, make sure to free it.
|
|
||||||
NN_FS_DROP,
|
|
||||||
// the filesystem state has been dropped.
|
|
||||||
// Make sure to close all file descriptors which are still open.
|
|
||||||
NN_FS_FREE,
|
|
||||||
// open a file. strarg1 stores the path, and strarg2 stores the mode.
|
|
||||||
// strarg1len and strarg2len are their respective lengths.
|
|
||||||
// The output should be in fd.
|
|
||||||
NN_FS_OPEN,
|
|
||||||
// read a file.
|
|
||||||
// The file descriptor is stored in fd,
|
|
||||||
// make sure to ensure it is valid.
|
|
||||||
// strarg1len is the capacity of strarg1.
|
|
||||||
// Write the result of reading into strarg1.
|
|
||||||
// Update strarg1len to reflect the amount of data read.
|
|
||||||
// Set strarg1 to NULL to indicate EOF.
|
|
||||||
NN_FS_READ,
|
|
||||||
// write to a file.
|
|
||||||
// The file descriptor is stored in fd,
|
|
||||||
// make sure to ensure it is valid.
|
|
||||||
// strarg1len is the amount of data to write.
|
|
||||||
// strarg1 is the contents of the buffer to write.
|
|
||||||
NN_FS_WRITE,
|
|
||||||
// seek a file.
|
|
||||||
// The file descriptor is stored in fd,
|
|
||||||
// make sure to ensure it is valid.
|
|
||||||
// The offset is stored in off.
|
|
||||||
// The seek mode is stored in whence.
|
|
||||||
// It should set off to the new position.
|
|
||||||
NN_FS_SEEK,
|
|
||||||
// close a file.
|
|
||||||
// The file descriptor is stored in fd,
|
|
||||||
// make sure to ensure it is valid.
|
|
||||||
NN_FS_CLOSE,
|
|
||||||
// open a directory file descriptor.
|
|
||||||
// The result should be in fd.
|
|
||||||
NN_FS_OPENDIR,
|
|
||||||
// read a directory file descriptor, stored in fd.
|
|
||||||
// The entry should be stored in strarg2, and strarg2len is the capacity of the buffer.
|
|
||||||
// If the buffer is too short, truncate the result.
|
|
||||||
// Set strarg2len to the length of the entry.
|
|
||||||
// If there are no more entries, set strarg2 to NULL.
|
|
||||||
// Do note that directories should have / appended at the end of their entries.
|
|
||||||
// Directory file descriptors are not exposed to the architecture,
|
|
||||||
// thus they can only come from NN_FS_OPENDIR.
|
|
||||||
// This means you may not need to validate these file descriptors.
|
|
||||||
NN_FS_READDIR,
|
|
||||||
// close a directory file descriptor, stored in fd.
|
|
||||||
// Directory file descriptors are not exposed to the architecture,
|
|
||||||
// thus they can only come from NN_FS_OPENDIR.
|
|
||||||
// This means you may not need to validate these file descriptors.
|
|
||||||
NN_FS_CLOSEDIR,
|
|
||||||
// Create a directory at a given path stored in strarg1.
|
|
||||||
// strarg1len is the length of the path.
|
|
||||||
// It is meant to also create parent directories recursively
|
|
||||||
// as needed.
|
|
||||||
NN_FS_MKDIR,
|
|
||||||
// Return the lastmodified timestamp.
|
|
||||||
// This number is stored in seconds.
|
|
||||||
// The timestamp should be stored in size, it may not make
|
|
||||||
// sense but it is a field and it is there.
|
|
||||||
// Do note that the lastModified() method returns it in milliseconds,
|
|
||||||
// however it must be a multiple of 1000 due to OpenOS depending
|
|
||||||
// on that behavior.
|
|
||||||
NN_FS_LASTMODIFIED,
|
|
||||||
// Checks if a path, stored in strarg1, is a directory.
|
|
||||||
// If it is, size should be set to 1.
|
|
||||||
// If it is not, size should be set to 0.
|
|
||||||
NN_FS_ISDIRECTORY,
|
|
||||||
// Checks if the filesystem is read-only.
|
|
||||||
// If it is, size should be set to 1.
|
|
||||||
// If it is not, size should be set to 0.
|
|
||||||
NN_FS_ISREADONLY,
|
|
||||||
// Checks if a path, stored in strarg1, exists on the filesystem.
|
|
||||||
// If it is, size should be set to 1.
|
|
||||||
// If it is not, size should be set to 0.
|
|
||||||
NN_FS_EXISTS,
|
|
||||||
// Returns the label.
|
|
||||||
// The label should be written into strarg1, with strarg1len as the capacity.
|
|
||||||
// Set strarg1len to the label length.
|
|
||||||
NN_FS_GETLABEL,
|
|
||||||
// Sets the label.
|
|
||||||
// The label is stored in strarg1, with strarg1len as the length.
|
|
||||||
NN_FS_SETLABEL,
|
|
||||||
// Gets the space used, it should be stored in size.
|
|
||||||
NN_FS_SPACEUSED,
|
|
||||||
// Gets 2 paths, strarg1 and strarg2, with their lengths.
|
|
||||||
// It should try to rename strarg1 to strarg2, as in,
|
|
||||||
// it should move strarg1 to be at strarg2, potentially
|
|
||||||
// using recursive directory copies.
|
|
||||||
NN_FS_RENAME,
|
|
||||||
// Removes the path stored in strarg1.
|
|
||||||
NN_FS_REMOVE,
|
|
||||||
// Returns the size of the entry at strarg1.
|
|
||||||
// The size of a directory is typically 0.
|
|
||||||
// The size of a file is typically the amount of bytes in its contents.
|
|
||||||
// Using other measures of size will rarely break code,
|
|
||||||
// but may confuse users.
|
|
||||||
NN_FS_SIZE,
|
|
||||||
} nn_FilesystemAction;
|
|
||||||
|
|
||||||
typedef enum nn_FilesystemWhence {
|
|
||||||
// relative to start
|
|
||||||
NN_SEEK_SET,
|
|
||||||
// relative to the current position
|
|
||||||
NN_SEEK_CUR,
|
|
||||||
// relative to the EOF position.
|
|
||||||
NN_SEEK_END,
|
|
||||||
} nn_FilesystemWhence;
|
|
||||||
|
|
||||||
typedef struct nn_FilesystemRequest {
|
|
||||||
void *userdata;
|
|
||||||
void *instance;
|
|
||||||
nn_Computer *computer;
|
|
||||||
struct nn_Filesystem *fsConf;
|
|
||||||
nn_FilesystemAction action;
|
|
||||||
int fd;
|
|
||||||
nn_FilesystemWhence whence;
|
|
||||||
int off;
|
|
||||||
char *strarg1;
|
|
||||||
size_t strarg1len;
|
|
||||||
char *strarg2;
|
|
||||||
size_t strarg2len;
|
|
||||||
size_t size;
|
|
||||||
} nn_FilesystemRequest;
|
|
||||||
|
|
||||||
typedef struct nn_Filesystem {
|
typedef struct nn_Filesystem {
|
||||||
// the maximum capacity of the filesystem
|
// the maximum capacity of the filesystem
|
||||||
@@ -1049,8 +916,6 @@ extern const nn_Filesystem nn_defaultFloppy;
|
|||||||
// a generic tmpfs
|
// a generic tmpfs
|
||||||
extern const nn_Filesystem nn_defaultTmpFS;
|
extern const nn_Filesystem nn_defaultTmpFS;
|
||||||
|
|
||||||
typedef nn_Exit nn_FilesystemHandler(nn_FilesystemRequest *request);
|
|
||||||
|
|
||||||
typedef struct nn_VFileNode {
|
typedef struct nn_VFileNode {
|
||||||
// the name of the node.
|
// the name of the node.
|
||||||
// This is the raw name, do not append / to directories.
|
// This is the raw name, do not append / to directories.
|
||||||
@@ -1085,63 +950,7 @@ typedef struct nn_VFilesystem {
|
|||||||
nn_VFileNode *image;
|
nn_VFileNode *image;
|
||||||
} nn_VFilesystem;
|
} nn_VFilesystem;
|
||||||
|
|
||||||
nn_ComponentState *nn_createFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, nn_FilesystemHandler *handler, void *userdata);
|
// Drive class
|
||||||
nn_ComponentState *nn_createVFilesystem(nn_Universe *universe, const nn_Filesystem *filesystem, const nn_VFilesystem *vfs);
|
|
||||||
|
|
||||||
typedef enum nn_DriveAction {
|
|
||||||
// instance dropped
|
|
||||||
NN_DRIVE_DROP,
|
|
||||||
// free screen state
|
|
||||||
NN_DRIVE_FREE,
|
|
||||||
// Gets the current label.
|
|
||||||
// [index] is set to the capacity of [buf].
|
|
||||||
// You must write the label into [buf], then set [index] to the length of the label.
|
|
||||||
// Empty label means no label.
|
|
||||||
NN_DRIVE_GETLABEL,
|
|
||||||
// Sets the current label.
|
|
||||||
// [index] is set to the length of [buf].
|
|
||||||
// Empty label means no label.
|
|
||||||
// Set [index] to the new length of the label, if it has been truncated.
|
|
||||||
NN_DRIVE_SETLABEL,
|
|
||||||
// gets the current read head, or more accurately, the last sector used
|
|
||||||
// in order to compute seeking penalties.
|
|
||||||
// You must output the current read head in [index].
|
|
||||||
NN_DRIVE_GETCURSECTOR,
|
|
||||||
// Reads a sector.
|
|
||||||
// The sector index is in [index], and the contents are in [buf].
|
|
||||||
NN_DRIVE_READSECTOR,
|
|
||||||
// Writes a sector.
|
|
||||||
// The sector index is in [index].
|
|
||||||
// Output the contents of that sector in [buf].
|
|
||||||
NN_DRIVE_WRITESECTOR,
|
|
||||||
// Reads a byte
|
|
||||||
// The byte index is in [index].
|
|
||||||
// You must output the byte in [byte].
|
|
||||||
NN_DRIVE_READBYTE,
|
|
||||||
// Writes a byte.
|
|
||||||
// The byte index is in [index], the byte is in [byte].
|
|
||||||
NN_DRIVE_WRITEBYTE,
|
|
||||||
} nn_DriveAction;
|
|
||||||
|
|
||||||
// Note that sectors and bytes are 1-indexed.
|
|
||||||
// Bounds checking is done automatically by the interface.
|
|
||||||
typedef struct nn_DriveRequest {
|
|
||||||
void *userdata;
|
|
||||||
void *instance;
|
|
||||||
nn_Computer *computer;
|
|
||||||
struct nn_Drive *driveConf;
|
|
||||||
nn_DriveAction action;
|
|
||||||
size_t index;
|
|
||||||
union {
|
|
||||||
char *buf;
|
|
||||||
// OC explicitly uses *signed* chars.
|
|
||||||
// Helper methods for reading unsigned bytes cast it to an unsigned byte first.
|
|
||||||
// Just, do not ask.
|
|
||||||
signed char byte;
|
|
||||||
};
|
|
||||||
} nn_DriveRequest;
|
|
||||||
|
|
||||||
typedef nn_Exit nn_DriveHandler(nn_DriveRequest *req);
|
|
||||||
|
|
||||||
typedef struct nn_Drive {
|
typedef struct nn_Drive {
|
||||||
// The capacity of the drive.
|
// The capacity of the drive.
|
||||||
@@ -1191,64 +1000,9 @@ typedef struct nn_VDrive {
|
|||||||
} nn_VDrive;
|
} nn_VDrive;
|
||||||
|
|
||||||
extern const nn_Drive nn_defaultDrives[4];
|
extern const nn_Drive nn_defaultDrives[4];
|
||||||
|
extern const nn_Drive nn_floppyDrive;
|
||||||
|
|
||||||
nn_ComponentState *nn_createDrive(nn_Universe *universe, const nn_Drive *drive, nn_DriveHandler *handler, void *userdata);
|
// Screen class
|
||||||
nn_ComponentState *nn_createVDrive(nn_Universe *universe, const nn_Drive *drive, const nn_VDrive *vdrive);
|
|
||||||
|
|
||||||
typedef enum nn_ScreenAction {
|
|
||||||
// instance dropped
|
|
||||||
NN_SCR_DROP,
|
|
||||||
// free screen state
|
|
||||||
NN_SCR_FREE,
|
|
||||||
|
|
||||||
// set w to 1 if it is on, or 0 if it is off.
|
|
||||||
NN_SCR_ISON,
|
|
||||||
// attempt to turn the screen on.
|
|
||||||
// set w to 1 if it was on, or 0 if it was off.
|
|
||||||
// set h to 1 if it is now on, or 0 if it is now off.
|
|
||||||
NN_SCR_TURNON,
|
|
||||||
// attempt to turn the screen off.
|
|
||||||
// set w to 1 if it was on, or 0 if it was off.
|
|
||||||
// set h to 1 if it is now on, or 0 if it is now off.
|
|
||||||
NN_SCR_TURNOFF,
|
|
||||||
// get a keyboard. The index requested is stored in h.
|
|
||||||
// If the index is out of bounds, set keyboard to NULL.
|
|
||||||
// Else, write the keyboard address into the buffer in keyboard.
|
|
||||||
// The capacity of the buffer is stored in w.
|
|
||||||
NN_SCR_GETKEYBOARD,
|
|
||||||
// change the screen to/from precise mode.
|
|
||||||
// Precise mode means mouse events will have real-number coordinates, as opposed to integer-based ones.
|
|
||||||
// NeoNucleus does not automatically round this, you are meant to round it.
|
|
||||||
// The new precision value is stored in w, where it is a 1 to enable it and 0 to disable it.
|
|
||||||
// Set w to 1 if precise mode is now enabled, or 0 if it isn't.
|
|
||||||
NN_SCR_SETPRECISE,
|
|
||||||
// Set w to 1 if precise mode is enabled, or 0 if it isn't.
|
|
||||||
NN_SCR_ISPRECISE,
|
|
||||||
// change the screen to/from inverted touch mode.
|
|
||||||
// Inverted touch mode normally provides an alternative way to interact with the touchscreen.
|
|
||||||
// For example, in OC, it makes the GUI only open with shift+rightclick, and normal rightclick
|
|
||||||
// triggers a touch event instead. It is best to give it an equivalent meaning to OC's to prevent
|
|
||||||
// unexpected program behavior.
|
|
||||||
// The new inverted touch mode state is stored in w, where it is a 1 to enable it and 0 to disable it.
|
|
||||||
// Set w to 1 if inverted touch mode is now enabled, or 0 if it isn't.
|
|
||||||
NN_SCR_SETTOUCHINVERTED,
|
|
||||||
// Set w to 1 if inverted touch mode is enabled, or 0 if it isn't.
|
|
||||||
NN_SCR_ISTOUCHINVERTED,
|
|
||||||
// Gets the aspect ratio (amount of screen blocks joined together).
|
|
||||||
// Outside of MC, this may not make much sense, in which case you can just set it to 1x1.
|
|
||||||
// Store the width in w and the height in h.
|
|
||||||
NN_SCR_GETASPECTRATIO,
|
|
||||||
} nn_ScreenAction;
|
|
||||||
|
|
||||||
typedef struct nn_ScreenRequest {
|
|
||||||
void *userdata;
|
|
||||||
void *instance;
|
|
||||||
nn_Computer *computer;
|
|
||||||
nn_ScreenAction action;
|
|
||||||
int w;
|
|
||||||
int h;
|
|
||||||
char *keyboard;
|
|
||||||
} nn_ScreenRequest;
|
|
||||||
|
|
||||||
typedef enum nn_ScreenFeatures {
|
typedef enum nn_ScreenFeatures {
|
||||||
NN_SCRF_NONE = 0,
|
NN_SCRF_NONE = 0,
|
||||||
@@ -1270,196 +1024,30 @@ typedef enum nn_ScreenFeatures {
|
|||||||
// however it exists as a runtime reference of what
|
// however it exists as a runtime reference of what
|
||||||
// the conventional screen tiers are.
|
// the conventional screen tiers are.
|
||||||
typedef struct nn_ScreenConfig {
|
typedef struct nn_ScreenConfig {
|
||||||
|
// maximum width
|
||||||
int maxWidth;
|
int maxWidth;
|
||||||
|
// maximum height
|
||||||
int maxHeight;
|
int maxHeight;
|
||||||
|
// screen features
|
||||||
nn_ScreenFeatures 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;
|
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;
|
char maxDepth;
|
||||||
} nn_ScreenConfig;
|
} nn_ScreenConfig;
|
||||||
|
|
||||||
// OC has 3 tiers, NN adds a 4th one as well.
|
// OC has 3 tiers, NN adds a 4th one as well.
|
||||||
extern const nn_ScreenConfig nn_defaultScreens[4];
|
extern const nn_ScreenConfig nn_defaultScreens[4];
|
||||||
|
|
||||||
typedef nn_Exit nn_ScreenHandler(nn_ScreenRequest *req);
|
// GPU class
|
||||||
|
|
||||||
nn_ComponentState *nn_createScreen(nn_Universe *universe, nn_ScreenHandler *handler, void *userdata);
|
|
||||||
// a useless component which does nothing
|
|
||||||
nn_ComponentState *nn_createKeyboard(nn_Universe *universe);
|
|
||||||
|
|
||||||
// Remember:
|
|
||||||
// - Colors are in 0xRRGGBB format.
|
|
||||||
// - Screen coordinates and palettes are 1-indexed.
|
|
||||||
// - If NN_GPU_SETRESOLUTION returns NN_OK, a screen_resized signal is queued automatically.
|
|
||||||
// - VRAM is always fast
|
|
||||||
typedef enum nn_GPUAction {
|
|
||||||
// instance dropped
|
|
||||||
NN_GPU_DROP,
|
|
||||||
// component state dropped
|
|
||||||
NN_GPU_FREE,
|
|
||||||
|
|
||||||
// Conventional GPU functions
|
|
||||||
|
|
||||||
// requests to bind to a screen connected to the computer.
|
|
||||||
// The address is stored in text, with the length in width.
|
|
||||||
// The interface does check that the computer does have the screen, but do look out
|
|
||||||
// for time-of-check/time-of-use issues which may occur in multi-threaded environments.
|
|
||||||
// If x is set to 1, the reset flag is enabled. This means the GPU should "reset" the state
|
|
||||||
// of the screen.
|
|
||||||
NN_GPU_BIND,
|
|
||||||
// requests to unbind the GPU from its screen.
|
|
||||||
// If there is no screen, it just does nothing.
|
|
||||||
NN_GPU_UNBIND,
|
|
||||||
// Ask for the screen the GPU is currently bound to.
|
|
||||||
// If it is not bound to any, text should be set to NULL.
|
|
||||||
// If it is, you must write to text the address of the screen.
|
|
||||||
// width stores the capacity of text, so if needed, truncate it to that many bytes.
|
|
||||||
// The length of this address must be stored in width.
|
|
||||||
NN_GPU_GETSCREEN,
|
|
||||||
// Gets the current background.
|
|
||||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
|
||||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
|
||||||
NN_GPU_GETBACKGROUND,
|
|
||||||
// Sets the current background.
|
|
||||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
|
||||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
|
||||||
// The values x and y should be updated to reflect the old state.
|
|
||||||
NN_GPU_SETBACKGROUND,
|
|
||||||
// Gets the current foreground.
|
|
||||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
|
||||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
|
||||||
NN_GPU_GETFOREGROUND,
|
|
||||||
// Sets the current foreground.
|
|
||||||
// x should store either the color in 0xRRGGBB format or the palette index.
|
|
||||||
// y should be 1 if x is a palette index and 0 if it is a color.
|
|
||||||
// The values x and y should be updated to reflect the old state.
|
|
||||||
NN_GPU_SETFOREGROUND,
|
|
||||||
// Gets the palette color.
|
|
||||||
// x is the index.
|
|
||||||
// y should be set to the color.
|
|
||||||
NN_GPU_GETPALETTECOLOR,
|
|
||||||
// Gets the palette color.
|
|
||||||
// x is the index.
|
|
||||||
// y is the color.
|
|
||||||
NN_GPU_SETPALETTECOLOR,
|
|
||||||
// Gets the maximum depth supported by the GPU and screen.
|
|
||||||
// Valid depth values in OC are 1, 4 and 8, however NN also recognizes 2, 3, 16 and 24.
|
|
||||||
// The result should be stored in x.
|
|
||||||
NN_GPU_MAXDEPTH,
|
|
||||||
// Gets the current depth the screen is displaying at.
|
|
||||||
// The result should be stored in x.
|
|
||||||
NN_GPU_GETDEPTH,
|
|
||||||
// Sets the current depth the screen is displaying at.
|
|
||||||
// The new depth is in x.
|
|
||||||
// This should not change the stored color values of neither the palette nor the characters,
|
|
||||||
// but simply change what their color is translated to graphically.
|
|
||||||
// The old depth should be stored in x.
|
|
||||||
NN_GPU_SETDEPTH,
|
|
||||||
// Gets the maximum resolution supported by the GPU and screen.
|
|
||||||
// Result should be in width and height.
|
|
||||||
NN_GPU_MAXRESOLUTION,
|
|
||||||
// Gets the resolution of the screen.
|
|
||||||
// Result should be in width and height.
|
|
||||||
NN_GPU_GETRESOLUTION,
|
|
||||||
// Sets the resolution of the screen.
|
|
||||||
// The new resolution should be stored in width and height.
|
|
||||||
// If successful, a screen_resized event is implicitly queued.
|
|
||||||
NN_GPU_SETRESOLUTION,
|
|
||||||
// Gets the current screen viewport.
|
|
||||||
// The result should be in width and height.
|
|
||||||
NN_GPU_GETVIEWPORT,
|
|
||||||
// Sets the screen viewport.
|
|
||||||
// The new viewport dimensions are stored in width and height.
|
|
||||||
NN_GPU_SETVIEWPORT,
|
|
||||||
// Gets a character.
|
|
||||||
// The position requested is given in x and y.
|
|
||||||
// The codepoint of the character should be set in [codepoint].
|
|
||||||
// The foreground and background color should be set in [width] and [height].
|
|
||||||
// The palette indexes of the foreground and background should be set
|
|
||||||
// in [dest] and [src] respectively. If the pixel color was not from
|
|
||||||
// the palette, the imaginary -1 palette index can be used.
|
|
||||||
NN_GPU_GET,
|
|
||||||
// Sets a horizontal line of text at a given x, y.
|
|
||||||
// The position is stored in x, y, and is the position of the first character.
|
|
||||||
// The text goes left-to-right on the horizontal line. Anything off-screen is discared.
|
|
||||||
// There is no wrapping.
|
|
||||||
// The text is stored in text, with the size of the text, in bytes, being stored in width.
|
|
||||||
NN_GPU_SET,
|
|
||||||
// like NN_GPU_SET, but the text is set vertically.
|
|
||||||
// This means instead of going from left-to-right on the screen on a horizontal line,
|
|
||||||
// it is up-to-down on a vertical line.
|
|
||||||
NN_GPU_SETVERTICAL,
|
|
||||||
// Copies a portion of the screen to another location.
|
|
||||||
// The rectangle being copied is width x height, and has the top-left corner at x, y.
|
|
||||||
// The destination rectangle is also width x height, but has the top-left corner at x + tx, y + ty.
|
|
||||||
// The copy happens as if it is using an intermediary buffer, thus even if the source and destination
|
|
||||||
// intersect, the order in which characters are copied must not change the result.
|
|
||||||
NN_GPU_COPY,
|
|
||||||
// Fills a rectangle
|
|
||||||
// The rectangle's top-left corner is at x, y, and its dimensions are width x height.
|
|
||||||
// The character it should be filled with has its unicode codepoint stored in codepoint.
|
|
||||||
NN_GPU_FILL,
|
|
||||||
|
|
||||||
// VRAM buffers (always blazing fast)
|
|
||||||
|
|
||||||
// Should return the current active buffer.
|
|
||||||
// 0 for the screen, or if there is no screen.
|
|
||||||
// The result should be stored in x.
|
|
||||||
NN_GPU_GETACTIVEBUFFER,
|
|
||||||
// Switches the active buffer to a new one, stored in x.
|
|
||||||
NN_GPU_SETACTIVEBUFFER,
|
|
||||||
// Gets a buffer by index in an imaginary list containing all of them.
|
|
||||||
// The index is in x, the buffer is output in y.
|
|
||||||
// If y is 0, the sequence is assumed to end.
|
|
||||||
NN_GPU_BUFFERS,
|
|
||||||
// Allocates a buffer.
|
|
||||||
// The buffer sizes are in width and height, with 0 x 0 meaning max resolution (default).
|
|
||||||
// This consumes exactly width * height VRAM.
|
|
||||||
// The new buffer should be put in x.
|
|
||||||
// If there was not enough VRAM for this, x can be set to 0.
|
|
||||||
NN_GPU_ALLOCBUFFER,
|
|
||||||
// Frees a buffer.
|
|
||||||
// The buffer is stored in x.
|
|
||||||
// This releases the same VRAM that the buffer consumed when allocated.
|
|
||||||
NN_GPU_FREEBUFFER,
|
|
||||||
// Frees all buffers. The free VRAM should be equal to the total VRAM after this.
|
|
||||||
NN_GPU_FREEBUFFERS,
|
|
||||||
// Gets memory info about the GPU.
|
|
||||||
// x should be set to the amount of free VRAM available.
|
|
||||||
NN_GPU_FREEMEM,
|
|
||||||
// Gets the size of a buffer, stored in x.
|
|
||||||
// The size should be stored in width and height.
|
|
||||||
NN_GPU_GETBUFFERSIZE,
|
|
||||||
// Copy a region between buffers or between the screen and buffers.
|
|
||||||
// The destination buffer is stored in dest. If 0, it refers to the screen.
|
|
||||||
// The source buffer is stored in src. If 0, it refers to the screen.
|
|
||||||
// x, y, width and height define the source rectangle, in the same way as in fill, to copy from the source buffer.
|
|
||||||
// tx, ty refer to the top-left corner for the destination rectangle, in the destination buffer. It has the same width
|
|
||||||
// and height as the source rectangle.
|
|
||||||
// Screen-to-screen copies are illegal and checked, no need to worry about handling them.
|
|
||||||
NN_GPU_BITBLT,
|
|
||||||
} nn_GPUAction;
|
|
||||||
|
|
||||||
typedef struct nn_GPURequest {
|
|
||||||
void *userdata;
|
|
||||||
void *instance;
|
|
||||||
nn_Computer *computer;
|
|
||||||
struct nn_GPU *gpuConf;
|
|
||||||
nn_GPUAction action;
|
|
||||||
int x;
|
|
||||||
int y;
|
|
||||||
int width;
|
|
||||||
int height;
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
int tx;
|
|
||||||
int ty;
|
|
||||||
};
|
|
||||||
nn_codepoint codepoint;
|
|
||||||
char *text;
|
|
||||||
};
|
|
||||||
int dest;
|
|
||||||
int src;
|
|
||||||
} nn_GPURequest;
|
|
||||||
|
|
||||||
typedef struct nn_GPU {
|
typedef struct nn_GPU {
|
||||||
// the minimum between these and the screen's
|
// the minimum between these and the screen's
|
||||||
@@ -1485,13 +1073,9 @@ typedef struct nn_GPU {
|
|||||||
double energyPerClear;
|
double energyPerClear;
|
||||||
} nn_GPU;
|
} nn_GPU;
|
||||||
|
|
||||||
typedef nn_Exit nn_GPUHandler(nn_GPURequest *req);
|
|
||||||
|
|
||||||
// 1 GPU tier for every screen.
|
// 1 GPU tier for every screen.
|
||||||
extern const nn_GPU nn_defaultGPUs[4];
|
extern const nn_GPU nn_defaultGPUs[4];
|
||||||
|
|
||||||
nn_ComponentState *nn_createGPU(nn_Universe *universe, const nn_GPU *gpu, nn_GPUHandler *handler, void *userdata);
|
|
||||||
|
|
||||||
// Colors and palettes.
|
// Colors and palettes.
|
||||||
// Do note that the
|
// Do note that the
|
||||||
|
|
||||||
@@ -1518,9 +1102,8 @@ void nn_initPalettes();
|
|||||||
int nn_mapColor(int color, int *palette, size_t len);
|
int nn_mapColor(int color, int *palette, size_t len);
|
||||||
// Expensive.
|
// Expensive.
|
||||||
// Maps a color within a given depth.
|
// Maps a color within a given depth.
|
||||||
// ocCompatible only matters for 4-bit, and determines whether to use the OC palette or the MC palette.
|
|
||||||
// Invalid depths behave identically to 24-bit, in which case the color is left unchanged.
|
// Invalid depths behave identically to 24-bit, in which case the color is left unchanged.
|
||||||
int nn_mapDepth(int color, int depth, bool ocCompatible);
|
int nn_mapDepth(int color, int depth);
|
||||||
|
|
||||||
// the name of a depth, if valid.
|
// the name of a depth, if valid.
|
||||||
// If invalid, NULL is returned, thus this can be used to check
|
// If invalid, NULL is returned, thus this can be used to check
|
||||||
|
|||||||
Reference in New Issue
Block a user