major restructuring, prepare for smp
This commit is contained in:
1 parent
b3569a2b90
commit
6bff37746d
229 files changed
+1617
-1489
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,388 @@
|
||||
// doom.c — PureDOOM port
|
||||
// Native personality carrying all the weight.
|
||||
// Drop doom1.wad (or doom.wad) next to this binary on your FAT32 volume.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define DOOM_IMPLEMENTATION
|
||||
#include "PureDOOM.h"
|
||||
|
||||
int _fltused;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_print_fn
|
||||
// ---------------------------------------------------------------------------
|
||||
static void doom_print_wrapper(const char* msg)
|
||||
{
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(msg) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_malloc_fn / doom_free_fn
|
||||
// ---------------------------------------------------------------------------
|
||||
static void* doom_malloc_wrapper(int size)
|
||||
{
|
||||
void* p = SysAlloc(size);
|
||||
if (!p) SysExitProcess(1);
|
||||
return p;
|
||||
}
|
||||
|
||||
static void doom_free_wrapper(void* ptr)
|
||||
{
|
||||
if (!ptr) return;
|
||||
|
||||
SysFree(ptr);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// File I/O wrappers
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct DOOM_FILE_HANDLE {
|
||||
uint32_t Handle;
|
||||
uint64_t Pos;
|
||||
uint64_t Size;
|
||||
} DOOM_FILE_HANDLE;
|
||||
|
||||
static void* doom_open_wrapper(const char* path, const char* mode)
|
||||
{
|
||||
uint32_t handle = 0;
|
||||
uint32_t result;
|
||||
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), &handle };
|
||||
if (mode[0] == 'w') {
|
||||
result = SysCreateFile(&req);
|
||||
} else {
|
||||
result = SysOpenFile(&req);
|
||||
}
|
||||
if (result != 0) return 0;
|
||||
|
||||
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)SysAlloc(sizeof(DOOM_FILE_HANDLE));
|
||||
if (!f) {
|
||||
SysCloseHandle(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
f->Handle = handle;
|
||||
f->Pos = 0;
|
||||
f->Size = SysGetFileSize(handle);
|
||||
return f;
|
||||
}
|
||||
|
||||
static void doom_close_wrapper(void* handle)
|
||||
{
|
||||
if (!handle) return;
|
||||
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||
SysCloseHandle(f->Handle);
|
||||
SysFree(f);
|
||||
}
|
||||
|
||||
static int doom_read_wrapper(void* handle, void* buf, int count)
|
||||
{
|
||||
if (!handle || count <= 0) return 0;
|
||||
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||
size_t read = 0;
|
||||
SysReadFile(f->Handle, buf, (size_t)count, &read);
|
||||
f->Pos += read;
|
||||
if (f->Pos > f->Size) f->Size = f->Pos;
|
||||
return (int)read;
|
||||
}
|
||||
|
||||
static int doom_write_wrapper(void* handle, const void* buf, int count)
|
||||
{
|
||||
if (!handle || count <= 0) return 0;
|
||||
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||
size_t written = 0;
|
||||
SysWriteFile(f->Handle, buf, (size_t)count, &written);
|
||||
f->Pos += written;
|
||||
if (f->Pos > f->Size) f->Size = f->Pos;
|
||||
return (int)written;
|
||||
}
|
||||
|
||||
static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin)
|
||||
{
|
||||
if (!handle) return -1;
|
||||
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||
int64_t base = 0;
|
||||
|
||||
switch (origin) {
|
||||
case DOOM_SEEK_SET:
|
||||
base = 0;
|
||||
break;
|
||||
case DOOM_SEEK_CUR:
|
||||
base = (int64_t)f->Pos;
|
||||
break;
|
||||
case DOOM_SEEK_END:
|
||||
base = (int64_t)f->Size;
|
||||
break;
|
||||
default:
|
||||
base = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
int64_t target = base + (int64_t)offset;
|
||||
if (target < 0) target = 0;
|
||||
|
||||
uint32_t s = SysSeekFile(f->Handle, (uint64_t)target);
|
||||
if (s != 0) return -1;
|
||||
f->Pos = (uint64_t)target;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int doom_tell_wrapper(void* handle)
|
||||
{
|
||||
if (!handle) return -1;
|
||||
return (int)((DOOM_FILE_HANDLE*)handle)->Pos;
|
||||
}
|
||||
|
||||
static int doom_eof_wrapper(void* handle)
|
||||
{
|
||||
if (!handle) return 1;
|
||||
DOOM_FILE_HANDLE* f = (DOOM_FILE_HANDLE*)handle;
|
||||
return (f->Pos >= f->Size) ? 1 : 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_gettime_fn
|
||||
// ---------------------------------------------------------------------------
|
||||
static void doom_gettime_wrapper(int* sec, int* usec)
|
||||
{
|
||||
uint64_t t = SysGetTick();
|
||||
*sec = (int)(t / 1000000); // 1,000,000 us = 1 second
|
||||
*usec = (int)(t % 1000000); // Remainder in microseconds
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_exit_fn
|
||||
// ---------------------------------------------------------------------------
|
||||
static void doom_exit_wrapper(int code)
|
||||
{
|
||||
SysExitProcess(code);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// doom_getenv_fn
|
||||
// ---------------------------------------------------------------------------
|
||||
static char* doom_getenv_wrapper(const char* name)
|
||||
{
|
||||
if (strcmp(name, "HOME") == 0) {
|
||||
static char home[] = ".";
|
||||
doom_print_wrapper("getenv: Returning HOME="); doom_print_wrapper(home); doom_print_wrapper("\n");
|
||||
return home;
|
||||
}
|
||||
doom_print_wrapper("getenv: Unknown variable "); doom_print_wrapper(name); doom_print_wrapper("\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Input — Maps Scancode Set 1 values to PureDOOM
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct { uint8_t scancode; bool extended; doom_key_t key; } KeyMap;
|
||||
|
||||
static const KeyMap g_keymap[] = {
|
||||
// Letters
|
||||
{ 0x11, false, DOOM_KEY_W },
|
||||
{ 0x1E, false, DOOM_KEY_A },
|
||||
{ 0x1F, false, DOOM_KEY_S },
|
||||
{ 0x20, false, DOOM_KEY_D },
|
||||
{ 0x12, false, DOOM_KEY_E },
|
||||
{ 0x10, false, DOOM_KEY_Q },
|
||||
{ 0x15, false, DOOM_KEY_Y },
|
||||
{ 0x31, false, DOOM_KEY_N },
|
||||
|
||||
// Arrow keys
|
||||
{ 0x48, true, DOOM_KEY_UP_ARROW },
|
||||
{ 0x50, true, DOOM_KEY_DOWN_ARROW },
|
||||
{ 0x4B, true, DOOM_KEY_LEFT_ARROW },
|
||||
{ 0x4D, true, DOOM_KEY_RIGHT_ARROW },
|
||||
|
||||
// Action keys
|
||||
{ 0x1D, false, DOOM_KEY_CTRL },
|
||||
{ 0x39, false, DOOM_KEY_SPACE },
|
||||
{ 0x2A, false, DOOM_KEY_SHIFT },
|
||||
{ 0x38, false, DOOM_KEY_ALT },
|
||||
|
||||
// Navigation
|
||||
{ 0x01, false, DOOM_KEY_ESCAPE },
|
||||
{ 0x1C, false, DOOM_KEY_ENTER },
|
||||
{ 0x0F, false, DOOM_KEY_TAB },
|
||||
{ 0x0E, false, DOOM_KEY_BACKSPACE },
|
||||
|
||||
// Weapon slots
|
||||
{ 0x02, false, DOOM_KEY_1 },
|
||||
{ 0x03, false, DOOM_KEY_2 },
|
||||
{ 0x04, false, DOOM_KEY_3 },
|
||||
{ 0x05, false, DOOM_KEY_4 },
|
||||
{ 0x06, false, DOOM_KEY_5 },
|
||||
{ 0x07, false, DOOM_KEY_6 },
|
||||
{ 0x08, false, DOOM_KEY_7 },
|
||||
|
||||
// Misc
|
||||
{ 0x0C, false, DOOM_KEY_MINUS },
|
||||
{ 0x0D, false, DOOM_KEY_EQUALS },
|
||||
{ 0x33, false, DOOM_KEY_COMMA },
|
||||
{ 0x34, false, DOOM_KEY_PERIOD },
|
||||
|
||||
// Function keys
|
||||
{ 0x3B, false, DOOM_KEY_F1 },
|
||||
{ 0x3C, false, DOOM_KEY_F2 },
|
||||
{ 0x3D, false, DOOM_KEY_F3 },
|
||||
{ 0x3E, false, DOOM_KEY_F4 },
|
||||
{ 0x3F, false, DOOM_KEY_F5 },
|
||||
{ 0x40, false, DOOM_KEY_F6 },
|
||||
{ 0x41, false, DOOM_KEY_F7 },
|
||||
{ 0x42, false, DOOM_KEY_F8 },
|
||||
{ 0x43, false, DOOM_KEY_F9 },
|
||||
{ 0x44, false, DOOM_KEY_F10 },
|
||||
{ 0x57, false, DOOM_KEY_F11 },
|
||||
{ 0x58, false, DOOM_KEY_F12 },
|
||||
};
|
||||
|
||||
#define KEYMAP_COUNT (sizeof(g_keymap) / sizeof(g_keymap[0]))
|
||||
|
||||
static bool g_prev_key[KEYMAP_COUNT];
|
||||
|
||||
static void poll_input(void)
|
||||
{
|
||||
for (int i = 0; i < (int)KEYMAP_COUNT; i++) {
|
||||
bool cur_down = (SysKbIsKeyDown(g_keymap[i].scancode, g_keymap[i].extended) != 0);
|
||||
bool prev_down = g_prev_key[i];
|
||||
|
||||
if (cur_down && !prev_down) doom_key_down(g_keymap[i].key);
|
||||
if (!cur_down && prev_down) doom_key_up (g_keymap[i].key);
|
||||
|
||||
g_prev_key[i] = cur_down;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Render — Performs lightweight software upscale and blits to the screen
|
||||
// ---------------------------------------------------------------------------
|
||||
#define DOOM_W 320
|
||||
#define DOOM_H 200
|
||||
#define SCALE 3
|
||||
#define DISP_W (DOOM_W * SCALE)
|
||||
#define DISP_H (DOOM_H * SCALE)
|
||||
|
||||
static uint32_t g_blit_buf[DISP_W * DISP_H];
|
||||
|
||||
static void render_frame(const uint8_t* rgba)
|
||||
{
|
||||
const uint32_t* src = (const uint32_t*)rgba;
|
||||
|
||||
// Software upscale loop mapping 320x200 buffer directly to scaled window buffer
|
||||
for (int y = 0; y < DOOM_H; y++) {
|
||||
for (int x = 0; x < DOOM_W; x++) {
|
||||
uint32_t p = src[y * DOOM_W + x];
|
||||
|
||||
// RGBA -> BGRA conversion (standard for linear x86/64 framebuffers)
|
||||
uint32_t bgra = (p & 0xFF00FF00u)
|
||||
| ((p & 0x00FF0000u) >> 16)
|
||||
| ((p & 0x000000FFu) << 16);
|
||||
|
||||
// Populate SCALE * SCALE pixel block
|
||||
for (int sy = 0; sy < SCALE; sy++) {
|
||||
for (int sx = 0; sx < SCALE; sx++) {
|
||||
g_blit_buf[(y * SCALE + sy) * DISP_W + (x * SCALE + sx)] = bgra;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SysScreenBlit(0, 0, DISP_W, DISP_H, g_blit_buf);
|
||||
}
|
||||
|
||||
static int build_argv_from_command_line(const char* cmdline, char* storage, size_t storageSize,
|
||||
const char** argv, int maxArgv) {
|
||||
if (!storage || storageSize == 0 || !argv || maxArgv <= 1) return 0;
|
||||
|
||||
if (!cmdline || cmdline[0] == '\0') cmdline = "doom";
|
||||
|
||||
size_t len = strlen(cmdline);
|
||||
if (len >= storageSize) len = storageSize - 1;
|
||||
memcpy(storage, cmdline, len);
|
||||
storage[len] = '\0';
|
||||
|
||||
int argc = 0;
|
||||
char* p = storage;
|
||||
while (*p && argc < maxArgv - 1) {
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
if (!*p) break;
|
||||
|
||||
if (*p == '"') {
|
||||
p++;
|
||||
argv[argc++] = p;
|
||||
while (*p && *p != '"') p++;
|
||||
if (*p) *p++ = '\0';
|
||||
} else {
|
||||
argv[argc++] = p;
|
||||
while (*p && *p != ' ' && *p != '\t') p++;
|
||||
if (*p) *p++ = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
argv[argc] = 0;
|
||||
if (argc == 0) {
|
||||
argv[0] = "doom";
|
||||
argv[1] = 0;
|
||||
argc = 1;
|
||||
}
|
||||
return argc;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// main
|
||||
// ---------------------------------------------------------------------------
|
||||
int main(void)
|
||||
{
|
||||
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
||||
|
||||
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: starting\n") }; SysWriteConsole(&req); } while (0);
|
||||
|
||||
// Wire up PureDOOM core mechanics
|
||||
doom_set_print(doom_print_wrapper);
|
||||
doom_set_malloc(doom_malloc_wrapper, doom_free_wrapper);
|
||||
doom_set_file_io(
|
||||
doom_open_wrapper,
|
||||
doom_close_wrapper,
|
||||
doom_read_wrapper,
|
||||
doom_write_wrapper,
|
||||
doom_seek_wrapper,
|
||||
doom_tell_wrapper,
|
||||
doom_eof_wrapper
|
||||
);
|
||||
doom_set_gettime(doom_gettime_wrapper);
|
||||
doom_set_exit(doom_exit_wrapper);
|
||||
doom_set_getenv(doom_getenv_wrapper);
|
||||
|
||||
// Resolution + modern WASD bindings
|
||||
doom_set_resolution(DOOM_W, DOOM_H);
|
||||
doom_set_default_int("key_up", DOOM_KEY_W);
|
||||
doom_set_default_int("key_down", DOOM_KEY_S);
|
||||
doom_set_default_int("key_strafeleft", DOOM_KEY_A);
|
||||
doom_set_default_int("key_straferight", DOOM_KEY_D);
|
||||
doom_set_default_int("key_use", DOOM_KEY_E);
|
||||
doom_set_default_int("mouse_move", 0);
|
||||
|
||||
char argvStorage[512];
|
||||
const char* argv[32];
|
||||
int argc = build_argv_from_command_line(cmdline, argvStorage, sizeof(argvStorage), argv, 32);
|
||||
doom_init(argc, argv, 0);
|
||||
|
||||
do { SYS_WRITE_CONSOLE_REQUEST req = { SYS_SV_LITERAL("doom: entering game loop\n") }; SysWriteConsole(&req); } while (0);
|
||||
|
||||
// Core execution loop
|
||||
while (1) {
|
||||
poll_input();
|
||||
doom_update();
|
||||
render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */));
|
||||
}
|
||||
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,494 @@
|
||||
// edit.c - tiny native text editor for nt-lite
|
||||
// Controls:
|
||||
// arrows/home/end/pageup/pagedown move
|
||||
// enter/backspace/delete/tab edit
|
||||
// ctrl+s save
|
||||
// ctrl+q quit (press twice if dirty)
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define GLYPH_W 8
|
||||
#define GLYPH_H 16
|
||||
|
||||
#define MAX_LINES 512
|
||||
#define MAX_LINE 256
|
||||
#define PATH_MAX2 260
|
||||
#define MSG_MAX 160
|
||||
|
||||
#define COL_BG 0x000000u
|
||||
#define COL_FG 0xC8C8C8u
|
||||
#define COL_DIM 0x808080u
|
||||
#define COL_STATUS 0x303030u
|
||||
#define COL_STATUSF 0xFFFFFFu
|
||||
#define COL_CURSOR 0xE0E0E0u
|
||||
#define COL_CURSORF 0x000000u
|
||||
|
||||
#define MOD_LSHIFT (1u << 0)
|
||||
#define MOD_RSHIFT (1u << 1)
|
||||
#define MOD_LCTRL (1u << 2)
|
||||
#define MOD_RCTRL (1u << 3)
|
||||
#define MOD_CAPS (1u << 6)
|
||||
#define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT)
|
||||
#define MOD_CTRL (MOD_LCTRL | MOD_RCTRL)
|
||||
|
||||
#define SC_ESC 0x01u
|
||||
#define SC_BACKSPACE 0x0Eu
|
||||
#define SC_TAB 0x0Fu
|
||||
#define SC_Q 0x10u
|
||||
#define SC_S 0x1Fu
|
||||
#define SC_ENTER 0x1Cu
|
||||
#define SC_HOME 0x47u
|
||||
#define SC_UP 0x48u
|
||||
#define SC_PGUP 0x49u
|
||||
#define SC_LEFT 0x4Bu
|
||||
#define SC_RIGHT 0x4Du
|
||||
#define SC_END 0x4Fu
|
||||
#define SC_DOWN 0x50u
|
||||
#define SC_PGDN 0x51u
|
||||
#define SC_DEL 0x53u
|
||||
|
||||
typedef struct LINE {
|
||||
int len;
|
||||
char text[MAX_LINE];
|
||||
} LINE;
|
||||
|
||||
static LINE gLines[MAX_LINES];
|
||||
static int gLineCount = 1;
|
||||
static int gCx = 0;
|
||||
static int gCy = 0;
|
||||
static int gTop = 0;
|
||||
static int gLeft = 0;
|
||||
static int gCols = 80;
|
||||
static int gRows = 25;
|
||||
static bool gDirty = false;
|
||||
static bool gQuitArmed = false;
|
||||
static char gFilename[PATH_MAX2] = "untitled.txt";
|
||||
static char gMessage[MSG_MAX] = "ready";
|
||||
|
||||
static const char gUnshifted[128] = {
|
||||
0, 0, '1','2','3','4','5','6','7','8','9','0','-','=','\b','\t',
|
||||
'q','w','e','r','t','y','u','i','o','p','[',']','\n', 0, 'a','s',
|
||||
'd','f','g','h','j','k','l',';','\'','`', 0, '\\','z','x','c','v',
|
||||
'b','n','m',',','.','/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.'
|
||||
};
|
||||
|
||||
static const char gShifted[128] = {
|
||||
0, 0, '!','@','#','$','%','^','&','*','(',')','_','+','\b','\t',
|
||||
'Q','W','E','R','T','Y','U','I','O','P','{','}','\n', 0, 'A','S',
|
||||
'D','F','G','H','J','K','L',':','"','~', 0, '|','Z','X','C','V',
|
||||
'B','N','M','<','>','?', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, '7','8','9','-', '4','5','6','+', '1','2','3','0','.'
|
||||
};
|
||||
|
||||
static void put_cell(int col, int row, char c, uint32_t fg, uint32_t bg) {
|
||||
SysScreenPutChar(c ? c : ' ', col * GLYPH_W, row * GLYPH_H, fg, bg);
|
||||
}
|
||||
|
||||
static void clear_row(int row, uint32_t bg) {
|
||||
for (int x = 0; x < gCols; x++) put_cell(x, row, ' ', COL_FG, bg);
|
||||
}
|
||||
|
||||
static void draw_text(int col, int row, const char* s, uint32_t fg, uint32_t bg) {
|
||||
while (*s && col < gCols) put_cell(col++, row, *s++, fg, bg);
|
||||
}
|
||||
|
||||
static void msg_set(const char* s) {
|
||||
size_t n = strlen(s);
|
||||
if (n >= sizeof(gMessage)) n = sizeof(gMessage) - 1;
|
||||
memcpy(gMessage, s, n);
|
||||
gMessage[n] = 0;
|
||||
}
|
||||
|
||||
static void append_char(char* b, size_t cap, size_t* p, char c) {
|
||||
if (*p + 1 < cap) b[(*p)++] = c;
|
||||
b[*p < cap ? *p : cap - 1] = 0;
|
||||
}
|
||||
|
||||
static void append_str(char* b, size_t cap, size_t* p, const char* s) {
|
||||
while (*s) append_char(b, cap, p, *s++);
|
||||
}
|
||||
|
||||
static void append_u32(char* b, size_t cap, size_t* p, uint32_t v) {
|
||||
char tmp[12];
|
||||
int n = 0;
|
||||
if (v == 0) { append_char(b, cap, p, '0'); return; }
|
||||
while (v && n < (int)sizeof(tmp)) {
|
||||
tmp[n++] = (char)('0' + (v % 10u));
|
||||
v /= 10u;
|
||||
}
|
||||
while (n > 0) append_char(b, cap, p, tmp[--n]);
|
||||
}
|
||||
|
||||
static char key_to_char(uint8_t sc, uint32_t mods) {
|
||||
if (sc >= 128) return 0;
|
||||
bool shifted = (mods & MOD_SHIFT) != 0;
|
||||
if (mods & MOD_CAPS) {
|
||||
char base = gUnshifted[sc];
|
||||
if (base >= 'a' && base <= 'z') shifted = !shifted;
|
||||
}
|
||||
return shifted ? gShifted[sc] : gUnshifted[sc];
|
||||
}
|
||||
|
||||
static void clamp_cursor(void) {
|
||||
if (gLineCount < 1) gLineCount = 1;
|
||||
if (gCy < 0) gCy = 0;
|
||||
if (gCy >= gLineCount) gCy = gLineCount - 1;
|
||||
if (gCx < 0) gCx = 0;
|
||||
if (gCx > gLines[gCy].len) gCx = gLines[gCy].len;
|
||||
}
|
||||
|
||||
static void ensure_visible(void) {
|
||||
int textRows = gRows - 2;
|
||||
if (textRows < 1) textRows = 1;
|
||||
|
||||
if (gCy < gTop) gTop = gCy;
|
||||
if (gCy >= gTop + textRows) gTop = gCy - textRows + 1;
|
||||
if (gTop < 0) gTop = 0;
|
||||
|
||||
if (gCx < gLeft) gLeft = gCx;
|
||||
if (gCx >= gLeft + gCols) gLeft = gCx - gCols + 1;
|
||||
if (gLeft < 0) gLeft = 0;
|
||||
}
|
||||
|
||||
static void render(void) {
|
||||
int pxW = 0, pxH = 0;
|
||||
if (SysScreenGetSize(&pxW, &pxH) == 0 && pxW > 0 && pxH > 0) {
|
||||
gCols = pxW / GLYPH_W;
|
||||
gRows = pxH / GLYPH_H;
|
||||
if (gCols < 20) gCols = 20;
|
||||
if (gRows < 5) gRows = 5;
|
||||
}
|
||||
|
||||
clamp_cursor();
|
||||
ensure_visible();
|
||||
|
||||
char status[256];
|
||||
size_t p = 0;
|
||||
append_str(status, sizeof(status), &p, " edit ");
|
||||
append_str(status, sizeof(status), &p, gFilename);
|
||||
append_str(status, sizeof(status), &p, gDirty ? " * " : " ");
|
||||
append_str(status, sizeof(status), &p, "Ctrl+S save Ctrl+Q quit Ln ");
|
||||
append_u32(status, sizeof(status), &p, (uint32_t)(gCy + 1));
|
||||
append_str(status, sizeof(status), &p, ", Col ");
|
||||
append_u32(status, sizeof(status), &p, (uint32_t)(gCx + 1));
|
||||
|
||||
clear_row(0, COL_STATUS);
|
||||
draw_text(0, 0, status, COL_STATUSF, COL_STATUS);
|
||||
|
||||
int textRows = gRows - 2;
|
||||
for (int sy = 0; sy < textRows; sy++) {
|
||||
int row = sy + 1;
|
||||
int li = gTop + sy;
|
||||
clear_row(row, COL_BG);
|
||||
if (li >= gLineCount) continue;
|
||||
|
||||
LINE* ln = &gLines[li];
|
||||
for (int sx = 0; sx < gCols; sx++) {
|
||||
int ci = gLeft + sx;
|
||||
char c = (ci < ln->len) ? ln->text[ci] : ' ';
|
||||
bool atCursor = (li == gCy && ci == gCx);
|
||||
put_cell(sx, row, c, atCursor ? COL_CURSORF : COL_FG,
|
||||
atCursor ? COL_CURSOR : COL_BG);
|
||||
}
|
||||
}
|
||||
|
||||
clear_row(gRows - 1, COL_STATUS);
|
||||
draw_text(0, gRows - 1, gMessage, COL_STATUSF, COL_STATUS);
|
||||
}
|
||||
|
||||
static void insert_char_at_cursor(char c) {
|
||||
LINE* ln = &gLines[gCy];
|
||||
if (ln->len >= MAX_LINE - 1) { msg_set("line too long"); return; }
|
||||
memmove(&ln->text[gCx + 1], &ln->text[gCx], (size_t)(ln->len - gCx + 1));
|
||||
ln->text[gCx] = c;
|
||||
ln->len++;
|
||||
gCx++;
|
||||
gDirty = true;
|
||||
}
|
||||
|
||||
static void newline_at_cursor(void) {
|
||||
if (gLineCount >= MAX_LINES) { msg_set("too many lines"); return; }
|
||||
|
||||
for (int i = gLineCount; i > gCy + 1; i--) gLines[i] = gLines[i - 1];
|
||||
|
||||
LINE* cur = &gLines[gCy];
|
||||
LINE* next = &gLines[gCy + 1];
|
||||
int tail = cur->len - gCx;
|
||||
if (tail > 0) memcpy(next->text, &cur->text[gCx], (size_t)tail);
|
||||
next->len = tail;
|
||||
next->text[next->len] = 0;
|
||||
|
||||
cur->len = gCx;
|
||||
cur->text[cur->len] = 0;
|
||||
|
||||
gLineCount++;
|
||||
gCy++;
|
||||
gCx = 0;
|
||||
gDirty = true;
|
||||
}
|
||||
|
||||
static void backspace_at_cursor(void) {
|
||||
if (gCx > 0) {
|
||||
LINE* ln = &gLines[gCy];
|
||||
memmove(&ln->text[gCx - 1], &ln->text[gCx], (size_t)(ln->len - gCx + 1));
|
||||
ln->len--;
|
||||
gCx--;
|
||||
gDirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gCy <= 0) return;
|
||||
|
||||
LINE* prev = &gLines[gCy - 1];
|
||||
LINE* cur = &gLines[gCy];
|
||||
if (prev->len + cur->len >= MAX_LINE) { msg_set("join would make line too long"); return; }
|
||||
|
||||
int oldLen = prev->len;
|
||||
memcpy(&prev->text[prev->len], cur->text, (size_t)cur->len + 1);
|
||||
prev->len += cur->len;
|
||||
|
||||
for (int i = gCy; i < gLineCount - 1; i++) gLines[i] = gLines[i + 1];
|
||||
gLineCount--;
|
||||
gCy--;
|
||||
gCx = oldLen;
|
||||
gDirty = true;
|
||||
}
|
||||
|
||||
static void delete_at_cursor(void) {
|
||||
LINE* ln = &gLines[gCy];
|
||||
if (gCx < ln->len) {
|
||||
memmove(&ln->text[gCx], &ln->text[gCx + 1], (size_t)(ln->len - gCx));
|
||||
ln->len--;
|
||||
gDirty = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (gCy + 1 >= gLineCount) return;
|
||||
|
||||
LINE* next = &gLines[gCy + 1];
|
||||
if (ln->len + next->len >= MAX_LINE) { msg_set("join would make line too long"); return; }
|
||||
|
||||
memcpy(&ln->text[ln->len], next->text, (size_t)next->len + 1);
|
||||
ln->len += next->len;
|
||||
for (int i = gCy + 1; i < gLineCount - 1; i++) gLines[i] = gLines[i + 1];
|
||||
gLineCount--;
|
||||
gDirty = true;
|
||||
}
|
||||
|
||||
static void move_left(void) {
|
||||
if (gCx > 0) gCx--;
|
||||
else if (gCy > 0) { gCy--; gCx = gLines[gCy].len; }
|
||||
}
|
||||
|
||||
static void move_right(void) {
|
||||
if (gCx < gLines[gCy].len) gCx++;
|
||||
else if (gCy + 1 < gLineCount) { gCy++; gCx = 0; }
|
||||
}
|
||||
|
||||
static void move_up(void) {
|
||||
if (gCy > 0) gCy--;
|
||||
if (gCx > gLines[gCy].len) gCx = gLines[gCy].len;
|
||||
}
|
||||
|
||||
static void move_down(void) {
|
||||
if (gCy + 1 < gLineCount) gCy++;
|
||||
if (gCx > gLines[gCy].len) gCx = gLines[gCy].len;
|
||||
}
|
||||
|
||||
static void save_file(void) {
|
||||
uint32_t h = 0;
|
||||
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
|
||||
uint32_t st = SysCreateFile(&pathReq);
|
||||
if (st != 0) st = SysOpenFile(&pathReq);
|
||||
if (st != 0) { msg_set("save failed: cannot open file"); return; }
|
||||
|
||||
SysTruncateFile(h, 0);
|
||||
SysSeekFile(h, 0);
|
||||
|
||||
for (int i = 0; i < gLineCount; i++) {
|
||||
size_t written = 0;
|
||||
if (gLines[i].len) {
|
||||
st = SysWriteFile(h, gLines[i].text, (size_t)gLines[i].len, &written);
|
||||
if (st != 0) { SysCloseHandle(h); msg_set("save failed: write error"); return; }
|
||||
}
|
||||
if (i + 1 < gLineCount) {
|
||||
const char nl = '\n';
|
||||
st = SysWriteFile(h, &nl, 1, &written);
|
||||
if (st != 0) { SysCloseHandle(h); msg_set("save failed: write error"); return; }
|
||||
}
|
||||
}
|
||||
|
||||
SysCloseHandle(h);
|
||||
gDirty = false;
|
||||
msg_set("saved");
|
||||
}
|
||||
|
||||
static void reset_buffer(void) {
|
||||
gLineCount = 1;
|
||||
gLines[0].len = 0;
|
||||
gLines[0].text[0] = 0;
|
||||
gCx = gCy = gTop = gLeft = 0;
|
||||
gDirty = false;
|
||||
}
|
||||
|
||||
static void load_file(void) {
|
||||
reset_buffer();
|
||||
|
||||
uint32_t h = 0;
|
||||
SYS_PATH_HANDLE_REQUEST pathReq = { SysSvFromCstr(gFilename), &h };
|
||||
if (SysOpenFile(&pathReq) != 0) {
|
||||
msg_set("new file");
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[512];
|
||||
size_t got = 0;
|
||||
bool truncated = false;
|
||||
while (SysReadFile(h, buf, sizeof(buf), &got) == 0 && got > 0) {
|
||||
for (size_t i = 0; i < got; i++) {
|
||||
char c = buf[i];
|
||||
if (c == '\r') continue;
|
||||
if (c == '\n') {
|
||||
if (gLineCount < MAX_LINES) {
|
||||
gLineCount++;
|
||||
gLines[gLineCount - 1].len = 0;
|
||||
gLines[gLineCount - 1].text[0] = 0;
|
||||
} else truncated = true;
|
||||
continue;
|
||||
}
|
||||
if (c < 0x20 && c != '\t') continue;
|
||||
LINE* ln = &gLines[gLineCount - 1];
|
||||
if (ln->len + 1 < MAX_LINE) {
|
||||
ln->text[ln->len++] = (c == '\t') ? ' ' : c;
|
||||
ln->text[ln->len] = 0;
|
||||
} else truncated = true;
|
||||
}
|
||||
}
|
||||
SysCloseHandle(h);
|
||||
msg_set(truncated ? "loaded, truncated long file" : "loaded");
|
||||
}
|
||||
|
||||
static const char* skip_spaces(const char* s) {
|
||||
while (s && (*s == ' ' || *s == '\t')) s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static const char* skip_token(const char* s) {
|
||||
s = skip_spaces(s);
|
||||
if (!s || !*s) return s;
|
||||
if (*s == '"') {
|
||||
s++;
|
||||
while (*s && *s != '"') s++;
|
||||
if (*s == '"') s++;
|
||||
return s;
|
||||
}
|
||||
while (*s && *s != ' ' && *s != '\t') s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool read_token(const char* s, char* out, size_t cap) {
|
||||
s = skip_spaces(s);
|
||||
if (!s || !*s || cap == 0) return false;
|
||||
size_t n = 0;
|
||||
if (*s == '"') {
|
||||
s++;
|
||||
while (*s && *s != '"') {
|
||||
if (n + 1 < cap) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
} else {
|
||||
while (*s && *s != ' ' && *s != '\t') {
|
||||
if (n + 1 < cap) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
out[n] = 0;
|
||||
return n != 0;
|
||||
}
|
||||
|
||||
static void parse_filename_from_cmdline(void) {
|
||||
SYS_PEB* peb = SysGetPeb();
|
||||
const char* cmd = (peb && peb->CommandLine.Data) ? peb->CommandLine.Data : NULL;
|
||||
const char* arg = skip_token(cmd);
|
||||
if (read_token(arg, gFilename, sizeof(gFilename))) return;
|
||||
strcpy(gFilename, "untitled.txt");
|
||||
}
|
||||
|
||||
static void editor_loop(void) {
|
||||
while (true) {
|
||||
render();
|
||||
|
||||
uint8_t sc = SysKbReadScancode();
|
||||
uint32_t mods = SysKbGetModifiers();
|
||||
bool ctrl = (mods & MOD_CTRL) != 0;
|
||||
|
||||
if (ctrl && sc == SC_Q) {
|
||||
if (gDirty && !gQuitArmed) {
|
||||
gQuitArmed = true;
|
||||
msg_set("unsaved changes: press Ctrl+Q again to quit");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
gQuitArmed = false;
|
||||
|
||||
if (ctrl && sc == SC_S) { save_file(); continue; }
|
||||
if (sc == SC_ESC) { msg_set("Ctrl+Q quits, Ctrl+S saves"); continue; }
|
||||
|
||||
switch (sc) {
|
||||
case SC_LEFT: move_left(); break;
|
||||
case SC_RIGHT: move_right(); break;
|
||||
case SC_UP: move_up(); break;
|
||||
case SC_DOWN: move_down(); break;
|
||||
case SC_HOME: gCx = 0; break;
|
||||
case SC_END: gCx = gLines[gCy].len; break;
|
||||
case SC_PGUP: {
|
||||
int n = gRows - 2;
|
||||
if (n < 1) n = 1;
|
||||
gCy -= n;
|
||||
clamp_cursor();
|
||||
break;
|
||||
}
|
||||
case SC_PGDN: {
|
||||
int n = gRows - 2;
|
||||
if (n < 1) n = 1;
|
||||
gCy += n;
|
||||
clamp_cursor();
|
||||
break;
|
||||
}
|
||||
case SC_BACKSPACE: backspace_at_cursor(); break;
|
||||
case SC_DEL: delete_at_cursor(); break;
|
||||
case SC_ENTER: newline_at_cursor(); break;
|
||||
case SC_TAB:
|
||||
for (int i = 0; i < 4; i++) insert_char_at_cursor(' ');
|
||||
break;
|
||||
default: {
|
||||
char c = key_to_char(sc, mods);
|
||||
if (!ctrl && c >= 0x20 && c <= 0x7E) insert_char_at_cursor(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
parse_filename_from_cmdline();
|
||||
load_file();
|
||||
SysScreenClear(COL_BG);
|
||||
editor_loop();
|
||||
SysScreenClear(COL_BG);
|
||||
print("edit: goodbye\n");
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
static void print(const char* s) {
|
||||
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||
}
|
||||
|
||||
static void print_char(char c) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int n = 0;
|
||||
if (v == 0) { print("0"); return; }
|
||||
while (v && n < (int)sizeof(buf)) {
|
||||
buf[n++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (n > 0) print_char(buf[--n]);
|
||||
}
|
||||
|
||||
static void append_char(char* dst, size_t cap, size_t* off, char c) {
|
||||
if (!dst || !off || cap == 0 || *off + 1 >= cap) return;
|
||||
dst[*off] = c;
|
||||
*off += 1;
|
||||
dst[*off] = 0;
|
||||
}
|
||||
|
||||
static void append_str(char* dst, size_t cap, size_t* off, const char* s) {
|
||||
if (!s) return;
|
||||
while (*s) append_char(dst, cap, off, *s++);
|
||||
}
|
||||
|
||||
static void append_u8(char* dst, size_t cap, size_t* off, uint8_t v) {
|
||||
if (v >= 100) {
|
||||
append_char(dst, cap, off, (char)('0' + v / 100));
|
||||
v %= 100;
|
||||
append_char(dst, cap, off, (char)('0' + v / 10));
|
||||
append_char(dst, cap, off, (char)('0' + v % 10));
|
||||
} else if (v >= 10) {
|
||||
append_char(dst, cap, off, (char)('0' + v / 10));
|
||||
append_char(dst, cap, off, (char)('0' + v % 10));
|
||||
} else {
|
||||
append_char(dst, cap, off, (char)('0' + v));
|
||||
}
|
||||
}
|
||||
|
||||
static void format_ipv4(char* dst, size_t cap, const uint8_t ip[4]) {
|
||||
size_t off = 0;
|
||||
if (!dst || cap == 0) return;
|
||||
dst[0] = 0;
|
||||
append_u8(dst, cap, &off, ip[0]); append_char(dst, cap, &off, '.');
|
||||
append_u8(dst, cap, &off, ip[1]); append_char(dst, cap, &off, '.');
|
||||
append_u8(dst, cap, &off, ip[2]); append_char(dst, cap, &off, '.');
|
||||
append_u8(dst, cap, &off, ip[3]);
|
||||
}
|
||||
|
||||
static bool parse_u8(const char** p, uint8_t* out) {
|
||||
const char* s = *p;
|
||||
uint32_t v = 0;
|
||||
int digits = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10u + (uint32_t)(*s - '0');
|
||||
if (v > 255u) return false;
|
||||
s++;
|
||||
digits++;
|
||||
}
|
||||
if (digits == 0) return false;
|
||||
*out = (uint8_t)v;
|
||||
*p = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_ipv4(const char* s, uint8_t ip[4]) {
|
||||
if (!s || !ip) return false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!parse_u8(&s, &ip[i])) return false;
|
||||
if (i != 3) {
|
||||
if (*s != '.') return false;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return *s == 0;
|
||||
}
|
||||
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == 0) { out[0] = 0; return NULL; }
|
||||
|
||||
size_t n = 0;
|
||||
while (*s && *s != ' ' && *s != '\t') {
|
||||
if (n + 1 < outSize) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
out[n] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
print("usage: httpget.exe [host-or-ipv4] [path]\n");
|
||||
print("default: httpget.exe 10.0.2.2 /\n");
|
||||
}
|
||||
|
||||
static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
|
||||
if (parse_ipv4(host, ip))
|
||||
return SYS_STATUS_SUCCESS;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 resolved;
|
||||
size_t resolvedSize = sizeof(resolved);
|
||||
SYS_RESOLVE_HOST_REQUEST req;
|
||||
memset(&resolved, 0, sizeof(resolved));
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Name = SysSvFromCstr(host);
|
||||
req.Family = SYS_AF_INET;
|
||||
req.AddressOut = &resolved;
|
||||
req.AddressSizeInOut = &resolvedSize;
|
||||
|
||||
uint32_t st = SysResolveHost(&req);
|
||||
if (st != SYS_STATUS_SUCCESS)
|
||||
return st;
|
||||
if (resolved.Family != SYS_AF_INET)
|
||||
return SYS_STATUS_NOT_FOUND;
|
||||
|
||||
ip[0] = resolved.Address[0];
|
||||
ip[1] = resolved.Address[1];
|
||||
ip[2] = resolved.Address[2];
|
||||
ip[3] = resolved.Address[3];
|
||||
return SYS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
uint8_t ip[4] = {10, 0, 2, 2};
|
||||
char host[256];
|
||||
char path[128];
|
||||
strcpy(host, "10.0.2.2");
|
||||
path[0] = '/';
|
||||
path[1] = 0;
|
||||
|
||||
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||
char tok[256];
|
||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||
if (cmd) {
|
||||
const char* rest = next_token(cmd, tok, sizeof(tok));
|
||||
if (tok[0]) {
|
||||
if (strlen(tok) >= sizeof(host)) {
|
||||
print("httpget: host too long\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
strcpy(host, tok);
|
||||
cmd = rest;
|
||||
}
|
||||
if (cmd) {
|
||||
(void)next_token(cmd, tok, sizeof(tok));
|
||||
if (tok[0]) {
|
||||
size_t len = strlen(tok);
|
||||
if (len + 2 > sizeof(path)) {
|
||||
print("httpget: path too long\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
if (tok[0] != '/') {
|
||||
path[0] = '/';
|
||||
memcpy(path + 1, tok, len + 1);
|
||||
} else {
|
||||
memcpy(path, tok, len + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t st = resolve_host(host, ip);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("httpget: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
|
||||
usage();
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
char ipText[16];
|
||||
format_ipv4(ipText, sizeof(ipText), ip);
|
||||
|
||||
print("httpget: GET http://");
|
||||
print(host);
|
||||
print(path);
|
||||
print(" (");
|
||||
print(ipText);
|
||||
print(")\n");
|
||||
|
||||
uint32_t sock = 0;
|
||||
st = SysSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &sock);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("httpget: SysSocket failed "); print_u64(st); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.Family = SYS_AF_INET;
|
||||
addr.Port = 80;
|
||||
addr.Address[0] = ip[0];
|
||||
addr.Address[1] = ip[1];
|
||||
addr.Address[2] = ip[2];
|
||||
addr.Address[3] = ip[3];
|
||||
|
||||
st = SysConnect(sock, &addr, sizeof(addr));
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("httpget: SysConnect failed "); print_u64(st); print("\n");
|
||||
SysCloseHandle(sock);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
char request[512];
|
||||
size_t off = 0;
|
||||
request[0] = 0;
|
||||
append_str(request, sizeof(request), &off, "GET ");
|
||||
append_str(request, sizeof(request), &off, path);
|
||||
append_str(request, sizeof(request), &off, " HTTP/1.0\r\nHost: ");
|
||||
append_str(request, sizeof(request), &off, host);
|
||||
append_str(request, sizeof(request), &off, "\r\nUser-Agent: nt-lite-sys-socket\r\nConnection: close\r\n\r\n");
|
||||
|
||||
size_t totalSent = 0;
|
||||
while (totalSent < off) {
|
||||
size_t sent = 0;
|
||||
st = SysSend(sock, request + totalSent, off - totalSent, 0, &sent);
|
||||
if (st != SYS_STATUS_SUCCESS || sent == 0) {
|
||||
print("httpget: SysSend failed "); print_u64(st); print(" sent="); print_u64(sent); print("\n");
|
||||
SysCloseHandle(sock);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
totalSent += sent;
|
||||
}
|
||||
|
||||
char buf[513];
|
||||
size_t total = 0;
|
||||
for (;;) {
|
||||
size_t got = 0;
|
||||
st = SysRecv(sock, buf, sizeof(buf) - 1, 0, &got);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("\nhttpget: SysRecv failed "); print_u64(st); print(" after "); print_u64(total); print(" bytes\n");
|
||||
SysCloseHandle(sock);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
if (got == 0) break;
|
||||
buf[got] = 0;
|
||||
print(buf);
|
||||
total += got;
|
||||
}
|
||||
|
||||
print("\nhttpget: done, "); print_u64(total); print(" bytes\n");
|
||||
SysShutdown(sock, SYS_SHUTDOWN_BOTH);
|
||||
SysCloseHandle(sock);
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define PING_DEFAULT_TARGET "10.0.2.2"
|
||||
#define PING_DEFAULT_COUNT 4u
|
||||
#define PING_TIMEOUT_MS 1000u
|
||||
#define US_PER_MS 1000ull
|
||||
#define PING_PAYLOAD_BYTES 32u
|
||||
#define PING_PACKET_BYTES (8u + PING_PAYLOAD_BYTES)
|
||||
|
||||
static void print(const char* s) {
|
||||
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||
}
|
||||
|
||||
static void print_char(char c) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int n = 0;
|
||||
if (v == 0) { print("0"); return; }
|
||||
while (v && n < (int)sizeof(buf)) {
|
||||
buf[n++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (n > 0) print_char(buf[--n]);
|
||||
}
|
||||
|
||||
static void print_ipv4(const uint8_t ip[4]) {
|
||||
print_u64(ip[0]); print("."); print_u64(ip[1]); print(".");
|
||||
print_u64(ip[2]); print("."); print_u64(ip[3]);
|
||||
}
|
||||
|
||||
static void print_rtt_us(uint64_t us) {
|
||||
uint64_t whole = us / US_PER_MS;
|
||||
uint64_t frac = us % US_PER_MS;
|
||||
print_u64(whole);
|
||||
print(".");
|
||||
if (frac < 100) print("0");
|
||||
if (frac < 10) print("0");
|
||||
print_u64(frac);
|
||||
}
|
||||
|
||||
static void format_ipv4(char* out, size_t outSize, const uint8_t ip[4]) {
|
||||
if (!out || outSize == 0) return;
|
||||
size_t off = 0;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
char tmp[4];
|
||||
uint32_t v = ip[i];
|
||||
int n = 0;
|
||||
if (v >= 100) tmp[n++] = (char)('0' + (v / 100));
|
||||
if (v >= 10) tmp[n++] = (char)('0' + ((v / 10) % 10));
|
||||
tmp[n++] = (char)('0' + (v % 10));
|
||||
for (int j = 0; j < n && off + 1 < outSize; j++) out[off++] = tmp[j];
|
||||
if (i != 3 && off + 1 < outSize) out[off++] = '.';
|
||||
}
|
||||
out[off < outSize ? off : outSize - 1] = 0;
|
||||
}
|
||||
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == 0) { out[0] = 0; return NULL; }
|
||||
|
||||
size_t n = 0;
|
||||
while (*s && *s != ' ' && *s != '\t') {
|
||||
if (n + 1 < outSize) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
out[n] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool parse_u8(const char** p, uint8_t* out) {
|
||||
const char* s = *p;
|
||||
uint32_t v = 0;
|
||||
int digits = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10u + (uint32_t)(*s - '0');
|
||||
if (v > 255u) return false;
|
||||
s++;
|
||||
digits++;
|
||||
}
|
||||
if (digits == 0) return false;
|
||||
*out = (uint8_t)v;
|
||||
*p = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_ipv4(const char* s, uint8_t ip[4]) {
|
||||
if (!s || !ip) return false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!parse_u8(&s, &ip[i])) return false;
|
||||
if (i != 3) {
|
||||
if (*s != '.') return false;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return *s == 0;
|
||||
}
|
||||
|
||||
static bool parse_u32(const char* s, uint32_t* out) {
|
||||
if (!s || !*s || !out) return false;
|
||||
uint32_t v = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
uint32_t d = (uint32_t)(*s - '0');
|
||||
if (v > (0xFFFFFFFFu - d) / 10u) return false;
|
||||
v = v * 10u + d;
|
||||
s++;
|
||||
}
|
||||
if (*s != 0) return false;
|
||||
*out = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t resolve_host(const char* host, uint8_t ip[4]) {
|
||||
if (parse_ipv4(host, ip))
|
||||
return SYS_STATUS_SUCCESS;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 resolved;
|
||||
size_t resolvedSize = sizeof(resolved);
|
||||
SYS_RESOLVE_HOST_REQUEST req;
|
||||
memset(&resolved, 0, sizeof(resolved));
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Name = SysSvFromCstr(host);
|
||||
req.Family = SYS_AF_INET;
|
||||
req.AddressOut = &resolved;
|
||||
req.AddressSizeInOut = &resolvedSize;
|
||||
|
||||
uint32_t st = SysResolveHost(&req);
|
||||
if (st != SYS_STATUS_SUCCESS)
|
||||
return st;
|
||||
if (resolved.Family != SYS_AF_INET)
|
||||
return SYS_STATUS_NOT_FOUND;
|
||||
|
||||
ip[0] = resolved.Address[0];
|
||||
ip[1] = resolved.Address[1];
|
||||
ip[2] = resolved.Address[2];
|
||||
ip[3] = resolved.Address[3];
|
||||
return SYS_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static uint16_t icmp_checksum(const uint8_t* data, size_t length) {
|
||||
uint32_t sum = 0;
|
||||
while (length >= 2) {
|
||||
sum += ((uint32_t)data[0] << 8) | data[1];
|
||||
data += 2;
|
||||
length -= 2;
|
||||
}
|
||||
if (length)
|
||||
sum += ((uint32_t)data[0] << 8);
|
||||
|
||||
while (sum >> 16)
|
||||
sum = (sum & 0xFFFFu) + (sum >> 16);
|
||||
return (uint16_t)(~sum & 0xFFFFu);
|
||||
}
|
||||
|
||||
static void make_echo_request(uint8_t packet[PING_PACKET_BYTES], uint16_t ident, uint16_t seq) {
|
||||
memset(packet, 0, PING_PACKET_BYTES);
|
||||
packet[0] = 8; // echo request
|
||||
packet[1] = 0;
|
||||
packet[4] = (uint8_t)(ident >> 8);
|
||||
packet[5] = (uint8_t)(ident & 0xFFu);
|
||||
packet[6] = (uint8_t)(seq >> 8);
|
||||
packet[7] = (uint8_t)(seq & 0xFFu);
|
||||
|
||||
for (uint32_t i = 0; i < PING_PAYLOAD_BYTES; i++)
|
||||
packet[8 + i] = (uint8_t)('a' + (i % 26u));
|
||||
|
||||
uint16_t sum = icmp_checksum(packet, PING_PACKET_BYTES);
|
||||
packet[2] = (uint8_t)(sum >> 8);
|
||||
packet[3] = (uint8_t)(sum & 0xFFu);
|
||||
}
|
||||
|
||||
static bool parse_echo_reply(const uint8_t* buf, size_t len, uint16_t ident, uint16_t seq,
|
||||
size_t* payloadLenOut, uint8_t* typeOut, uint8_t* codeOut) {
|
||||
size_t off = 0;
|
||||
|
||||
if (len >= 20 && (buf[0] >> 4) == 4) {
|
||||
size_t ihl = (size_t)(buf[0] & 0x0Fu) * 4u;
|
||||
if (ihl >= 20 && ihl <= len && buf[9] == SYS_IPPROTO_ICMP)
|
||||
off = ihl;
|
||||
}
|
||||
|
||||
if (len < off + 8)
|
||||
return false;
|
||||
|
||||
uint8_t type = buf[off + 0];
|
||||
uint8_t code = buf[off + 1];
|
||||
if (typeOut) *typeOut = type;
|
||||
if (codeOut) *codeOut = code;
|
||||
|
||||
uint16_t rid = ((uint16_t)buf[off + 4] << 8) | buf[off + 5];
|
||||
uint16_t rseq = ((uint16_t)buf[off + 6] << 8) | buf[off + 7];
|
||||
|
||||
if (type == 0 && code == 0 && rid == ident && rseq == seq) {
|
||||
if (payloadLenOut)
|
||||
*payloadLenOut = len - off;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
print("usage: apps/ping.exe [host-or-ipv4] [count]\n");
|
||||
print("default: apps/ping.exe 10.0.2.2 4\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
char host[256];
|
||||
char tok[256];
|
||||
uint32_t count = PING_DEFAULT_COUNT;
|
||||
strcpy(host, PING_DEFAULT_TARGET);
|
||||
|
||||
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||
if (cmd) {
|
||||
const char* rest = next_token(cmd, tok, sizeof(tok));
|
||||
if (tok[0]) {
|
||||
if (strlen(tok) >= sizeof(host)) {
|
||||
print("ping: host too long\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
strcpy(host, tok);
|
||||
cmd = rest;
|
||||
}
|
||||
if (cmd) {
|
||||
(void)next_token(cmd, tok, sizeof(tok));
|
||||
if (tok[0] && (!parse_u32(tok, &count) || count == 0 || count > 1000)) {
|
||||
usage();
|
||||
SysExitProcess(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t ip[4];
|
||||
uint32_t st = resolve_host(host, ip);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("ping: resolve failed "); print_u64(st); print(" for "); print(host); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
char ipText[16];
|
||||
format_ipv4(ipText, sizeof(ipText), ip);
|
||||
|
||||
uint32_t sock = 0;
|
||||
st = SysSocket(SYS_AF_INET, SYS_SOCK_RAW, SYS_IPPROTO_ICMP, &sock);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("ping: SysSocket(raw icmp) failed "); print_u64(st); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
uint32_t timeout = PING_TIMEOUT_MS;
|
||||
SYS_SOCKET_OPTION_REQUEST opt;
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.Option = SYS_SOCKET_OPT_RECV_TIMEOUT_MS;
|
||||
opt.Value = &timeout;
|
||||
opt.ValueSize = sizeof(timeout);
|
||||
(void)SysSetSocketOption(sock, &opt);
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
memset(&remote, 0, sizeof(remote));
|
||||
remote.Family = SYS_AF_INET;
|
||||
remote.Port = 0;
|
||||
remote.Address[0] = ip[0];
|
||||
remote.Address[1] = ip[1];
|
||||
remote.Address[2] = ip[2];
|
||||
remote.Address[3] = ip[3];
|
||||
|
||||
print("PING "); print(host); print(" ("); print(ipText); print(") ");
|
||||
print_u64(PING_PAYLOAD_BYTES); print(" data bytes\n");
|
||||
|
||||
uint16_t ident = (uint16_t)(SysGetCurrentPid() & 0xFFFFu);
|
||||
uint32_t sentPackets = 0;
|
||||
uint32_t receivedPackets = 0;
|
||||
uint64_t minUs = 0, maxUs = 0, sumUs = 0;
|
||||
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
uint16_t seq = (uint16_t)(i + 1u);
|
||||
uint8_t packet[PING_PACKET_BYTES];
|
||||
make_echo_request(packet, ident, seq);
|
||||
|
||||
size_t sent = 0;
|
||||
SYS_SOCKET_SEND_TO_REQUEST sendReq;
|
||||
memset(&sendReq, 0, sizeof(sendReq));
|
||||
sendReq.Buffer = packet;
|
||||
sendReq.Length = sizeof(packet);
|
||||
sendReq.RemoteAddress = &remote;
|
||||
sendReq.RemoteAddressSize = sizeof(remote);
|
||||
sendReq.SentOut = &sent;
|
||||
|
||||
uint64_t start = SysGetTick();
|
||||
st = SysSendTo(sock, &sendReq);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("ping: send failed seq="); print_u64(seq); print(" status="); print_u64(st); print("\n");
|
||||
if (i + 1 < count) SysSleep(1000);
|
||||
continue;
|
||||
}
|
||||
sentPackets++;
|
||||
|
||||
bool matched = false;
|
||||
uint8_t lastType = 0, lastCode = 0;
|
||||
for (;;) {
|
||||
uint8_t recvBuf[512];
|
||||
SYS_SOCKET_ADDRESS_IPV4 from;
|
||||
size_t fromSize = sizeof(from);
|
||||
size_t got = 0;
|
||||
SYS_SOCKET_RECV_FROM_REQUEST recvReq;
|
||||
memset(&recvReq, 0, sizeof(recvReq));
|
||||
recvReq.Buffer = recvBuf;
|
||||
recvReq.Length = sizeof(recvBuf);
|
||||
recvReq.RemoteAddressOut = &from;
|
||||
recvReq.RemoteAddressSizeInOut = &fromSize;
|
||||
recvReq.ReceivedOut = &got;
|
||||
|
||||
st = SysRecvFrom(sock, &recvReq);
|
||||
if (st == SYS_STATUS_TIMEOUT)
|
||||
break;
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("ping: recv failed status="); print_u64(st); print("\n");
|
||||
break;
|
||||
}
|
||||
|
||||
size_t icmpLen = 0;
|
||||
if (parse_echo_reply(recvBuf, got, ident, seq, &icmpLen, &lastType, &lastCode)) {
|
||||
uint64_t end = SysGetTick();
|
||||
uint64_t deltaUs = end >= start ? end - start : 0;
|
||||
receivedPackets++;
|
||||
sumUs += deltaUs;
|
||||
if (receivedPackets == 1 || deltaUs < minUs) minUs = deltaUs;
|
||||
if (deltaUs > maxUs) maxUs = deltaUs;
|
||||
|
||||
print_u64(icmpLen); print(" bytes from "); print_ipv4(from.Address);
|
||||
print(": icmp_seq="); print_u64(seq);
|
||||
print(" time="); print_rtt_us(deltaUs); print(" ms\n");
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t now = SysGetTick();
|
||||
if (now - start >= ((uint64_t)PING_TIMEOUT_MS * US_PER_MS))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!matched) {
|
||||
print("Request timeout for icmp_seq "); print_u64(seq);
|
||||
if (lastType || lastCode) {
|
||||
print(" last_type="); print_u64(lastType); print(" code="); print_u64(lastCode);
|
||||
}
|
||||
print("\n");
|
||||
}
|
||||
|
||||
if (i + 1 < count)
|
||||
SysSleep(1000);
|
||||
}
|
||||
|
||||
print("--- "); print(host); print(" ping statistics ---\n");
|
||||
print_u64(sentPackets); print(" packets transmitted, ");
|
||||
print_u64(receivedPackets); print(" packets received, ");
|
||||
uint32_t loss = sentPackets ? (uint32_t)(((uint64_t)(sentPackets - receivedPackets) * 100u) / sentPackets) : 100u;
|
||||
print_u64(loss); print("% packet loss\n");
|
||||
if (receivedPackets) {
|
||||
print("round-trip min/avg/max = ");
|
||||
print_rtt_us(minUs); print("/"); print_rtt_us(sumUs / receivedPackets); print("/"); print_rtt_us(maxUs); print(" ms\n");
|
||||
}
|
||||
|
||||
SysCloseHandle(sock);
|
||||
SysExitProcess(receivedPackets ? 0 : 1);
|
||||
return receivedPackets ? 0 : 1;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
static void print(const char* s) {
|
||||
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||
}
|
||||
|
||||
static void print_char(char c) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int n = 0;
|
||||
if (v == 0) { print("0"); return; }
|
||||
while (v && n < (int)sizeof(buf)) {
|
||||
buf[n++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (n > 0) print_char(buf[--n]);
|
||||
}
|
||||
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == 0) { out[0] = 0; return NULL; }
|
||||
|
||||
size_t n = 0;
|
||||
while (*s && *s != ' ' && *s != '\t') {
|
||||
if (n + 1 < outSize) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
out[n] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void print_ipv4(const uint8_t ip[4]) {
|
||||
print_u64(ip[0]); print("."); print_u64(ip[1]); print(".");
|
||||
print_u64(ip[2]); print("."); print_u64(ip[3]);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
char name[256];
|
||||
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||
cmd = next_token(cmd, name, sizeof(name)); // program path
|
||||
(void)cmd;
|
||||
|
||||
name[0] = 0;
|
||||
if (cmd)
|
||||
(void)next_token(cmd, name, sizeof(name));
|
||||
if (!name[0]) {
|
||||
print("usage: resolve.exe hostname\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 addr;
|
||||
size_t addrSize = sizeof(addr);
|
||||
SYS_RESOLVE_HOST_REQUEST req;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Name = SysSvFromCstr(name);
|
||||
req.Family = SYS_AF_INET;
|
||||
req.AddressOut = &addr;
|
||||
req.AddressSizeInOut = &addrSize;
|
||||
|
||||
uint32_t st = SysResolveHost(&req);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("resolve: SysResolveHost failed "); print_u64(st); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
print(name);
|
||||
print(" -> ");
|
||||
print_ipv4(addr.Address);
|
||||
print("\n");
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define MAX_CMD 256
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Output helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
static void print(const char* s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||
|
||||
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||
return SysOpenFile(&req);
|
||||
}
|
||||
|
||||
static uint32_t create_file(const char* path, uint32_t* handleOut) {
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||
return SysCreateFile(&req);
|
||||
}
|
||||
|
||||
static uint32_t open_dir(const char* path, uint32_t* handleOut) {
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||
return SysOpenDir(&req);
|
||||
}
|
||||
|
||||
static uint32_t set_cwd(const char* path) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
return SysSetCwd(&req);
|
||||
}
|
||||
|
||||
static uint32_t delete_file(const char* path) {
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(path) };
|
||||
return SysDeleteFile(&req);
|
||||
}
|
||||
|
||||
static uint32_t create_process(const char* path, uint32_t* handleOut, const char* cmdline) {
|
||||
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(path), SysSvFromCstr(cmdline), handleOut };
|
||||
return SysCreateProcess(&req);
|
||||
}
|
||||
|
||||
static void printUInt(uint32_t n) {
|
||||
char buf[12]; int i = 0;
|
||||
if (n == 0) { print("0"); return; }
|
||||
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
|
||||
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
||||
}
|
||||
|
||||
static void printInt(int32_t n) {
|
||||
if (n < 0) { print("-"); printUInt((uint32_t)-(n + 1) + 1); }
|
||||
else { printUInt((uint32_t)n); }
|
||||
}
|
||||
|
||||
static void printUInt64(uint64_t n) {
|
||||
if (n == 0) { print("0"); return; }
|
||||
char buf[20]; int i = 0;
|
||||
while (n > 0) { buf[i++] = (char)('0' + (int)(n % 10)); n /= 10; }
|
||||
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Line editor
|
||||
// ---------------------------------------------------------------------------
|
||||
static void readLine(char* buf, int len) {
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
char c = SysKbReadChar();
|
||||
if (!c) { SysYield(); continue; }
|
||||
if (c == '\r' || c == '\n') { print("\n"); break; }
|
||||
if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
|
||||
if (c < 0x20 || c == 127) continue;
|
||||
if (pos < len - 1) { buf[pos++] = c; char echo[2] = {c, '\0'}; print(echo); }
|
||||
}
|
||||
buf[pos] = '\0';
|
||||
}
|
||||
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
while (len > 0 && s[len - 1] == ' ') len--;
|
||||
s[len] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Built-in commands
|
||||
// ---------------------------------------------------------------------------
|
||||
static void cmd_help() {
|
||||
print("Available commands:\n"
|
||||
" help - Show this list\n"
|
||||
" mem - Show memory usage\n"
|
||||
" dir [path] - List directory contents\n"
|
||||
" cd <path> - Change directory\n"
|
||||
" type <file> - Display file contents\n"
|
||||
" del <file> - Delete a file\n"
|
||||
" write <file> <tx> - Create a file with text\n"
|
||||
" clear - Clear the screen\n"
|
||||
" exit - Close the shell\n");
|
||||
}
|
||||
|
||||
static void printSizeKiB(uint64_t bytes) {
|
||||
printUInt64(bytes / 1024u);
|
||||
print(" KiB");
|
||||
}
|
||||
|
||||
static void cmd_mem(void) {
|
||||
SYS_MEMORY_STATUS st;
|
||||
if (SysGetMemoryStatus(&st) != 0) {
|
||||
print("mem: failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
print("Memory Status:\n");
|
||||
print(" Free physical: ");
|
||||
printSizeKiB(st.FreePhysicalBytes);
|
||||
print("\n Kernel pool: ");
|
||||
printSizeKiB(st.KernelPoolBytes);
|
||||
print("\n User heap: ");
|
||||
printSizeKiB(st.UserHeapBytes);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
static void cmd_write(const char* args) {
|
||||
char filename[MAX_CMD]; int i = 0;
|
||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
||||
filename[i] = args[i]; i++;
|
||||
}
|
||||
filename[i] = '\0';
|
||||
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
|
||||
uint32_t hFile;
|
||||
if (create_file(filename, &hFile) != 0) { print("write: could not create file\n"); return; }
|
||||
size_t written;
|
||||
SysWriteFile(hFile, args + i + 1, strlen(args + i + 1), &written);
|
||||
SysCloseHandle(hFile);
|
||||
print("File written successfully.\n");
|
||||
}
|
||||
|
||||
static void cmd_dir(const char* path) {
|
||||
uint32_t hDir;
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
|
||||
if (open_dir(target, &hDir) != 0) {
|
||||
print("dir: failed to open directory\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SYS_DIR_ENTRY ke;
|
||||
while (SysReadDir(hDir, &ke) == 0) {
|
||||
// Corrected Attribute Check: 0x10 is the Directory bit in FAT
|
||||
if (ke.Attr & 0x10) {
|
||||
print("<DIR> ");
|
||||
} else {
|
||||
print(" ");
|
||||
}
|
||||
|
||||
print(ke.Name);
|
||||
|
||||
// Optional: print file size for non-directories
|
||||
if (!(ke.Attr & 0x10)) {
|
||||
print(" (");
|
||||
printUInt(ke.FileSize);
|
||||
print(" bytes)");
|
||||
}
|
||||
|
||||
print("\n");
|
||||
}
|
||||
SysCloseHandle(hDir);
|
||||
}
|
||||
|
||||
static void cmd_type(const char* filename) {
|
||||
uint32_t hFile;
|
||||
if (open_file(filename, &hFile) != 0) { print("type: cannot open file\n"); return; }
|
||||
char buffer[512]; size_t bytesRead;
|
||||
while (SysReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead) == 0 && bytesRead > 0) {
|
||||
buffer[bytesRead] = '\0'; print(buffer);
|
||||
}
|
||||
SysCloseHandle(hFile); print("\n");
|
||||
}
|
||||
|
||||
|
||||
static bool extractProgramPath(const char* cmdline, char* out, size_t outSize) {
|
||||
if (!cmdline || !out || outSize == 0) return false;
|
||||
|
||||
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
|
||||
if (*cmdline == '\0') return false;
|
||||
|
||||
size_t pos = 0;
|
||||
if (*cmdline == '"') {
|
||||
cmdline++;
|
||||
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
|
||||
out[pos++] = *cmdline++;
|
||||
} else {
|
||||
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
|
||||
out[pos++] = *cmdline++;
|
||||
}
|
||||
|
||||
out[pos] = '\0';
|
||||
return pos != 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Run one command
|
||||
// ---------------------------------------------------------------------------
|
||||
static int runCommand(const char* cmd) {
|
||||
if (!cmd || *cmd == '\0') return 0;
|
||||
if (strcmp(cmd, "exit") == 0) return 1;
|
||||
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
|
||||
if (strcmp(cmd, "clear") == 0) { SysScreenClear(0); return 0; }
|
||||
if (strncmp(cmd, "crash", 5) == 0) {
|
||||
// Check the sub-option trailing "crash " (offset 6)
|
||||
if (strcmp(cmd + 6, "void") == 0) {
|
||||
// Jumping into the void
|
||||
void (*invalid_func)(void) = NULL;
|
||||
invalid_func();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(cmd + 6, "noncanonical") == 0) {
|
||||
// Non-canonical pointer dereference
|
||||
volatile int *ptr = (volatile int*)0x4242424242424242ULL;
|
||||
*ptr = 1337;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(cmd + 6, "ud2") == 0) {
|
||||
__ud2();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(cmd + 6, "nx") == 0) {
|
||||
// NX / DEP Violation
|
||||
unsigned char code[] = { 0xC3 };
|
||||
void (*execute_stack)(void) = (void (*)(void))code;
|
||||
execute_stack();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(cmd + 6, "divzero") == 0) {
|
||||
// Division by zero
|
||||
volatile int zero = 0;
|
||||
volatile int result = 1 / zero;
|
||||
(void)result;
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
// Default fallback if just "crash" or an unknown sub-option is passed
|
||||
*(volatile int*)0 = 42;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (strcmp(cmd, "mem") == 0) { cmd_mem(); return 0; }
|
||||
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
||||
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
||||
if (strncmp(cmd, "cd ", 3) == 0) { if (set_cwd(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
||||
if (strncmp(cmd, "del ", 4) == 0) { delete_file(cmd + 4); return 0; }
|
||||
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
||||
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
||||
|
||||
char program[MAX_CMD];
|
||||
if (!extractProgramPath(cmd, program, sizeof(program))) return 0;
|
||||
|
||||
uint32_t hProc;
|
||||
if (create_process(program, &hProc, cmd) == 0) {
|
||||
SysWaitForProcess(hProc);
|
||||
SysCloseHandle(hProc);
|
||||
while (SysKbPoll()) (void)SysKbReadChar();
|
||||
} else { print("shell: launch failed\n"); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: command line: ");
|
||||
print(cmdline);
|
||||
print("\n");
|
||||
print("shell: ready\n");
|
||||
while (true) {
|
||||
print("> ");
|
||||
readLine(lineBuf, MAX_CMD);
|
||||
if (runCommand(trim(lineBuf))) break;
|
||||
}
|
||||
print("shell: goodbye\n");
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define MAX_CMD 256
|
||||
#define NAME_MAX 259
|
||||
|
||||
typedef struct dirent { char d_name[NAME_MAX + 1]; unsigned char d_type; } dirent;
|
||||
typedef struct DIR { uint32_t KHandle; dirent Current; bool Valid; } DIR;
|
||||
|
||||
extern int read(int fd, void *buf, size_t count);
|
||||
extern int write(int fd, const void *buf, size_t count);
|
||||
extern int open(const char *pathname, int flags, ...);
|
||||
extern int close(int fd);
|
||||
extern int chdir(const char *path);
|
||||
extern DIR* opendir(const char* path);
|
||||
extern dirent* readdir(DIR* d);
|
||||
extern int closedir(DIR* d);
|
||||
extern int unlink(const char *pathname);
|
||||
extern int posix_spawn(int *pid, const char *path, const void *act, const void *attr, char *const argv[], char *const envp[]);
|
||||
extern int waitpid(int pid, int *status, int options);
|
||||
extern void exit(int status);
|
||||
extern void clearscreen(void);
|
||||
extern void clearkeyboard(void);
|
||||
extern void PosixFdInit(void);
|
||||
|
||||
static int hIn = 0; static int hOut = 1;
|
||||
|
||||
static void print(const char* s) { write(hOut, s, strlen(s)); }
|
||||
|
||||
static void printUInt(uint32_t n) {
|
||||
char buf[12]; int i = 0;
|
||||
if (n == 0) { print("0"); return; }
|
||||
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
|
||||
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
||||
}
|
||||
|
||||
static void printInt(int32_t n) {
|
||||
if (n < 0) { print("-"); n = -n; }
|
||||
printUInt((uint32_t)n);
|
||||
}
|
||||
|
||||
static void readLine(char* buf, int len) {
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
char c; if (read(hIn, &c, 1) <= 0) break;
|
||||
if (c == '\r' || c == '\n') { print("\n"); break; }
|
||||
if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
|
||||
if (c < 0x20 || c == 127) continue;
|
||||
if (pos < len - 1) { buf[pos++] = c; char echo[2] = {c, '\0'}; print(echo); }
|
||||
}
|
||||
buf[pos] = '\0';
|
||||
}
|
||||
|
||||
|
||||
static int splitCommandLine(char* line, char** argv, int maxArgv) {
|
||||
int argc = 0;
|
||||
char* p = line;
|
||||
|
||||
while (*p && argc < maxArgv - 1) {
|
||||
while (*p == ' ' || *p == '\t') p++;
|
||||
if (!*p) break;
|
||||
|
||||
if (*p == '"') {
|
||||
p++;
|
||||
argv[argc++] = p;
|
||||
while (*p && *p != '"') p++;
|
||||
if (*p) *p++ = '\0';
|
||||
} else {
|
||||
argv[argc++] = p;
|
||||
while (*p && *p != ' ' && *p != '\t') p++;
|
||||
if (*p) *p++ = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
argv[argc] = NULL;
|
||||
return argc;
|
||||
}
|
||||
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
while (len > 0 && s[len - 1] == ' ') len--;
|
||||
s[len] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
static void cmd_help() {
|
||||
print("Available commands:\n"
|
||||
" help - Show this list\n"
|
||||
" dir [path] - List directory contents\n"
|
||||
" cd <path> - Change directory\n"
|
||||
" type <file> - Display file contents\n"
|
||||
" del <file> - Delete a file\n"
|
||||
" write <file> <txt> - Create a file with text\n"
|
||||
" clear - Clear the screen\n"
|
||||
" exit - Close the shell\n");
|
||||
}
|
||||
|
||||
static void cmd_write(const char* args) {
|
||||
char filename[MAX_CMD]; int i = 0;
|
||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_CMD - 1) {
|
||||
filename[i] = args[i]; i++;
|
||||
}
|
||||
filename[i] = '\0';
|
||||
if (args[i] == '\0' || args[i+1] == '\0') { print("write: usage: write <filename> <text>\n"); return; }
|
||||
int fd = open(filename, 0x0241, 0644); // O_WRONLY | O_CREAT | O_TRUNC
|
||||
if (fd < 0) { print("write: could not create file\n"); return; }
|
||||
write(fd, args + i + 1, strlen(args + i + 1));
|
||||
close(fd);
|
||||
print("File written successfully.\n");
|
||||
}
|
||||
|
||||
static void cmd_dir(const char* path) {
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
DIR* d = opendir(target);
|
||||
if (!d) { print("dir: cannot access directory\n"); return; }
|
||||
dirent* entry;
|
||||
while ((entry = readdir(d)) != NULL) {
|
||||
print(entry->d_type == 1 ? "<DIR> " : " ");
|
||||
print(entry->d_name); print("\n");
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
static void cmd_type(const char* filename) {
|
||||
int fd = open(filename, 0); // O_RDONLY
|
||||
if (fd < 0) { print("type: cannot open file\n"); return; }
|
||||
char buffer[512]; int n;
|
||||
while ((n = read(fd, buffer, 511)) > 0) { buffer[n] = '\0'; print(buffer); }
|
||||
close(fd); print("\n");
|
||||
}
|
||||
|
||||
static int runCommand(const char* cmd) {
|
||||
if (!cmd || *cmd == '\0') return 0;
|
||||
if (strcmp(cmd, "exit") == 0) return 1;
|
||||
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
|
||||
if (strcmp(cmd, "clear") == 0) { clearscreen(); return 0; }
|
||||
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
|
||||
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
||||
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
||||
if (strncmp(cmd, "cd ", 3) == 0) { if (chdir(cmd + 3) != 0) print("cd: failed\n"); return 0; }
|
||||
if (strncmp(cmd, "del ", 4) == 0) { unlink(cmd + 4); return 0; }
|
||||
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
||||
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
||||
|
||||
char cmdBuf[MAX_CMD];
|
||||
strncpy(cmdBuf, cmd, MAX_CMD - 1);
|
||||
cmdBuf[MAX_CMD - 1] = '\0';
|
||||
|
||||
char* argv[32];
|
||||
int argc = splitCommandLine(cmdBuf, argv, 32);
|
||||
if (argc == 0) return 0;
|
||||
|
||||
int pid;
|
||||
if (posix_spawn(&pid, argv[0], NULL, NULL, argv, NULL) == 0) {
|
||||
int status; waitpid(pid, &status, 0);
|
||||
clearkeyboard();
|
||||
} else { print("shell: launch failed\n"); }
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
PosixFdInit();
|
||||
|
||||
const char* cmdline = SysGetPeb()->CommandLine.Data;
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: command line: ");
|
||||
print(cmdline);
|
||||
print("\n");
|
||||
print("shell: ready\n");
|
||||
while (true) {
|
||||
print("> ");
|
||||
readLine(lineBuf, MAX_CMD);
|
||||
if (runCommand(trim(lineBuf))) break;
|
||||
}
|
||||
print("shell: goodbye\n");
|
||||
exit(0);
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAX_CMD 256
|
||||
static HANDLE hIn = INVALID_HANDLE_VALUE;
|
||||
static HANDLE hOut = INVALID_HANDLE_VALUE;
|
||||
|
||||
// External/Custom API
|
||||
BOOL ClearConsoleScreenBuffer(HANDLE hConsole);
|
||||
|
||||
// --- Output Helpers ---
|
||||
static void print(const char* s) {
|
||||
DWORD written;
|
||||
if (hOut != INVALID_HANDLE_VALUE) WriteConsoleA(hOut, s, (DWORD)strlen(s), &written, 0);
|
||||
else OutputDebugStringA(s);
|
||||
}
|
||||
|
||||
static void printUInt(uint32_t n) {
|
||||
char buf[12]; int i = 0;
|
||||
if (n == 0) { print("0"); return; }
|
||||
while (n > 0) { buf[i++] = (char)('0' + n % 10); n /= 10; }
|
||||
while (i > 0) { char c[2] = { buf[--i], '\0' }; print(c); }
|
||||
}
|
||||
|
||||
static void printInt(int32_t n) {
|
||||
if (n < 0) { print("-"); n = -n; }
|
||||
printUInt((uint32_t)n);
|
||||
}
|
||||
|
||||
// --- Line Editor ---
|
||||
static void readLine(char* buf, int len) {
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
char c; DWORD nRead;
|
||||
ReadConsoleA(hIn, &c, 1, &nRead, 0);
|
||||
if (!nRead) continue;
|
||||
if (c == '\r' || c == '\n') { print("\n"); break; }
|
||||
if ((c == '\b' || c == 127) && pos > 0) { pos--; print("\b \b"); continue; }
|
||||
if (c < 0x20 || c == 127) continue;
|
||||
if (pos < len - 1) {
|
||||
buf[pos++] = c;
|
||||
char echo[2] = { c, '\0' };
|
||||
print(echo);
|
||||
}
|
||||
}
|
||||
buf[pos] = '\0';
|
||||
}
|
||||
|
||||
static char* trim(char* s) {
|
||||
while (*s == ' ') s++;
|
||||
int len = (int)strlen(s);
|
||||
while (len > 0 && s[len - 1] == ' ') len--;
|
||||
s[len] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
// --- Built-in Commands ---
|
||||
static void cmd_help() {
|
||||
print("Available commands:\n"
|
||||
" help - Show this list\n"
|
||||
" dir [path] - List directory contents\n"
|
||||
" cd <path> - Change directory\n"
|
||||
" type <file> - Display file contents\n"
|
||||
" del <file> - Delete a file\n"
|
||||
" write <file> <tx> - Create a file with text\n"
|
||||
" clear - Clear the screen\n"
|
||||
" exit - Close the shell\n");
|
||||
}
|
||||
|
||||
static void cmd_write(const char* args) {
|
||||
char filename[MAX_PATH]; int i = 0;
|
||||
while (args[i] != ' ' && args[i] != '\0' && i < MAX_PATH - 1) {
|
||||
filename[i] = args[i]; i++;
|
||||
}
|
||||
filename[i] = '\0';
|
||||
if (args[i] == '\0' || args[i+1] == '\0') {
|
||||
print("write: usage: write <filename> <text>\n");
|
||||
return;
|
||||
}
|
||||
HANDLE hFile = CreateFileA(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE) { print("write: could not create file\n"); return; }
|
||||
DWORD written;
|
||||
WriteFile(hFile, args + i + 1, (DWORD)strlen(args + i + 1), &written, 0);
|
||||
CloseHandle(hFile);
|
||||
print("File written successfully.\n");
|
||||
}
|
||||
|
||||
static void cmd_dir(const char* path) {
|
||||
WIN32_FIND_DATAA fd;
|
||||
char search_path[MAX_PATH];
|
||||
const char* target = (path && *path != '\0') ? path : ".";
|
||||
|
||||
size_t len = strlen(target);
|
||||
if (len + 3 > MAX_PATH) { print("dir: path too long\n"); return; }
|
||||
strcpy(search_path, target);
|
||||
strcat(search_path, "\\*");
|
||||
|
||||
HANDLE hFind = FindFirstFileA(search_path, &fd);
|
||||
if (hFind == INVALID_HANDLE_VALUE) { print("dir: failed to read directory\n"); return; }
|
||||
do {
|
||||
print(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? "<DIR> " : " ");
|
||||
print(fd.cFileName); print("\n");
|
||||
} while (FindNextFileA(hFind, &fd));
|
||||
FindClose(hFind);
|
||||
}
|
||||
|
||||
static void cmd_type(const char* filename) {
|
||||
HANDLE hFile = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
if (hFile == INVALID_HANDLE_VALUE) { print("type: cannot open file\n"); return; }
|
||||
char buffer[512]; DWORD bytesRead;
|
||||
while (ReadFile(hFile, buffer, sizeof(buffer) - 1, &bytesRead, 0) && bytesRead > 0) {
|
||||
buffer[bytesRead] = '\0'; print(buffer);
|
||||
}
|
||||
CloseHandle(hFile); print("\n");
|
||||
}
|
||||
|
||||
static int runCommand(const char* cmd) {
|
||||
if (!cmd || *cmd == '\0') return 0;
|
||||
if (strcmp(cmd, "exit") == 0) return 1;
|
||||
if (strcmp(cmd, "help") == 0) { cmd_help(); return 0; }
|
||||
if (strcmp(cmd, "clear") == 0) { ClearConsoleScreenBuffer(hOut); return 0; }
|
||||
if (strcmp(cmd, "crash") == 0) { *(int*)0 = 42; return 0; }
|
||||
if (strcmp(cmd, "dir") == 0) { cmd_dir("."); return 0; }
|
||||
if (strncmp(cmd, "dir ", 4) == 0) { cmd_dir(cmd + 4); return 0; }
|
||||
if (strncmp(cmd, "cd ", 3) == 0) { if (!SetCurrentDirectoryA(cmd + 3)) print("cd: failed\n"); return 0; }
|
||||
if (strncmp(cmd, "del ", 4) == 0) { DeleteFileA(cmd + 4); return 0; }
|
||||
if (strncmp(cmd, "type ", 5) == 0) { cmd_type(cmd + 5); return 0; }
|
||||
if (strncmp(cmd, "write ", 6) == 0) { cmd_write(cmd + 6); return 0; }
|
||||
|
||||
PROCESS_INFORMATION pi;
|
||||
char cmdBuf[MAX_CMD];
|
||||
strncpy(cmdBuf, cmd, MAX_CMD - 1);
|
||||
cmdBuf[MAX_CMD - 1] = '\0';
|
||||
if (CreateProcessA(NULL, cmdBuf, NULL, NULL, FALSE, 0, NULL, NULL, NULL, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
if (pi.hThread && pi.hThread != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(pi.hThread);
|
||||
FlushConsoleInputBuffer(hIn);
|
||||
} else {
|
||||
print("shell: launch failed\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
hIn = GetStdHandle(STD_INPUT_HANDLE);
|
||||
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
const char* cmdline = GetCommandLineA();
|
||||
char lineBuf[MAX_CMD];
|
||||
|
||||
print("shell: command line: ");
|
||||
print(cmdline);
|
||||
print("\n");
|
||||
print("shell: ready\n");
|
||||
while (true) {
|
||||
print("> ");
|
||||
readLine(lineBuf, MAX_CMD);
|
||||
if (runCommand(trim(lineBuf))) break;
|
||||
}
|
||||
print("shell: goodbye\n");
|
||||
ExitProcess(0);
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
static void print(const char* s) {
|
||||
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||
}
|
||||
|
||||
static void print_char(char c) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int n = 0;
|
||||
if (v == 0) { print("0"); return; }
|
||||
while (v && n < (int)sizeof(buf)) {
|
||||
buf[n++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (n > 0) print_char(buf[--n]);
|
||||
}
|
||||
|
||||
static void print_ipv4(const uint8_t ip[4]) {
|
||||
print_u64(ip[0]); print("."); print_u64(ip[1]); print(".");
|
||||
print_u64(ip[2]); print("."); print_u64(ip[3]);
|
||||
}
|
||||
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == 0) { out[0] = 0; return NULL; }
|
||||
|
||||
size_t n = 0;
|
||||
while (*s && *s != ' ' && *s != '\t') {
|
||||
if (n + 1 < outSize) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
out[n] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool parse_port(const char* s, uint16_t* out) {
|
||||
if (!s || !*s || !out) return false;
|
||||
uint32_t v = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10u + (uint32_t)(*s - '0');
|
||||
if (v > 65535u) return false;
|
||||
s++;
|
||||
}
|
||||
if (*s != 0 || v == 0) return false;
|
||||
*out = (uint16_t)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
print("usage: tcpserve.exe [port]\n");
|
||||
print("default: tcpserve.exe 8080\n");
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
uint16_t port = 8080;
|
||||
|
||||
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||
char tok[64];
|
||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||
if (cmd) {
|
||||
(void)next_token(cmd, tok, sizeof(tok));
|
||||
if (tok[0] && !parse_port(tok, &port)) {
|
||||
usage();
|
||||
SysExitProcess(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t listener = 0;
|
||||
uint32_t st = SysSocket(SYS_AF_INET, SYS_SOCK_STREAM, SYS_IPPROTO_TCP, &listener);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("tcpserve: SysSocket failed "); print_u64(st); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 bindAddr;
|
||||
memset(&bindAddr, 0, sizeof(bindAddr));
|
||||
bindAddr.Family = SYS_AF_INET;
|
||||
bindAddr.Port = port;
|
||||
bindAddr.Address[0] = 0;
|
||||
bindAddr.Address[1] = 0;
|
||||
bindAddr.Address[2] = 0;
|
||||
bindAddr.Address[3] = 0;
|
||||
|
||||
st = SysBind(listener, &bindAddr, sizeof(bindAddr));
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("tcpserve: SysBind failed "); print_u64(st); print("\n");
|
||||
SysCloseHandle(listener);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
st = SysListen(listener, 4);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("tcpserve: SysListen failed "); print_u64(st); print("\n");
|
||||
SysCloseHandle(listener);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
print("tcpserve: listening on 0.0.0.0:"); print_u64(port); print("\n");
|
||||
print("tcpserve: accepting one connection...\n");
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
size_t remoteSize = sizeof(remote);
|
||||
uint32_t client = 0;
|
||||
st = SysAccept(listener, &remote, &remoteSize, &client);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("tcpserve: SysAccept failed "); print_u64(st); print("\n");
|
||||
SysCloseHandle(listener);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
print("tcpserve: accepted from ");
|
||||
if (remote.Family == SYS_AF_INET) {
|
||||
print_ipv4(remote.Address);
|
||||
print(":"); print_u64(remote.Port);
|
||||
} else {
|
||||
print("unknown");
|
||||
}
|
||||
print("\n");
|
||||
|
||||
char request[1025];
|
||||
size_t got = 0;
|
||||
st = SysRecv(client, request, sizeof(request) - 1, 0, &got);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("tcpserve: SysRecv failed "); print_u64(st); print("\n");
|
||||
SysCloseHandle(client);
|
||||
SysCloseHandle(listener);
|
||||
SysExitProcess(1);
|
||||
}
|
||||
request[got] = 0;
|
||||
print("tcpserve: received "); print_u64(got); print(" bytes\n");
|
||||
if (got) {
|
||||
print("--- request begins ---\n");
|
||||
print(request);
|
||||
print("\n--- request ends ---\n");
|
||||
}
|
||||
|
||||
const char body[] = "hello from nt-lite tcpserve\n";
|
||||
char response[256];
|
||||
size_t off = 0;
|
||||
#define APPEND_LITERAL(x) do { const char* _s = (x); while (*_s && off + 1 < sizeof(response)) response[off++] = *_s++; response[off] = 0; } while (0)
|
||||
APPEND_LITERAL("HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\nConnection: close\r\nContent-Length: ");
|
||||
char lenbuf[24];
|
||||
uint64_t len = sizeof(body) - 1;
|
||||
int n = 0;
|
||||
if (len == 0) lenbuf[n++] = '0';
|
||||
else {
|
||||
char rev[24];
|
||||
while (len && n < (int)sizeof(rev)) { rev[n++] = (char)('0' + (len % 10)); len /= 10; }
|
||||
for (int i = n - 1; i >= 0 && off + 1 < sizeof(response); --i) response[off++] = rev[i];
|
||||
response[off] = 0;
|
||||
n = 0;
|
||||
}
|
||||
if (n) {
|
||||
for (int i = 0; i < n && off + 1 < sizeof(response); ++i) response[off++] = lenbuf[i];
|
||||
response[off] = 0;
|
||||
}
|
||||
APPEND_LITERAL("\r\n\r\n");
|
||||
#undef APPEND_LITERAL
|
||||
|
||||
size_t sent = 0;
|
||||
st = SysSend(client, response, off, 0, &sent);
|
||||
if (st == SYS_STATUS_SUCCESS)
|
||||
st = SysSend(client, body, sizeof(body) - 1, 0, &sent);
|
||||
if (st != SYS_STATUS_SUCCESS)
|
||||
print("tcpserve: SysSend failed "), print_u64(st), print("\n");
|
||||
|
||||
SysShutdown(client, SYS_SHUTDOWN_BOTH);
|
||||
SysCloseHandle(client);
|
||||
SysCloseHandle(listener);
|
||||
print("tcpserve: done\n");
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
static void print(const char* s) {
|
||||
if (s) { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) }; SysWriteConsole(&req); }
|
||||
}
|
||||
|
||||
static void print_char(char c) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int n = 0;
|
||||
if (v == 0) { print("0"); return; }
|
||||
while (v && n < (int)sizeof(buf)) {
|
||||
buf[n++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (n > 0) print_char(buf[--n]);
|
||||
}
|
||||
|
||||
static void print_ipv4(const uint8_t ip[4]) {
|
||||
print_u64(ip[0]); print("."); print_u64(ip[1]); print(".");
|
||||
print_u64(ip[2]); print("."); print_u64(ip[3]);
|
||||
}
|
||||
|
||||
static const char* next_token(const char* s, char* out, size_t outSize) {
|
||||
if (!s || !out || outSize == 0) return NULL;
|
||||
while (*s == ' ' || *s == '\t') s++;
|
||||
if (*s == 0) { out[0] = 0; return NULL; }
|
||||
|
||||
size_t n = 0;
|
||||
while (*s && *s != ' ' && *s != '\t') {
|
||||
if (n + 1 < outSize) out[n++] = *s;
|
||||
s++;
|
||||
}
|
||||
out[n] = 0;
|
||||
return s;
|
||||
}
|
||||
|
||||
static bool parse_u8(const char** p, uint8_t* out) {
|
||||
const char* s = *p;
|
||||
uint32_t v = 0;
|
||||
int digits = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10u + (uint32_t)(*s - '0');
|
||||
if (v > 255u) return false;
|
||||
s++;
|
||||
digits++;
|
||||
}
|
||||
if (digits == 0) return false;
|
||||
*out = (uint8_t)v;
|
||||
*p = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parse_ipv4(const char* s, uint8_t ip[4]) {
|
||||
if (!s || !ip) return false;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!parse_u8(&s, &ip[i])) return false;
|
||||
if (i != 3) {
|
||||
if (*s != '.') return false;
|
||||
s++;
|
||||
}
|
||||
}
|
||||
return *s == 0;
|
||||
}
|
||||
|
||||
static bool parse_port(const char* s, uint16_t* out) {
|
||||
if (!s || !*s || !out) return false;
|
||||
uint32_t v = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10u + (uint32_t)(*s - '0');
|
||||
if (v > 65535u) return false;
|
||||
s++;
|
||||
}
|
||||
if (*s != 0 || v == 0) return false;
|
||||
*out = (uint16_t)v;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void usage(void) {
|
||||
print("usage:\n");
|
||||
print(" udp.exe send <ipv4> <port> [message]\n");
|
||||
print(" udp.exe listen <port>\n");
|
||||
print("examples:\n");
|
||||
print(" udp.exe send 10.0.2.2 9999 hello\n");
|
||||
print(" udp.exe listen 9999\n");
|
||||
}
|
||||
|
||||
static int run_send(const char* cmd) {
|
||||
char tok[256];
|
||||
uint8_t ip[4];
|
||||
uint16_t port = 0;
|
||||
|
||||
cmd = next_token(cmd, tok, sizeof(tok));
|
||||
if (!tok[0] || !parse_ipv4(tok, ip)) { usage(); return 1; }
|
||||
cmd = next_token(cmd, tok, sizeof(tok));
|
||||
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
|
||||
|
||||
// Skip leading whitespace, then take the entire remaining command line
|
||||
if (cmd) {
|
||||
while (*cmd == ' ' || *cmd == '\t') {
|
||||
cmd++;
|
||||
}
|
||||
}
|
||||
|
||||
const char* msg = "hello from nt-lite udp\n";
|
||||
if (cmd && *cmd != '\0') {
|
||||
msg = cmd;
|
||||
}
|
||||
|
||||
uint32_t sock = 0;
|
||||
uint32_t st = SysSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("udp: SysSocket failed "); print_u64(st); print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
memset(&remote, 0, sizeof(remote));
|
||||
remote.Family = SYS_AF_INET;
|
||||
remote.Port = port;
|
||||
remote.Address[0] = ip[0];
|
||||
remote.Address[1] = ip[1];
|
||||
remote.Address[2] = ip[2];
|
||||
remote.Address[3] = ip[3];
|
||||
|
||||
size_t sent = 0;
|
||||
SYS_SOCKET_SEND_TO_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Buffer = msg;
|
||||
req.Length = strlen(msg);
|
||||
req.RemoteAddress = &remote;
|
||||
req.RemoteAddressSize = sizeof(remote);
|
||||
req.SentOut = &sent;
|
||||
|
||||
st = SysSendTo(sock, &req);
|
||||
SysCloseHandle(sock);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("udp: SysSendTo failed "); print_u64(st); print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
print("udp: sent "); print_u64(sent); print(" bytes to "); print_ipv4(ip); print(":"); print_u64(port); print("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int run_listen(const char* cmd) {
|
||||
char tok[64];
|
||||
uint16_t port = 0;
|
||||
|
||||
(void)next_token(cmd, tok, sizeof(tok));
|
||||
if (!tok[0] || !parse_port(tok, &port)) { usage(); return 1; }
|
||||
|
||||
uint32_t sock = 0;
|
||||
uint32_t st = SysSocket(SYS_AF_INET, SYS_SOCK_DGRAM, SYS_IPPROTO_UDP, &sock);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("udp: SysSocket failed "); print_u64(st); print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 bindAddr;
|
||||
memset(&bindAddr, 0, sizeof(bindAddr));
|
||||
bindAddr.Family = SYS_AF_INET;
|
||||
bindAddr.Port = port;
|
||||
|
||||
st = SysBind(sock, &bindAddr, sizeof(bindAddr));
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("udp: SysBind failed "); print_u64(st); print("\n");
|
||||
SysCloseHandle(sock);
|
||||
return 1;
|
||||
}
|
||||
|
||||
print("udp: listening on 0.0.0.0:"); print_u64(port); print(" for one datagram\n");
|
||||
|
||||
char buf[1025];
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
size_t remoteSize = sizeof(remote);
|
||||
size_t got = 0;
|
||||
SYS_SOCKET_RECV_FROM_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Buffer = buf;
|
||||
req.Length = sizeof(buf) - 1;
|
||||
req.RemoteAddressOut = &remote;
|
||||
req.RemoteAddressSizeInOut = &remoteSize;
|
||||
req.ReceivedOut = &got;
|
||||
|
||||
st = SysRecvFrom(sock, &req);
|
||||
SysCloseHandle(sock);
|
||||
if (st != SYS_STATUS_SUCCESS) {
|
||||
print("udp: SysRecvFrom failed "); print_u64(st); print("\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
buf[got] = 0;
|
||||
print("udp: received "); print_u64(got); print(" bytes from ");
|
||||
if (remote.Family == SYS_AF_INET) {
|
||||
print_ipv4(remote.Address); print(":"); print_u64(remote.Port);
|
||||
} else {
|
||||
print("unknown");
|
||||
}
|
||||
print("\n");
|
||||
print(buf);
|
||||
if (got && buf[got - 1] != '\n') print("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char* cmd = SysGetPeb()->CommandLine.Data;
|
||||
char tok[64];
|
||||
cmd = next_token(cmd, tok, sizeof(tok)); // program path
|
||||
cmd = next_token(cmd, tok, sizeof(tok)); // verb
|
||||
if (!tok[0]) { usage(); SysExitProcess(1); }
|
||||
|
||||
int rc;
|
||||
if (strcmp(tok, "send") == 0) {
|
||||
rc = run_send(cmd);
|
||||
} else if (strcmp(tok, "listen") == 0) {
|
||||
rc = run_listen(cmd);
|
||||
} else {
|
||||
usage();
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
SysExitProcess(rc);
|
||||
return rc;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large.
Load diff
Binary file not shown.
@@ -0,0 +1,148 @@
|
||||
#include <windows.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include "bink.h"
|
||||
|
||||
static const char* commandLineSecondToken(const char* cmdline, char* out, size_t outSize) {
|
||||
if (!out || outSize == 0) return "video.bk2";
|
||||
strcpy(out, "video.bk2");
|
||||
if (!cmdline) return out;
|
||||
|
||||
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
|
||||
if (*cmdline == '\0') return out;
|
||||
|
||||
if (*cmdline == '"') {
|
||||
cmdline++;
|
||||
while (*cmdline && *cmdline != '"') cmdline++;
|
||||
if (*cmdline == '"') cmdline++;
|
||||
} else {
|
||||
while (*cmdline && *cmdline != ' ' && *cmdline != '\t') cmdline++;
|
||||
}
|
||||
|
||||
while (*cmdline == ' ' || *cmdline == '\t') cmdline++;
|
||||
if (*cmdline == '\0') return out;
|
||||
|
||||
size_t pos = 0;
|
||||
if (*cmdline == '"') {
|
||||
cmdline++;
|
||||
while (*cmdline && *cmdline != '"' && pos + 1 < outSize)
|
||||
out[pos++] = *cmdline++;
|
||||
} else {
|
||||
while (*cmdline && *cmdline != ' ' && *cmdline != '\t' && pos + 1 < outSize)
|
||||
out[pos++] = *cmdline++;
|
||||
}
|
||||
out[pos] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
const char* cmdline = GetCommandLineA();
|
||||
char videoPath[260];
|
||||
const char* path = commandLineSecondToken(cmdline, videoPath, sizeof(videoPath));
|
||||
|
||||
void* h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
WriteConsoleA(h, "video: starting\n", 16, 0, 0);
|
||||
|
||||
WriteConsoleA(h, cmdline, (unsigned int)strlen(cmdline), 0, 0);
|
||||
WriteConsoleA(h, "\n", 1, 0, 0);
|
||||
|
||||
void* f = CreateFileA(path, 0, 0, 0, 3, 0, 0);
|
||||
if (!f || f == (void*)(unsigned long long)0xFFFFFFFF) { // fuckass
|
||||
WriteConsoleA(h, "video: failed to open input file\n", 33, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
unsigned long s = GetFileSize(f, 0);
|
||||
if (!s || s == 0xFFFFFFFF) {
|
||||
WriteConsoleA(h, "video: GetFileSize failed!\n", 27, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
void* buf = HeapAlloc(0, 0, s);
|
||||
if (!buf) {
|
||||
WriteConsoleA(h, "video: HeapAlloc failed\n", 24, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
int r = ReadFile(f, buf, s, 0, 0);
|
||||
if (!r) {
|
||||
WriteConsoleA(h, "video: ReadFile failed\n", 23, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
WriteConsoleA(h, "video: input file loaded\n", 25, 0, 0);
|
||||
|
||||
CloseHandle(h);
|
||||
|
||||
HBINK b = BinkOpen(buf, BINKFROMMEMORY);
|
||||
if (!b) {
|
||||
WriteConsoleA(h, "video: BinkOpen failed: ", 24, 0, 0);
|
||||
const char* err = BinkGetError();
|
||||
WriteConsoleA(h, err, (unsigned int)strlen(err), 0, 0);
|
||||
WriteConsoleA(h, "\n", 1, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
uint32_t width = b->Width;
|
||||
uint32_t height = b->Height;
|
||||
|
||||
uint32_t* fb = HeapAlloc(0, 0, width * height * sizeof(uint32_t));
|
||||
if (!fb) {
|
||||
WriteConsoleA(h, "video: framebuffer alloc failed\n", 32, 0, 0);
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
// Prepare the Win32 BITMAPINFO structure
|
||||
BITMAPINFO bmi;
|
||||
memset(&bmi, 0, sizeof(bmi));
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = width;
|
||||
bmi.bmiHeader.biHeight = -(LONG)height; // Negative means top-down drawing!
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
while (1) {
|
||||
if (BinkWait(b)) {
|
||||
_mm_pause();
|
||||
continue;
|
||||
}
|
||||
|
||||
BinkDoFrame(b);
|
||||
|
||||
if (BinkShouldSkip(b)) {
|
||||
BinkNextFrame(b);
|
||||
BinkDoFrame(b);
|
||||
continue;
|
||||
}
|
||||
|
||||
BinkCopyToBuffer(b, fb, width * 4, height, 0, 0,
|
||||
BINKSURFACE32);
|
||||
|
||||
// Welcome to the GDI pipeline!
|
||||
PAINTSTRUCT ps;
|
||||
// Using NULL/0 as the HWND since we don't have a real window handle yet
|
||||
HDC hdc = BeginPaint((HWND)0, &ps);
|
||||
|
||||
StretchDIBits(
|
||||
hdc,
|
||||
0, 0, width, height, // Destination rect
|
||||
0, 0, width, height, // Source rect
|
||||
fb,
|
||||
&bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY
|
||||
);
|
||||
|
||||
EndPaint((HWND)0, &ps);
|
||||
|
||||
if (b->FrameNum == b->Frames)
|
||||
break;
|
||||
|
||||
BinkNextFrame(b);
|
||||
}
|
||||
|
||||
WriteConsoleA(h, "video: goodbye\n", 15, 0, 0);
|
||||
|
||||
ExitProcess(0);
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
#pragma once
|
||||
// posix.h - POSIX type and constant definitions for posix.dll
|
||||
// Covers just what open/read/write/close/lseek/spawn/pthread/chdir need.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// errno
|
||||
// ---------------------------------------------------------------------------
|
||||
#ifdef errno
|
||||
#undef errno
|
||||
#endif
|
||||
#ifdef EAGAIN
|
||||
#undef EAGAIN
|
||||
#endif
|
||||
#ifdef ENOSYS
|
||||
#undef ENOSYS
|
||||
#endif
|
||||
#ifdef EWOULDBLOCK
|
||||
#undef EWOULDBLOCK
|
||||
#endif
|
||||
#ifdef EINPROGRESS
|
||||
#undef EINPROGRESS
|
||||
#endif
|
||||
#ifdef ETIMEDOUT
|
||||
#undef ETIMEDOUT
|
||||
#endif
|
||||
#ifdef ECONNRESET
|
||||
#undef ECONNRESET
|
||||
#endif
|
||||
#ifdef ENOTSOCK
|
||||
#undef ENOTSOCK
|
||||
#endif
|
||||
extern int errno; // defined in posix_fd.c; single global in v1
|
||||
|
||||
#define EPERM 1
|
||||
#define ENOENT 2
|
||||
#define ESRCH 3
|
||||
#define EINTR 4
|
||||
#define EIO 5
|
||||
#define EBADF 9
|
||||
#define ENOMEM 12
|
||||
#define EACCES 13
|
||||
#define EEXIST 17
|
||||
#define ENODEV 19
|
||||
#define ENOTDIR 20
|
||||
#define EINVAL 22
|
||||
#define ESPIPE 29
|
||||
#define EMFILE 24
|
||||
#define ENOSPC 28
|
||||
#define ERANGE 34
|
||||
#define EAGAIN 35
|
||||
#define EWOULDBLOCK EAGAIN
|
||||
#define ENOSYS 38
|
||||
#define ENOTSOCK 88
|
||||
#define EINPROGRESS 115
|
||||
#define ETIMEDOUT 116
|
||||
#define ECONNRESET 104
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// open() flags
|
||||
// ---------------------------------------------------------------------------
|
||||
#define O_RDONLY 0x0000
|
||||
#define O_WRONLY 0x0001
|
||||
#define O_RDWR 0x0002
|
||||
#define O_CREAT 0x0040
|
||||
#define O_TRUNC 0x0200
|
||||
#define O_APPEND 0x0400
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// lseek() whence
|
||||
// ---------------------------------------------------------------------------
|
||||
#define SEEK_SET 0
|
||||
#define SEEK_CUR 1
|
||||
#define SEEK_END 2
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scalar types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef int32_t pid_t;
|
||||
typedef int32_t off_t;
|
||||
typedef uint32_t mode_t;
|
||||
typedef int64_t ssize_t;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Sockets
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef uint16_t sa_family_t;
|
||||
typedef uint16_t in_port_t;
|
||||
typedef uint32_t in_addr_t;
|
||||
typedef uint32_t socklen_t;
|
||||
|
||||
#define AF_INET 2
|
||||
#define PF_INET AF_INET
|
||||
#define SOCK_STREAM 1
|
||||
#define SOCK_DGRAM 2
|
||||
#define SOCK_RAW 3
|
||||
#define IPPROTO_ICMP 1
|
||||
#define IPPROTO_TCP 6
|
||||
#define IPPROTO_UDP 17
|
||||
|
||||
#define SHUT_RD 0
|
||||
#define SHUT_WR 1
|
||||
#define SHUT_RDWR 2
|
||||
|
||||
#define SOL_SOCKET 1
|
||||
#define SO_RCVTIMEO 1
|
||||
#define SO_SNDTIMEO 2
|
||||
#define SO_NONBLOCK 3
|
||||
|
||||
#define INADDR_ANY ((in_addr_t)0x00000000u)
|
||||
#define INADDR_NONE ((in_addr_t)0xFFFFFFFFu)
|
||||
|
||||
struct in_addr {
|
||||
in_addr_t s_addr;
|
||||
};
|
||||
|
||||
struct sockaddr {
|
||||
sa_family_t sa_family;
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
struct sockaddr_in {
|
||||
sa_family_t sin_family;
|
||||
in_port_t sin_port;
|
||||
struct in_addr sin_addr;
|
||||
char sin_zero[8];
|
||||
};
|
||||
|
||||
struct hostent {
|
||||
char* h_name;
|
||||
char** h_aliases;
|
||||
int h_addrtype;
|
||||
int h_length;
|
||||
char** h_addr_list;
|
||||
};
|
||||
#define h_addr h_addr_list[0]
|
||||
|
||||
struct addrinfo {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
socklen_t ai_addrlen;
|
||||
struct sockaddr* ai_addr;
|
||||
char* ai_canonname;
|
||||
struct addrinfo* ai_next;
|
||||
};
|
||||
|
||||
#define AI_PASSIVE 0x0001
|
||||
|
||||
struct pollfd {
|
||||
int fd;
|
||||
short events;
|
||||
short revents;
|
||||
};
|
||||
|
||||
#define POLLIN 0x0001
|
||||
#define POLLOUT 0x0002
|
||||
#define POLLERR 0x0004
|
||||
#define POLLHUP 0x0008
|
||||
#define POLLNVAL 0x0010
|
||||
|
||||
typedef unsigned long nfds_t;
|
||||
|
||||
typedef struct timeval {
|
||||
long tv_sec;
|
||||
long tv_usec;
|
||||
} timeval;
|
||||
|
||||
__declspec(dllexport) uint16_t htons(uint16_t x);
|
||||
__declspec(dllexport) uint16_t ntohs(uint16_t x);
|
||||
__declspec(dllexport) uint32_t htonl(uint32_t x);
|
||||
__declspec(dllexport) uint32_t ntohl(uint32_t x);
|
||||
__declspec(dllexport) in_addr_t inet_addr(const char* text);
|
||||
__declspec(dllexport) char* inet_ntoa(struct in_addr in);
|
||||
|
||||
__declspec(dllexport) int socket(int domain, int type, int protocol);
|
||||
__declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen);
|
||||
__declspec(dllexport) int listen(int fd, int backlog);
|
||||
__declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrlen);
|
||||
__declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t addrlen);
|
||||
__declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flags);
|
||||
__declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags);
|
||||
__declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int flags, const struct sockaddr* dest, socklen_t destlen);
|
||||
__declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags, struct sockaddr* src, socklen_t* srclen);
|
||||
__declspec(dllexport) int shutdown(int fd, int how);
|
||||
__declspec(dllexport) int setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen);
|
||||
__declspec(dllexport) int getsockopt(int fd, int level, int optname, void* optval, socklen_t* optlen);
|
||||
__declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout);
|
||||
__declspec(dllexport) struct hostent* gethostbyname(const char* name);
|
||||
__declspec(dllexport) int getaddrinfo(const char* node, const char* service, const struct addrinfo* hints, struct addrinfo** res);
|
||||
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// pthread types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct { uint32_t Tid; } pthread_t;
|
||||
typedef struct { uint32_t Handle; } pthread_mutex_t;
|
||||
typedef void* pthread_attr_t; // unused in v1
|
||||
typedef void* pthread_mutexattr_t;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// posix_spawn types (file_actions and attrp both ignored in v1)
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef void* posix_spawn_file_actions_t;
|
||||
typedef void* posix_spawnattr_t;
|
||||
|
||||
__declspec(dllexport) const char* posix_get_command_line(void);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// dirent (used by posix_dir.c)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define NAME_MAX 259
|
||||
|
||||
typedef struct dirent {
|
||||
char d_name[NAME_MAX + 1];
|
||||
unsigned char d_type; // DT_REG or DT_DIR
|
||||
} dirent;
|
||||
|
||||
#define DT_UNKNOWN 0
|
||||
#define DT_REG 8
|
||||
#define DT_DIR 4
|
||||
|
||||
typedef struct DIR {
|
||||
uint32_t KHandle; // kernel DIR_OBJECT handle
|
||||
dirent Current;
|
||||
bool Valid;
|
||||
} DIR;
|
||||
@@ -0,0 +1,69 @@
|
||||
// posix_dir.c - chdir, getcwd, opendir, readdir, closedir
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_path.h"
|
||||
|
||||
typedef SYS_DIR_ENTRY KDIR_ENTRY;
|
||||
|
||||
|
||||
#define KATTR_DIR 0x10
|
||||
|
||||
__declspec(dllexport) int chdir(const char* path) {
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(native) };
|
||||
uint32_t s = SysSetCwd(&req);
|
||||
if (s != 0) { errno = ENOENT; return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) char* getcwd(char* buf, size_t size) {
|
||||
if (!buf || size == 0) { errno = EINVAL; return (char*)0; }
|
||||
char native[512];
|
||||
size_t nativeLen = 0;
|
||||
SYS_GET_CWD_REQUEST req = { native, sizeof(native) - 1, &nativeLen };
|
||||
uint32_t s = SysGetCwd(&req);
|
||||
if (s == 0) native[nativeLen] = '\0';
|
||||
if (s != 0) { errno = ERANGE; return (char*)0; }
|
||||
NativeToPosix(native, buf, size);
|
||||
return buf;
|
||||
}
|
||||
|
||||
__declspec(dllexport) DIR* opendir(const char* path) {
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
uint32_t kh;
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(native), &kh };
|
||||
uint32_t s = SysOpenDir(&req);
|
||||
if (s != 0) { errno = ENOENT; return (DIR*)0; }
|
||||
|
||||
// Allocate DIR on the kernel heap (SysAlloc) so it outlives the stack.
|
||||
DIR* d = (DIR*)SysAlloc(sizeof(DIR));
|
||||
if (!d) { SysCloseHandle(kh); errno = ENOMEM; return (DIR*)0; }
|
||||
d->KHandle = kh;
|
||||
d->Valid = false;
|
||||
return d;
|
||||
}
|
||||
|
||||
__declspec(dllexport) struct dirent* readdir(DIR* d) {
|
||||
if (!d) { errno = EBADF; return (struct dirent*)0; }
|
||||
KDIR_ENTRY ke;
|
||||
uint32_t s = SysReadDir(d->KHandle, &ke);
|
||||
if (s != 0) return (struct dirent*)0; // end of directory or error
|
||||
|
||||
size_t len = strlen(ke.Name);
|
||||
if (len > NAME_MAX) len = NAME_MAX;
|
||||
memcpy(d->Current.d_name, ke.Name, len);
|
||||
d->Current.d_name[len] = '\0';
|
||||
d->Current.d_type = (ke.Attr & KATTR_DIR) ? DT_DIR : DT_REG;
|
||||
d->Valid = true;
|
||||
return &d->Current;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int closedir(DIR* d) {
|
||||
if (!d) { errno = EBADF; return -1; }
|
||||
SysCloseHandle(d->KHandle);
|
||||
SysFree(d);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
// posix_fd.c - file descriptor table
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
int errno = 0;
|
||||
|
||||
int PosixStatusToErrno(uint32_t s) {
|
||||
switch (s) {
|
||||
case 0x0000: return 0; // STATUS_SUCCESS
|
||||
case 0x0002: return ENOSYS; // STATUS_NOT_SUPPORTED
|
||||
case 0x0003: return EAGAIN; // STATUS_TIMEOUT / would block
|
||||
case 0x0010: return EINVAL; // STATUS_INVALID_PARAMETER
|
||||
case 0x0011: return EBADF; // STATUS_INVALID_HANDLE
|
||||
case 0x0012: return ERANGE; // STATUS_BUFFER_TOO_SMALL
|
||||
case 0x0020: return ENOMEM; // STATUS_NO_MEMORY
|
||||
case 0x0021: return EACCES; // STATUS_ACCESS_DENIED
|
||||
case 0x0022: return ENOENT; // STATUS_NOT_FOUND
|
||||
case 0x0023: return EEXIST; // STATUS_ALREADY_EXISTS
|
||||
case 0x0030: return ECONNRESET; // STATUS_OBJECT_DEAD
|
||||
case 0x0031: return EAGAIN; // STATUS_QUEUE_FULL
|
||||
case 0x0032: return EAGAIN; // STATUS_QUEUE_EMPTY
|
||||
default: return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static FD_ENTRY gFdTable[FD_TABLE_PIDS][FD_MAX];
|
||||
|
||||
__declspec(dllexport) void PosixFdInit(void) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
|
||||
// Clear this process's slice (handles inherited state from a previous
|
||||
// process that happened to land on the same slot - unlikely but safe).
|
||||
for (int i = 0; i < FD_MAX; i++)
|
||||
gFdTable[slot][i] = (FD_ENTRY){ FD_FREE, 0, false };
|
||||
|
||||
gFdTable[slot][0] = (FD_ENTRY){ FD_CONSOLE_R, 0, true };
|
||||
gFdTable[slot][1] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
|
||||
gFdTable[slot][2] = (FD_ENTRY){ FD_CONSOLE_W, 0, true };
|
||||
}
|
||||
|
||||
__declspec(dllexport) int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle) {
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
for (int fd = 3; fd < FD_MAX; fd++) {
|
||||
if (!gFdTable[slot][fd].Used) {
|
||||
gFdTable[slot][fd] = (FD_ENTRY){ type, khandle, true };
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
errno = EMFILE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
__declspec(dllexport) FD_ENTRY* FdGet(uint32_t pid, int fd) {
|
||||
if (fd < 0 || fd >= FD_MAX) { errno = EBADF; return (FD_ENTRY*)0; }
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
FD_ENTRY* e = &gFdTable[slot][fd];
|
||||
if (!e->Used) { errno = EBADF; return (FD_ENTRY*)0; }
|
||||
return e;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void FdFree(uint32_t pid, int fd) {
|
||||
if (fd < 0 || fd >= FD_MAX) return;
|
||||
uint32_t slot = pid % FD_TABLE_PIDS;
|
||||
gFdTable[slot][fd] = (FD_ENTRY){ FD_FREE, 0, false };
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
// posix_fd.h - file descriptor table (internal to posix.dll)
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
FD_FREE,
|
||||
FD_FILE,
|
||||
FD_CONSOLE_R, // fd 0 - reads from keyboard via SysKbReadChar
|
||||
FD_CONSOLE_W, // fd 1, 2 - writes via SysWriteConsole
|
||||
FD_DEVNULL, // /dev/null
|
||||
FD_DEVZERO, // /dev/zero
|
||||
FD_SOCKET, // kernel socket handle
|
||||
} FD_TYPE;
|
||||
|
||||
typedef struct {
|
||||
FD_TYPE Type;
|
||||
uint32_t KHandle; // kernel handle; 0 for console/devnull/devzero
|
||||
bool Used;
|
||||
} FD_ENTRY;
|
||||
|
||||
#define FD_MAX 64
|
||||
#define FD_TABLE_PIDS 32 // circular, indexed by pid % FD_TABLE_PIDS
|
||||
|
||||
// Called from _start before main(); sets up fds 0/1/2 for this process.
|
||||
__declspec(dllexport) void PosixFdInit(void);
|
||||
|
||||
// Allocate the lowest free fd >= 3. Returns -1 and sets errno=EMFILE on failure.
|
||||
__declspec(dllexport) int FdAlloc(uint32_t pid, FD_TYPE type, uint32_t khandle);
|
||||
|
||||
// Look up an fd. Returns NULL and sets errno=EBADF if invalid.
|
||||
__declspec(dllexport) FD_ENTRY* FdGet(uint32_t pid, int fd);
|
||||
|
||||
// Release an fd slot (does NOT close the kernel handle).
|
||||
__declspec(dllexport) void FdFree(uint32_t pid, int fd);
|
||||
|
||||
int PosixStatusToErrno(uint32_t s);
|
||||
@@ -0,0 +1,187 @@
|
||||
// posix_io.c - open, read, write, close, lseek
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
#include "posix_path.h"
|
||||
|
||||
__declspec(dllexport) void clearscreen(void) {
|
||||
SysScreenClear(0x00000000);
|
||||
}
|
||||
|
||||
__declspec(dllexport) void clearkeyboard(void) {
|
||||
while (SysKbPoll()) (void)SysKbReadChar();
|
||||
}
|
||||
|
||||
__declspec(dllexport) int open(const char* path, int flags, ...) {
|
||||
char native[512];
|
||||
const char* p = PosixTranslatePath(path, native, sizeof(native));
|
||||
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_TYPE type = FD_FILE;
|
||||
|
||||
// Synthetic devices
|
||||
if (strcmp(p, SYNTH_DEVNULL) == 0) {
|
||||
int fd = FdAlloc(pid, FD_DEVNULL, 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
if (strcmp(p, SYNTH_DEVZERO) == 0) {
|
||||
int fd = FdAlloc(pid, FD_DEVZERO, 0);
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint32_t kh = 0;
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(p), &kh };
|
||||
uint32_t s;
|
||||
if (flags & O_WRONLY) {
|
||||
s = SysCreateFile(&req);
|
||||
} else {
|
||||
s = SysOpenFile(&req);
|
||||
}
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
if ((flags & O_TRUNC) && (flags & O_WRONLY))
|
||||
SysTruncateFile(kh, 0);
|
||||
|
||||
int fd = FdAlloc(pid, type, kh);
|
||||
if (fd < 0) {
|
||||
SysCloseHandle(kh); // FdAlloc already set errno
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t read(int fd, void* buf, size_t count) {
|
||||
if (count == 0) return 0;
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return -1;
|
||||
|
||||
switch (e->Type) {
|
||||
case FD_CONSOLE_R: {
|
||||
// Read up to count chars from keyboard, blocking per char.
|
||||
char* dst = (char*)buf;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
dst[i] = SysKbReadChar();
|
||||
if (dst[i] == '\n' || dst[i] == '\r') return (ssize_t)(i + 1);
|
||||
}
|
||||
return (ssize_t)count;
|
||||
}
|
||||
case FD_DEVNULL:
|
||||
return 0;
|
||||
case FD_DEVZERO:
|
||||
memset(buf, 0, count);
|
||||
return (ssize_t)count;
|
||||
case FD_FILE: {
|
||||
size_t got = 0;
|
||||
uint32_t s = SysReadFile(e->KHandle, buf, count, &got);
|
||||
if (s != 0 && got == 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
case FD_SOCKET: {
|
||||
size_t got = 0;
|
||||
uint32_t s = SysRecv(e->KHandle, buf, count, 0, &got);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
default:
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t write(int fd, const void* buf, size_t count) {
|
||||
if (count == 0) return 0;
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return -1;
|
||||
|
||||
switch (e->Type) {
|
||||
case FD_CONSOLE_W: {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts((const char*)buf, count) };
|
||||
SysWriteConsole(&req);
|
||||
return (ssize_t)count;
|
||||
}
|
||||
case FD_DEVNULL:
|
||||
case FD_DEVZERO:
|
||||
return (ssize_t)count;
|
||||
case FD_FILE: {
|
||||
size_t written = 0;
|
||||
uint32_t s = SysWriteFile(e->KHandle, buf, count, &written);
|
||||
if (s != 0 && written == 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)written;
|
||||
}
|
||||
case FD_SOCKET: {
|
||||
size_t sent = 0;
|
||||
uint32_t s = SysSend(e->KHandle, buf, count, 0, &sent);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)sent;
|
||||
}
|
||||
default:
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(dllexport) int close(int fd) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e) return -1;
|
||||
if (e->Type == FD_FILE || e->Type == FD_SOCKET)
|
||||
SysCloseHandle(e->KHandle);
|
||||
FdFree(pid, fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int unlink(const char* path) {
|
||||
if (!path) { errno = EINVAL; return -1; }
|
||||
|
||||
char native[512];
|
||||
const char* p = PosixTranslatePath(path, native, sizeof(native));
|
||||
|
||||
if (strcmp(p, SYNTH_DEVNULL) == 0 || strcmp(p, SYNTH_DEVZERO) == 0) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SYS_PATH_REQUEST req = { SysSvFromCstr(p) };
|
||||
uint32_t s = SysDeleteFile(&req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) off_t lseek(int fd, off_t offset, int whence) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
FD_ENTRY* e = FdGet(pid, fd);
|
||||
if (!e || e->Type != FD_FILE) { errno = (e ? ESPIPE : EBADF); return -1; }
|
||||
|
||||
int64_t target = 0;
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
target = (int64_t)offset;
|
||||
break;
|
||||
case SEEK_CUR: {
|
||||
uint64_t cur = SysGetFilePos(e->KHandle);
|
||||
if (cur > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
|
||||
target = (int64_t)cur + (int64_t)offset;
|
||||
break;
|
||||
}
|
||||
case SEEK_END: {
|
||||
uint64_t size = SysGetFileSize(e->KHandle);
|
||||
if (size > 0x7FFFFFFFULL) { errno = EINVAL; return -1; }
|
||||
target = (int64_t)size + (int64_t)offset;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (target < 0 || target > 0x7FFFFFFFLL) { errno = EINVAL; return -1; }
|
||||
|
||||
uint32_t s = SysSeekFile(e->KHandle, (uint64_t)target);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (off_t)target;
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// posix_path.c - POSIX <-> native path translation
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include "posix_path.h"
|
||||
|
||||
static void FlipSlashes(char* s) {
|
||||
for (; *s; s++) if (*s == '/') *s = '\\';
|
||||
}
|
||||
|
||||
static int IsDriveMount(const char* p) {
|
||||
return p && p[0] == '/' && p[1] >= '0' && p[1] <= '7' &&
|
||||
(p[2] == '\0' || p[2] == '/');
|
||||
}
|
||||
|
||||
const char* PosixTranslatePath(const char* in, char* out, size_t outSize) {
|
||||
if (!in || !out || outSize < 4) return out;
|
||||
|
||||
if (strcmp(in, "/dev/null") == 0) return SYNTH_DEVNULL;
|
||||
if (strcmp(in, "/dev/zero") == 0) return SYNTH_DEVZERO;
|
||||
|
||||
if (IsDriveMount(in)) {
|
||||
out[0] = in[1];
|
||||
out[1] = ':';
|
||||
out[2] = '/';
|
||||
const char* rest = (in[2] == '/') ? in + 3 : "";
|
||||
size_t restLen = strlen(rest);
|
||||
if (restLen + 4 > outSize) restLen = outSize - 4;
|
||||
memcpy(out + 3, rest, restLen);
|
||||
out[3 + restLen] = '\0';
|
||||
FlipSlashes(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
if (in[0] == '/') {
|
||||
out[0] = '0';
|
||||
out[1] = ':';
|
||||
out[2] = '/';
|
||||
size_t rest = strlen(in + 1);
|
||||
if (rest + 4 > outSize) rest = outSize - 4;
|
||||
memcpy(out + 3, in + 1, rest);
|
||||
out[3 + rest] = '\0';
|
||||
} else {
|
||||
size_t len = strlen(in);
|
||||
if (len + 1 > outSize) len = outSize - 1;
|
||||
memcpy(out, in, len);
|
||||
out[len] = '\0';
|
||||
}
|
||||
|
||||
FlipSlashes(out);
|
||||
return out;
|
||||
}
|
||||
|
||||
void NativeToPosix(const char* in, char* out, size_t outSize) {
|
||||
if (!in || !out || outSize == 0) return;
|
||||
|
||||
const char* src = in;
|
||||
char drive = 0;
|
||||
if (src[0] >= '0' && src[0] <= '7' && src[1] == ':' &&
|
||||
(src[2] == '\\' || src[2] == '/')) {
|
||||
drive = src[0];
|
||||
src += 3;
|
||||
}
|
||||
|
||||
size_t pos = 0;
|
||||
if (drive && drive != '0') {
|
||||
if (outSize < 4) { out[0] = '\0'; return; }
|
||||
out[pos++] = '/';
|
||||
out[pos++] = drive;
|
||||
if (*src) out[pos++] = '/';
|
||||
} else {
|
||||
out[pos++] = '/';
|
||||
}
|
||||
|
||||
while (*src && pos + 1 < outSize) {
|
||||
out[pos++] = (*src == '\\') ? '/' : *src;
|
||||
src++;
|
||||
}
|
||||
out[pos] = '\0';
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
// posix_path.h - POSIX <-> native path translation
|
||||
|
||||
// SYNTHETIC sentinel returned from PosixTranslatePath for /dev/null and
|
||||
// /dev/zero; the caller must handle these before passing to any syscall.
|
||||
#define SYNTH_DEVNULL "\x01<devnull>"
|
||||
#define SYNTH_DEVZERO "\x01<devzero>"
|
||||
|
||||
// Translate a POSIX path to a native kernel path.
|
||||
// Returns a pointer to 'out' on success, or SYNTH_DEVNULL / SYNTH_DEVZERO
|
||||
// for those synthetic devices. 'out' must be at least 512 bytes.
|
||||
const char* PosixTranslatePath(const char* in, char* out, size_t outSize);
|
||||
|
||||
// Translate a native kernel path back to a POSIX-style path.
|
||||
// Strips the drive letter prefix and flips backslashes.
|
||||
void NativeToPosix(const char* in, char* out, size_t outSize);
|
||||
@@ -0,0 +1,138 @@
|
||||
// posix_proc.c - posix_spawn, waitpid, getpid
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_path.h"
|
||||
|
||||
|
||||
static SYS_PEB* gProcessPeb = NULL;
|
||||
|
||||
static SYS_PEB* PosixPeb(void) {
|
||||
if (!gProcessPeb) gProcessPeb = SysGetPeb();
|
||||
return gProcessPeb;
|
||||
}
|
||||
|
||||
__declspec(dllexport) const char* posix_get_command_line(void) {
|
||||
SYS_PEB* peb = PosixPeb();
|
||||
if (!peb || peb->Magic != SYS_PEB_MAGIC || !peb->CommandLine.Data) return "";
|
||||
return peb->CommandLine.Data;
|
||||
}
|
||||
|
||||
// pid -> proc handle table (user-mode side, for waitpid)
|
||||
#define PROC_TABLE_SIZE 32
|
||||
typedef struct {
|
||||
pid_t pid;
|
||||
uint32_t handle;
|
||||
bool used;
|
||||
} ProcEntry;
|
||||
|
||||
static ProcEntry gProcTable[PROC_TABLE_SIZE];
|
||||
|
||||
static void ProcTableInsert(pid_t pid, uint32_t handle) {
|
||||
for (int i = 0; i < PROC_TABLE_SIZE; i++) {
|
||||
if (!gProcTable[i].used) {
|
||||
gProcTable[i] = (ProcEntry){ pid, handle, true };
|
||||
return;
|
||||
}
|
||||
}
|
||||
SysCloseHandle(handle);
|
||||
}
|
||||
|
||||
static uint32_t ProcTableFind(pid_t pid) {
|
||||
for (int i = 0; i < PROC_TABLE_SIZE; i++)
|
||||
if (gProcTable[i].used && gProcTable[i].pid == pid)
|
||||
return gProcTable[i].handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ProcTableRemove(pid_t pid) {
|
||||
for (int i = 0; i < PROC_TABLE_SIZE; i++)
|
||||
if (gProcTable[i].used && gProcTable[i].pid == pid)
|
||||
gProcTable[i].used = false;
|
||||
}
|
||||
|
||||
// Build a flat Windows-style cmdline from a NULL-terminated argv array.
|
||||
// Each token that contains a space is wrapped in double-quotes.
|
||||
// argv[0] is included (it becomes the executable token visible to _start).
|
||||
static void BuildCmdline(char* const argv[], char* out, size_t outSize) {
|
||||
size_t pos = 0;
|
||||
for (int i = 0; argv && argv[i]; i++) {
|
||||
if (i > 0 && pos < outSize - 1) out[pos++] = ' ';
|
||||
const char* tok = argv[i];
|
||||
bool needQuote = false;
|
||||
for (const char* c = tok; *c; c++) if (*c == ' ' || *c == '\t') { needQuote = true; break; }
|
||||
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||
for (; *tok && pos < outSize - 2; tok++) {
|
||||
if (*tok == '"') { out[pos++] = '\\'; }
|
||||
out[pos++] = *tok;
|
||||
}
|
||||
if (needQuote && pos < outSize - 1) out[pos++] = '"';
|
||||
}
|
||||
if (pos < outSize) out[pos] = '\0';
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// POSIX API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) pid_t getpid(void) {
|
||||
return (pid_t)SysGetCurrentPid();
|
||||
}
|
||||
|
||||
__declspec(dllexport) int posix_spawn(pid_t* pidOut,
|
||||
const char* path,
|
||||
const posix_spawn_file_actions_t* file_actions, // ignored v1
|
||||
const posix_spawnattr_t* attrp, // ignored v1
|
||||
char* const argv[],
|
||||
char* const envp[]) // ignored v1
|
||||
{
|
||||
(void)file_actions; (void)attrp; (void)envp;
|
||||
|
||||
char native[512];
|
||||
PosixTranslatePath(path, native, sizeof(native));
|
||||
|
||||
char cmdline[512];
|
||||
BuildCmdline(argv, cmdline, sizeof(cmdline));
|
||||
// If argv was NULL or empty, fall back to just the path so _start still
|
||||
// gets a meaningful argv[0].
|
||||
if (cmdline[0] == '\0') {
|
||||
size_t len = strlen(path);
|
||||
if (len >= sizeof(cmdline)) len = sizeof(cmdline) - 1;
|
||||
memcpy(cmdline, path, len);
|
||||
cmdline[len] = '\0';
|
||||
}
|
||||
|
||||
uint32_t procHandle;
|
||||
SYS_CREATE_PROCESS_REQUEST req = { SysSvFromCstr(native), SysSvFromCstr(cmdline), &procHandle };
|
||||
uint32_t s = SysCreateProcess(&req);
|
||||
if (s != 0) { errno = ENOENT; return -1; }
|
||||
|
||||
uint32_t kpid = 0;
|
||||
SysGetProcessPid(procHandle, &kpid);
|
||||
ProcTableInsert((pid_t)kpid, procHandle);
|
||||
if (pidOut) *pidOut = (pid_t)kpid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// options: WNOHANG not supported in v1 (always blocks).
|
||||
__declspec(dllexport) pid_t waitpid(pid_t pid, int* status, int options) {
|
||||
(void)options;
|
||||
|
||||
uint32_t handle = ProcTableFind(pid);
|
||||
if (!handle) { errno = ESRCH; return -1; }
|
||||
|
||||
SysWaitForProcess(handle);
|
||||
|
||||
int exitCode = 0;
|
||||
SysGetProcessExitCode(handle, &exitCode);
|
||||
if (status) *status = exitCode << 8; // WEXITSTATUS convention
|
||||
|
||||
SysCloseHandle(handle);
|
||||
ProcTableRemove(pid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void exit(int status) {
|
||||
SysExitProcess(status);
|
||||
}
|
||||
@@ -0,0 +1,471 @@
|
||||
// posix_socket.c - POSIX-ish socket wrappers over native Sys* sockets.
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
#include "posix_fd.h"
|
||||
|
||||
static uint16_t bswap16(uint16_t x) {
|
||||
return (uint16_t)((x << 8) | (x >> 8));
|
||||
}
|
||||
|
||||
static uint32_t bswap32(uint32_t x) {
|
||||
return ((x & 0x000000FFu) << 24) |
|
||||
((x & 0x0000FF00u) << 8) |
|
||||
((x & 0x00FF0000u) >> 8) |
|
||||
((x & 0xFF000000u) >> 24);
|
||||
}
|
||||
|
||||
__declspec(dllexport) uint16_t htons(uint16_t x) { return bswap16(x); }
|
||||
__declspec(dllexport) uint16_t ntohs(uint16_t x) { return bswap16(x); }
|
||||
__declspec(dllexport) uint32_t htonl(uint32_t x) { return bswap32(x); }
|
||||
__declspec(dllexport) uint32_t ntohl(uint32_t x) { return bswap32(x); }
|
||||
|
||||
static int ParseByte(const char** p, uint8_t* out) {
|
||||
const char* s = *p;
|
||||
if (*s < '0' || *s > '9') return 0;
|
||||
unsigned v = 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10 + (unsigned)(*s - '0');
|
||||
if (v > 255) return 0;
|
||||
s++;
|
||||
}
|
||||
*out = (uint8_t)v;
|
||||
*p = s;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ParseIpv4Bytes(const char* text, uint8_t out[4]) {
|
||||
const char* p = text;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (!ParseByte(&p, &out[i])) return 0;
|
||||
if (i != 3) {
|
||||
if (*p != '.') return 0;
|
||||
p++;
|
||||
}
|
||||
}
|
||||
return *p == '\0';
|
||||
}
|
||||
|
||||
__declspec(dllexport) in_addr_t inet_addr(const char* text) {
|
||||
uint8_t b[4];
|
||||
if (!text || !ParseIpv4Bytes(text, b)) return INADDR_NONE;
|
||||
in_addr_t out = 0;
|
||||
uint8_t* p = (uint8_t*)&out;
|
||||
p[0] = b[0]; p[1] = b[1]; p[2] = b[2]; p[3] = b[3];
|
||||
return out;
|
||||
}
|
||||
|
||||
__declspec(dllexport) char* inet_ntoa(struct in_addr in) {
|
||||
static char buf[16];
|
||||
const uint8_t* p = (const uint8_t*)&in.s_addr;
|
||||
// Tiny decimal formatter, avoiding stdio dependency.
|
||||
char* w = buf;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
unsigned v = p[i];
|
||||
if (v >= 100) { *w++ = (char)('0' + v / 100); v %= 100; *w++ = (char)('0' + v / 10); *w++ = (char)('0' + v % 10); }
|
||||
else if (v >= 10) { *w++ = (char)('0' + v / 10); *w++ = (char)('0' + v % 10); }
|
||||
else { *w++ = (char)('0' + v); }
|
||||
if (i != 3) *w++ = '.';
|
||||
}
|
||||
*w = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int SockAddrToSys(const struct sockaddr* addr, socklen_t addrlen, SYS_SOCKET_ADDRESS_IPV4* out) {
|
||||
if (!addr || !out || addrlen < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; }
|
||||
const struct sockaddr_in* in = (const struct sockaddr_in*)addr;
|
||||
if (in->sin_family != AF_INET) { errno = EINVAL; return -1; }
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Family = SYS_AF_INET;
|
||||
out->Port = ntohs(in->sin_port);
|
||||
const uint8_t* p = (const uint8_t*)&in->sin_addr.s_addr;
|
||||
out->Address[0] = p[0];
|
||||
out->Address[1] = p[1];
|
||||
out->Address[2] = p[2];
|
||||
out->Address[3] = p[3];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void SysToSockAddr(const SYS_SOCKET_ADDRESS_IPV4* in, struct sockaddr* addr, socklen_t* addrlen) {
|
||||
if (!addr || !addrlen) return;
|
||||
if (*addrlen < sizeof(struct sockaddr_in)) {
|
||||
*addrlen = sizeof(struct sockaddr_in);
|
||||
return;
|
||||
}
|
||||
|
||||
struct sockaddr_in* out = (struct sockaddr_in*)addr;
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->sin_family = AF_INET;
|
||||
out->sin_port = htons(in->Port);
|
||||
uint8_t* p = (uint8_t*)&out->sin_addr.s_addr;
|
||||
p[0] = in->Address[0];
|
||||
p[1] = in->Address[1];
|
||||
p[2] = in->Address[2];
|
||||
p[3] = in->Address[3];
|
||||
*addrlen = sizeof(struct sockaddr_in);
|
||||
}
|
||||
|
||||
static FD_ENTRY* SocketFdGet(int fd) {
|
||||
FD_ENTRY* e = FdGet(SysGetCurrentPid(), fd);
|
||||
if (!e) return (FD_ENTRY*)0;
|
||||
if (e->Type != FD_SOCKET) { errno = ENOTSOCK; return (FD_ENTRY*)0; }
|
||||
return e;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int socket(int domain, int type, int protocol) {
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysSocket((uint32_t)domain, (uint32_t)type, (uint32_t)protocol, &kh);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
int fd = FdAlloc(SysGetCurrentPid(), FD_SOCKET, kh);
|
||||
if (fd < 0) {
|
||||
SysCloseHandle(kh);
|
||||
return -1;
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
SYS_SOCKET_ADDRESS_IPV4 a;
|
||||
if (SockAddrToSys(addr, addrlen, &a) != 0) return -1;
|
||||
uint32_t s = SysBind(e->KHandle, &a, sizeof(a));
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int listen(int fd, int backlog) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
uint32_t s = SysListen(e->KHandle, backlog < 0 ? 0u : (uint32_t)backlog);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrlen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
size_t remoteSize = sizeof(remote);
|
||||
uint32_t kh = 0;
|
||||
uint32_t s = SysAccept(e->KHandle, addr ? &remote : (SYS_SOCKET_ADDRESS_IPV4*)0,
|
||||
addr ? &remoteSize : (size_t*)0, &kh);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
int outfd = FdAlloc(SysGetCurrentPid(), FD_SOCKET, kh);
|
||||
if (outfd < 0) {
|
||||
SysCloseHandle(kh);
|
||||
return -1;
|
||||
}
|
||||
if (addr) SysToSockAddr(&remote, addr, addrlen);
|
||||
return outfd;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t addrlen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
SYS_SOCKET_ADDRESS_IPV4 a;
|
||||
if (SockAddrToSys(addr, addrlen, &a) != 0) return -1;
|
||||
uint32_t s = SysConnect(e->KHandle, &a, sizeof(a));
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flags) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
size_t sent = 0;
|
||||
uint32_t s = SysSend(e->KHandle, buf, len, (uint32_t)flags, &sent);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)sent;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
size_t got = 0;
|
||||
uint32_t s = SysRecv(e->KHandle, buf, len, (uint32_t)flags, &got);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)got;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int flags,
|
||||
const struct sockaddr* dest, socklen_t destlen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
if (SockAddrToSys(dest, destlen, &remote) != 0) return -1;
|
||||
|
||||
size_t sent = 0;
|
||||
SYS_SOCKET_SEND_TO_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Buffer = buf;
|
||||
req.Length = len;
|
||||
req.Flags = (uint32_t)flags;
|
||||
req.RemoteAddress = &remote;
|
||||
req.RemoteAddressSize = sizeof(remote);
|
||||
req.SentOut = &sent;
|
||||
|
||||
uint32_t s = SysSendTo(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return (ssize_t)sent;
|
||||
}
|
||||
|
||||
__declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags,
|
||||
struct sockaddr* src, socklen_t* srclen) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 remote;
|
||||
size_t remoteSize = sizeof(remote);
|
||||
size_t got = 0;
|
||||
SYS_SOCKET_RECV_FROM_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Buffer = buf;
|
||||
req.Length = len;
|
||||
req.Flags = (uint32_t)flags;
|
||||
req.RemoteAddressOut = src ? &remote : (SYS_SOCKET_ADDRESS_IPV4*)0;
|
||||
req.RemoteAddressSizeInOut = src ? &remoteSize : (size_t*)0;
|
||||
req.ReceivedOut = &got;
|
||||
|
||||
uint32_t s = SysRecvFrom(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
if (src) SysToSockAddr(&remote, src, srclen);
|
||||
return (ssize_t)got;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int shutdown(int fd, int how) {
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
uint32_t s = SysShutdown(e->KHandle, (uint32_t)how);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t TimevalToMs(const struct timeval* tv) {
|
||||
if (!tv) return 0;
|
||||
uint64_t ms = (uint64_t)tv->tv_sec * 1000u + (uint64_t)(tv->tv_usec / 1000);
|
||||
return ms > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)ms;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
|
||||
if (level != SOL_SOCKET || !optval) { errno = EINVAL; return -1; }
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
uint32_t option = 0;
|
||||
uint32_t value = 0;
|
||||
if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
|
||||
option = (optname == SO_RCVTIMEO) ? SYS_SOCKET_OPT_RECV_TIMEOUT_MS : SYS_SOCKET_OPT_SEND_TIMEOUT_MS;
|
||||
if (optlen == sizeof(struct timeval)) value = TimevalToMs((const struct timeval*)optval);
|
||||
else if (optlen == sizeof(uint32_t)) memcpy(&value, optval, sizeof(value));
|
||||
else { errno = EINVAL; return -1; }
|
||||
} else if (optname == SO_NONBLOCK) {
|
||||
option = SYS_SOCKET_OPT_NONBLOCKING;
|
||||
if (optlen < sizeof(int)) { errno = EINVAL; return -1; }
|
||||
value = (*(const int*)optval) ? 1u : 0u;
|
||||
} else {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SYS_SOCKET_OPTION_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Option = option;
|
||||
req.Value = &value;
|
||||
req.ValueSize = sizeof(value);
|
||||
uint32_t s = SysSetSocketOption(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int getsockopt(int fd, int level, int optname, void* optval, socklen_t* optlen) {
|
||||
if (level != SOL_SOCKET || !optval || !optlen) { errno = EINVAL; return -1; }
|
||||
FD_ENTRY* e = SocketFdGet(fd);
|
||||
if (!e) return -1;
|
||||
|
||||
uint32_t option = 0;
|
||||
if (optname == SO_RCVTIMEO) option = SYS_SOCKET_OPT_RECV_TIMEOUT_MS;
|
||||
else if (optname == SO_SNDTIMEO) option = SYS_SOCKET_OPT_SEND_TIMEOUT_MS;
|
||||
else if (optname == SO_NONBLOCK) option = SYS_SOCKET_OPT_NONBLOCKING;
|
||||
else { errno = ENOSYS; return -1; }
|
||||
|
||||
uint32_t value = 0;
|
||||
size_t actual = sizeof(value);
|
||||
SYS_SOCKET_OPTION_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Option = option;
|
||||
req.Value = &value;
|
||||
req.ValueSize = sizeof(value);
|
||||
req.ActualSizeOut = &actual;
|
||||
uint32_t s = SysGetSocketOption(e->KHandle, &req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
|
||||
if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && *optlen >= sizeof(struct timeval)) {
|
||||
struct timeval* tv = (struct timeval*)optval;
|
||||
tv->tv_sec = (long)(value / 1000u);
|
||||
tv->tv_usec = (long)((value % 1000u) * 1000u);
|
||||
*optlen = sizeof(struct timeval);
|
||||
} else if (*optlen >= sizeof(uint32_t)) {
|
||||
memcpy(optval, &value, sizeof(value));
|
||||
*optlen = sizeof(value);
|
||||
} else {
|
||||
errno = ERANGE;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout) {
|
||||
if (!fds || nfds == 0 || nfds > 64) { errno = EINVAL; return -1; }
|
||||
|
||||
SYS_POLLFD* kfds = (SYS_POLLFD*)SysAlloc(sizeof(SYS_POLLFD) * nfds);
|
||||
if (!kfds) { errno = ENOMEM; return -1; }
|
||||
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
kfds[i].Handle = 0;
|
||||
kfds[i].Events = 0;
|
||||
kfds[i].ReturnedEvents = 0;
|
||||
|
||||
fds[i].revents = 0;
|
||||
FD_ENTRY* e = FdGet(SysGetCurrentPid(), fds[i].fd);
|
||||
if (!e || e->Type != FD_SOCKET) {
|
||||
continue;
|
||||
}
|
||||
kfds[i].Handle = e->KHandle;
|
||||
if (fds[i].events & POLLIN) kfds[i].Events |= SYS_POLL_READ;
|
||||
if (fds[i].events & POLLOUT) kfds[i].Events |= SYS_POLL_WRITE;
|
||||
if (fds[i].events & POLLERR) kfds[i].Events |= SYS_POLL_ERROR;
|
||||
if (fds[i].events & POLLHUP) kfds[i].Events |= SYS_POLL_HANGUP;
|
||||
}
|
||||
|
||||
size_t ready = 0;
|
||||
uint32_t timeoutMs = timeout < 0 ? SYS_POLL_TIMEOUT_INFINITE : (uint32_t)timeout;
|
||||
uint32_t s = SysPoll(kfds, nfds, timeoutMs, &ready);
|
||||
if (s != 0) {
|
||||
SysFree(kfds);
|
||||
errno = PosixStatusToErrno(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int out = 0;
|
||||
for (nfds_t i = 0; i < nfds; i++) {
|
||||
short re = 0;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_READ) re |= POLLIN;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_WRITE) re |= POLLOUT;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_ERROR) re |= POLLERR;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_HANGUP) re |= POLLHUP;
|
||||
if (kfds[i].ReturnedEvents & SYS_POLL_INVALID) re |= POLLNVAL;
|
||||
fds[i].revents = re;
|
||||
if (re) out++;
|
||||
}
|
||||
|
||||
SysFree(kfds);
|
||||
return out;
|
||||
}
|
||||
|
||||
static unsigned ParsePort(const char* s) {
|
||||
unsigned v = 0;
|
||||
if (!s) return 0;
|
||||
while (*s >= '0' && *s <= '9') {
|
||||
v = v * 10 + (unsigned)(*s - '0');
|
||||
if (v > 65535u) return 0;
|
||||
s++;
|
||||
}
|
||||
return *s == '\0' ? v : 0;
|
||||
}
|
||||
|
||||
static int ResolveNameIpv4(const char* name, SYS_SOCKET_ADDRESS_IPV4* out) {
|
||||
uint8_t b[4];
|
||||
if (ParseIpv4Bytes(name, b)) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->Family = SYS_AF_INET;
|
||||
out->Address[0] = b[0]; out->Address[1] = b[1]; out->Address[2] = b[2]; out->Address[3] = b[3];
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t size = sizeof(*out);
|
||||
SYS_RESOLVE_HOST_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.Name = SysSvFromCstr(name);
|
||||
req.Family = SYS_AF_INET;
|
||||
req.AddressOut = out;
|
||||
req.AddressSizeInOut = &size;
|
||||
uint32_t s = SysResolveHost(&req);
|
||||
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) struct hostent* gethostbyname(const char* name) {
|
||||
static struct hostent h;
|
||||
static char hname[256];
|
||||
static char* aliases[1];
|
||||
static char* addr_list[2];
|
||||
static uint8_t addr[4];
|
||||
|
||||
if (!name || strlen(name) >= sizeof(hname)) { errno = EINVAL; return (struct hostent*)0; }
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 a;
|
||||
if (ResolveNameIpv4(name, &a) != 0) return (struct hostent*)0;
|
||||
|
||||
strcpy(hname, name);
|
||||
memcpy(addr, a.Address, 4);
|
||||
aliases[0] = (char*)0;
|
||||
addr_list[0] = (char*)addr;
|
||||
addr_list[1] = (char*)0;
|
||||
|
||||
h.h_name = hname;
|
||||
h.h_aliases = aliases;
|
||||
h.h_addrtype = AF_INET;
|
||||
h.h_length = 4;
|
||||
h.h_addr_list = addr_list;
|
||||
return &h;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int getaddrinfo(const char* node, const char* service,
|
||||
const struct addrinfo* hints, struct addrinfo** res) {
|
||||
if (!res) return EINVAL;
|
||||
*res = (struct addrinfo*)0;
|
||||
|
||||
int family = hints ? hints->ai_family : AF_INET;
|
||||
if (family != AF_INET && family != 0) return EINVAL;
|
||||
|
||||
SYS_SOCKET_ADDRESS_IPV4 a;
|
||||
memset(&a, 0, sizeof(a));
|
||||
a.Family = SYS_AF_INET;
|
||||
if (!node) {
|
||||
a.Address[0] = 0; a.Address[1] = 0; a.Address[2] = 0; a.Address[3] = 0;
|
||||
} else if (ResolveNameIpv4(node, &a) != 0) {
|
||||
return errno;
|
||||
}
|
||||
a.Port = service ? ParsePort(service) : 0;
|
||||
|
||||
size_t total = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
|
||||
struct addrinfo* ai = (struct addrinfo*)SysAlloc(total);
|
||||
if (!ai) return ENOMEM;
|
||||
memset(ai, 0, total);
|
||||
|
||||
struct sockaddr_in* sin = (struct sockaddr_in*)((uint8_t*)ai + sizeof(struct addrinfo));
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_port = htons(a.Port);
|
||||
uint8_t* p = (uint8_t*)&sin->sin_addr.s_addr;
|
||||
p[0] = a.Address[0]; p[1] = a.Address[1]; p[2] = a.Address[2]; p[3] = a.Address[3];
|
||||
|
||||
ai->ai_family = AF_INET;
|
||||
ai->ai_socktype = hints ? hints->ai_socktype : 0;
|
||||
ai->ai_protocol = hints ? hints->ai_protocol : 0;
|
||||
ai->ai_addrlen = sizeof(struct sockaddr_in);
|
||||
ai->ai_addr = (struct sockaddr*)sin;
|
||||
ai->ai_next = (struct addrinfo*)0;
|
||||
*res = ai;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res) {
|
||||
if (res) SysFree(res);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// posix_thread.c - pthread_create, pthread_join, pthread_exit, pthread_mutex_*
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
#include "posix.h"
|
||||
|
||||
// Thread return-value table (PID-keyed like the fd table)
|
||||
#define PTHREAD_TABLE_PIDS 32
|
||||
#define PTHREAD_TABLE_SIZE 32
|
||||
|
||||
typedef struct {
|
||||
uint32_t Tid;
|
||||
void* RetVal;
|
||||
bool Used;
|
||||
} PTHREAD_SLOT;
|
||||
|
||||
static PTHREAD_SLOT gPthreadTable[PTHREAD_TABLE_PIDS][PTHREAD_TABLE_SIZE];
|
||||
|
||||
static PTHREAD_SLOT* PthreadSlotAlloc(uint32_t pid, uint32_t tid) {
|
||||
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
||||
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++) {
|
||||
if (!table[i].Used) {
|
||||
table[i] = (PTHREAD_SLOT){ tid, (void*)0, true };
|
||||
return &table[i];
|
||||
}
|
||||
}
|
||||
return (PTHREAD_SLOT*)0;
|
||||
}
|
||||
|
||||
static PTHREAD_SLOT* PthreadSlotFind(uint32_t pid, uint32_t tid) {
|
||||
PTHREAD_SLOT* table = gPthreadTable[pid % PTHREAD_TABLE_PIDS];
|
||||
for (int i = 0; i < PTHREAD_TABLE_SIZE; i++)
|
||||
if (table[i].Used && table[i].Tid == tid)
|
||||
return &table[i];
|
||||
return (PTHREAD_SLOT*)0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Trampoline - this is what actually runs as the new thread
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct {
|
||||
void* (*Fn)(void*);
|
||||
void* Arg;
|
||||
uint32_t Pid; // owner pid, for the retval table
|
||||
} PTHREAD_TRAMP;
|
||||
|
||||
static void PthreadTrampoline(uintptr_t raw) {
|
||||
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)raw;
|
||||
void* (*fn)(void*) = tr->Fn;
|
||||
void* arg = tr->Arg;
|
||||
uint32_t pid = tr->Pid;
|
||||
uint32_t tid = SysGetCurrentTid();
|
||||
|
||||
// Create the join slot from inside the new thread. KeCreateThread makes the
|
||||
// thread runnable before the creator regains control, so the old design
|
||||
// raced on tr->Tid and could observe zero/stale memory.
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||
if (!slot) slot = PthreadSlotAlloc(pid, tid);
|
||||
|
||||
SysFree(tr);
|
||||
|
||||
void* retval = fn(arg);
|
||||
|
||||
// Store retval for pthread_join.
|
||||
if (slot) slot->RetVal = retval;
|
||||
|
||||
SysExitThread(0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// pthread API
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) int pthread_create(pthread_t* t, const pthread_attr_t* attr,
|
||||
void* (*fn)(void*), void* arg) {
|
||||
(void)attr;
|
||||
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
|
||||
PTHREAD_TRAMP* tr = (PTHREAD_TRAMP*)SysAlloc(sizeof(PTHREAD_TRAMP));
|
||||
if (!tr) return ENOMEM;
|
||||
tr->Fn = fn;
|
||||
tr->Arg = arg;
|
||||
tr->Pid = pid;
|
||||
|
||||
uint32_t tid = 0;
|
||||
uint32_t s = SysCreateThread((uint64_t)PthreadTrampoline,
|
||||
(uint64_t)tr, 1 /*PriorityNormal*/, &tid);
|
||||
if (s != 0) {
|
||||
SysFree(tr);
|
||||
return EAGAIN;
|
||||
}
|
||||
// The trampoline allocates the retval slot itself after it knows its real
|
||||
// TID, avoiding the creator/new-thread race.
|
||||
if (t) t->Tid = tid;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_join(pthread_t t, void** retval) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t s = SysWaitThread(t.Tid);
|
||||
// STATUS_NOT_FOUND (0x22) means already dead - treat as success
|
||||
if (s != 0 && s != 0x0022) return ESRCH;
|
||||
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, t.Tid);
|
||||
if (retval) *retval = slot ? slot->RetVal : (void*)0;
|
||||
if (slot) slot->Used = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) void pthread_exit(void* retval) {
|
||||
uint32_t pid = SysGetCurrentPid();
|
||||
uint32_t tid = SysGetCurrentTid();
|
||||
PTHREAD_SLOT* slot = PthreadSlotFind(pid, tid);
|
||||
if (slot) slot->RetVal = retval;
|
||||
SysExitThread(0);
|
||||
while (1); // unreachable
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_init(pthread_mutex_t* m, const pthread_mutexattr_t* attr) {
|
||||
(void)attr;
|
||||
uint32_t h;
|
||||
uint32_t s = SysMutexCreate(&h);
|
||||
if (s != 0) return ENOMEM;
|
||||
m->Handle = h;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_destroy(pthread_mutex_t* m) {
|
||||
SysCloseHandle(m->Handle);
|
||||
m->Handle = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_lock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysMutexAcquire(m->Handle);
|
||||
return s == 0 ? 0 : EINVAL;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_trylock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysMutexTryAcquire(m->Handle);
|
||||
return s == 0 ? 0 : EAGAIN;
|
||||
}
|
||||
|
||||
__declspec(dllexport) int pthread_mutex_unlock(pthread_mutex_t* m) {
|
||||
uint32_t s = SysMutexRelease(m->Handle);
|
||||
return s == 0 ? 0 : EINVAL;
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
// gdi32.c
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#if defined(__SSE2__) || defined(_M_X64) || defined(_M_IX86)
|
||||
#include <emmintrin.h>
|
||||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Win32 Base Types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef void* HDC;
|
||||
typedef unsigned long DWORD;
|
||||
typedef unsigned short WORD;
|
||||
typedef unsigned int UINT;
|
||||
typedef long LONG;
|
||||
typedef int BOOL;
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
// StretchDIBits Constants
|
||||
#define BI_RGB 0L
|
||||
#define DIB_RGB_COLORS 0
|
||||
#define SRCCOPY (DWORD)0x00CC0020
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GDI Structs
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef struct tagBITMAPINFOHEADER {
|
||||
DWORD biSize;
|
||||
LONG biWidth;
|
||||
LONG biHeight;
|
||||
WORD biPlanes;
|
||||
WORD biBitCount;
|
||||
DWORD biCompression;
|
||||
DWORD biSizeImage;
|
||||
LONG biXPelsPerMeter;
|
||||
LONG biYPelsPerMeter;
|
||||
DWORD biClrUsed;
|
||||
DWORD biClrImportant;
|
||||
} BITMAPINFOHEADER;
|
||||
|
||||
typedef struct tagRGBQUAD {
|
||||
unsigned char rgbBlue;
|
||||
unsigned char rgbGreen;
|
||||
unsigned char rgbRed;
|
||||
unsigned char rgbReserved;
|
||||
} RGBQUAD;
|
||||
|
||||
typedef struct tagBITMAPINFO {
|
||||
BITMAPINFOHEADER bmiHeader;
|
||||
RGBQUAD bmiColors[1];
|
||||
} BITMAPINFO;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// GDI Exports
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Static buffers to persist between function calls
|
||||
static uint32_t* destBuffer = NULL;
|
||||
static int* srcX_map = NULL;
|
||||
static int currentDestCapacity = 0;
|
||||
static int currentXMapCapacity = 0;
|
||||
|
||||
__declspec(dllexport) int StretchDIBits(
|
||||
HDC hdc,
|
||||
int xDest, int yDest, int DestWidth, int DestHeight,
|
||||
int xSrc, int ySrc, int SrcWidth, int SrcHeight,
|
||||
const void *lpBits, const BITMAPINFO *lpbmi,
|
||||
UINT iUsage, DWORD rop)
|
||||
{
|
||||
(void)hdc; (void)lpbmi; (void)iUsage; (void)rop;
|
||||
|
||||
// Safety check to prevent division by zero
|
||||
if (DestWidth <= 0 || DestHeight <= 0 || SrcWidth <= 0 || SrcHeight <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Handle trivial case (1:1 copy) to bypass allocation and scaling overhead
|
||||
if (DestWidth == SrcWidth && DestHeight == SrcHeight && xSrc == 0 && ySrc == 0) {
|
||||
SysScreenBlit(xDest, yDest, DestWidth, DestHeight, (const uint32_t*)lpBits);
|
||||
return DestHeight;
|
||||
}
|
||||
|
||||
const uint32_t* srcPixels = (const uint32_t*)lpBits;
|
||||
|
||||
// Only reallocate if the new size is LARGER than what we already have
|
||||
int requiredPixels = DestWidth * DestHeight;
|
||||
if (requiredPixels > currentDestCapacity) {
|
||||
SysFree(destBuffer);
|
||||
destBuffer = (uint32_t*)SysAlloc(requiredPixels * sizeof(uint32_t));
|
||||
currentDestCapacity = requiredPixels;
|
||||
}
|
||||
|
||||
if (DestWidth > currentXMapCapacity) {
|
||||
SysFree(srcX_map);
|
||||
srcX_map = (int*)SysAlloc(DestWidth * sizeof(int));
|
||||
currentXMapCapacity = DestWidth;
|
||||
}
|
||||
|
||||
for (int dx = 0; dx < DestWidth; ++dx) {
|
||||
// Nearest neighbor coordinate mapping
|
||||
srcX_map[dx] = xSrc + (dx * SrcWidth) / DestWidth;
|
||||
|
||||
// Clamp to safe bounds
|
||||
if (srcX_map[dx] < 0) srcX_map[dx] = 0;
|
||||
if (srcX_map[dx] >= SrcWidth) srcX_map[dx] = SrcWidth - 1;
|
||||
}
|
||||
|
||||
// Process the image row by row
|
||||
for (int dy = 0; dy < DestHeight; ++dy) {
|
||||
// Map destination Y to source Y
|
||||
int sy = ySrc + (dy * SrcHeight) / DestHeight;
|
||||
if (sy < 0) sy = 0;
|
||||
if (sy >= SrcHeight) sy = SrcHeight - 1;
|
||||
|
||||
// Assumes stride == SrcWidth (tightly packed 32bpp)
|
||||
const uint32_t* srcRow = srcPixels + (sy * SrcWidth);
|
||||
uint32_t* dstRow = destBuffer + (dy * DestWidth);
|
||||
|
||||
int dx = 0;
|
||||
|
||||
#if defined(__SSE2__) || defined(_M_X64)
|
||||
// Process 4 pixels (16 bytes) per loop iteration using SSE2
|
||||
for (; dx <= DestWidth - 4; dx += 4) {
|
||||
// Gather 4 source pixels using our precomputed X map.
|
||||
// Note: _mm_set_epi32 takes arguments in reverse order (3, 2, 1, 0)
|
||||
__m128i pixels = _mm_set_epi32(
|
||||
srcRow[srcX_map[dx + 3]],
|
||||
srcRow[srcX_map[dx + 2]],
|
||||
srcRow[srcX_map[dx + 1]],
|
||||
srcRow[srcX_map[dx + 0]]
|
||||
);
|
||||
// Issue a single 128-bit unaligned store
|
||||
_mm_storeu_si128((__m128i*)&dstRow[dx], pixels);
|
||||
}
|
||||
#elif defined(__ARM_NEON) || defined(__ARM_NEON__) || defined(_M_ARM64)
|
||||
// Process 4 pixels per loop iteration using ARM NEON
|
||||
for (; dx <= DestWidth - 4; dx += 4) {
|
||||
// Initialize a vector and populate lanes (avoids stack-memory hop)
|
||||
uint32x4_t pixels = vdupq_n_u32(0);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 0]], pixels, 0);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 1]], pixels, 1);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 2]], pixels, 2);
|
||||
pixels = vsetq_lane_u32(srcRow[srcX_map[dx + 3]], pixels, 3);
|
||||
|
||||
// Issue a single 128-bit store
|
||||
vst1q_u32(&dstRow[dx], pixels);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Epilogue: Handle remaining pixels if DestWidth isn't a multiple of 4
|
||||
for (; dx < DestWidth; ++dx) {
|
||||
dstRow[dx] = srcRow[srcX_map[dx]];
|
||||
}
|
||||
}
|
||||
|
||||
// Direct mapping to kernel screen blit using the stretched buffer
|
||||
SysScreenBlit(xDest, yDest, DestWidth, DestHeight, destBuffer);
|
||||
|
||||
return DestHeight;
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,80 @@
|
||||
// ole32.c - Win32 COM / OLE stubs
|
||||
// No COM infrastructure at this stage; all functions fail gracefully.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef long HRESULT;
|
||||
typedef void* IUnknown;
|
||||
|
||||
#define S_OK ((HRESULT)0x00000000L)
|
||||
#define E_NOTIMPL ((HRESULT)0x80004001L)
|
||||
#define E_OUTOFMEMORY ((HRESULT)0x8007000EL)
|
||||
#define COINIT_MULTITHREADED 0x0
|
||||
|
||||
// GUID - 16-byte COM interface identifier.
|
||||
typedef struct {
|
||||
unsigned int Data1;
|
||||
unsigned short Data2;
|
||||
unsigned short Data3;
|
||||
unsigned char Data4[8];
|
||||
} GUID;
|
||||
|
||||
// PROPVARIANT - variant type used by shell / media APIs.
|
||||
typedef struct {
|
||||
unsigned short vt;
|
||||
unsigned short wReserved1, wReserved2, wReserved3;
|
||||
union {
|
||||
long long hVal;
|
||||
double dblVal;
|
||||
void* punkVal;
|
||||
unsigned char bVal;
|
||||
unsigned int ulVal;
|
||||
// ... (only common fields; full union is ~16 bytes)
|
||||
unsigned char padding[16];
|
||||
};
|
||||
} PROPVARIANT;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// COM initialisation
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) HRESULT CoInitializeEx(void* reserved, unsigned long coInit) {
|
||||
(void)reserved; (void)coInit;
|
||||
return S_OK; // Pretend success; no actual apartment is created.
|
||||
}
|
||||
|
||||
__declspec(dllexport) void CoUninitialize(void) {
|
||||
// No-op.
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Object creation
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) HRESULT CoCreateInstance(const GUID* clsid, IUnknown* outer,
|
||||
unsigned long ctx, const GUID* iid,
|
||||
void** ppv) {
|
||||
(void)clsid; (void)outer; (void)ctx; (void)iid;
|
||||
if (ppv) *ppv = (void*)0;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Memory
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) void CoTaskMemFree(void* pv) {
|
||||
if (pv) SysFree(pv);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PROPVARIANT
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) HRESULT PropVariantClear(PROPVARIANT* pvar) {
|
||||
if (!pvar) return E_NOTIMPL;
|
||||
// Zero the entire structure; no deep release needed for stub types.
|
||||
unsigned char* p = (unsigned char*)pvar;
|
||||
for (size_t i = 0; i < sizeof(*pvar); i++) p[i] = 0;
|
||||
return S_OK;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// user32.c - Win32 User Interface
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
typedef void* HWND;
|
||||
typedef void* HDC;
|
||||
typedef long LONG;
|
||||
typedef int BOOL;
|
||||
|
||||
typedef struct _RECT { LONG left, top, right, bottom; } RECT;
|
||||
typedef struct tagPAINTSTRUCT {
|
||||
HDC hdc; BOOL fErase; RECT rcPaint; BOOL fRestore; BOOL fIncUpdate; char res[32];
|
||||
} PAINTSTRUCT;
|
||||
|
||||
|
||||
static void User32DebugPrintA(const char* s) {
|
||||
if (!s) return;
|
||||
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysDebugPrint(&req);
|
||||
}
|
||||
|
||||
// --- Painting Functions ---
|
||||
__declspec(dllexport) HDC BeginPaint(HWND hwnd, PAINTSTRUCT* lpPaint) {
|
||||
(void)hwnd;
|
||||
if (lpPaint) {
|
||||
lpPaint->hdc = (HDC)1;
|
||||
lpPaint->rcPaint.right = 1920; lpPaint->rcPaint.bottom = 1080;
|
||||
}
|
||||
return (HDC)1;
|
||||
}
|
||||
|
||||
__declspec(dllexport) BOOL EndPaint(HWND hwnd, const PAINTSTRUCT* lp) { (void)hwnd; (void)lp; return 1; }
|
||||
|
||||
// --- Message Loop Stubs ---
|
||||
__declspec(dllexport) int GetMessage(void* m, HWND h, uint32_t f, uint32_t l) {
|
||||
(void)m; (void)h; (void)f; (void)l; SysYield(); return 0;
|
||||
}
|
||||
__declspec(dllexport) int PeekMessage(void* m, HWND h, uint32_t f, uint32_t l, uint32_t r) {
|
||||
(void)m; (void)h; (void)f; (void)l; (void)r;
|
||||
return 0;
|
||||
}
|
||||
__declspec(dllexport) int DispatchMessage(const void* m) { (void)m; return 0; }
|
||||
__declspec(dllexport) int TranslateMessage(const void* m) { (void)m; return 0; }
|
||||
|
||||
// --- Dialogs & UI ---
|
||||
__declspec(dllexport) int MessageBoxA(HWND h, const char* txt, const char* cap, uint32_t type) {
|
||||
(void)h; (void)cap; (void)type;
|
||||
User32DebugPrintA("USER32: MessageBoxA: ");
|
||||
User32DebugPrintA(txt);
|
||||
User32DebugPrintA("\n");
|
||||
return 1; // IDOK
|
||||
}
|
||||
|
||||
// --- Window Queries ---
|
||||
__declspec(dllexport) HWND GetDesktopWindow(void) { return (HWND)0; }
|
||||
__declspec(dllexport) HWND GetActiveWindow(void) { return (HWND)0; }
|
||||
__declspec(dllexport) HWND GetTopWindow(HWND hwnd){ (void)hwnd; return (HWND)0; }
|
||||
|
||||
__declspec(dllexport) HWND GetWindow(HWND hwnd, unsigned int cmd) {
|
||||
(void)hwnd; (void)cmd; return (HWND)0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) long GetWindowLongA(HWND hwnd, int index) {
|
||||
(void)hwnd; (void)index; return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) intptr_t GetWindowLongPtrA(HWND hwnd, int index) {
|
||||
(void)hwnd; (void)index; return 0;
|
||||
}
|
||||
|
||||
__declspec(dllexport) unsigned long GetWindowThreadProcessId(HWND hwnd, unsigned long* pid) {
|
||||
(void)hwnd;
|
||||
if (pid) *pid = 1;
|
||||
return 1; // Thread ID
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
// winmm.c - Win32 multimedia: waveform audio, timer
|
||||
// Stubs only; no audio hardware access at this stage.
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
typedef unsigned int MMRESULT;
|
||||
typedef void* HWAVEOUT;
|
||||
|
||||
#define MMSYSERR_NOERROR 0
|
||||
#define MMSYSERR_NOTSUPPORTED 8
|
||||
#define WAVERR_STILLPLAYING 33
|
||||
|
||||
// WAVEFORMATEX - standard PCM audio format descriptor.
|
||||
typedef struct {
|
||||
unsigned short wFormatTag;
|
||||
unsigned short nChannels;
|
||||
unsigned int nSamplesPerSec;
|
||||
unsigned int nAvgBytesPerSec;
|
||||
unsigned short nBlockAlign;
|
||||
unsigned short wBitsPerSample;
|
||||
unsigned short cbSize;
|
||||
} WAVEFORMATEX;
|
||||
|
||||
// WAVEHDR - buffer header submitted to waveOutWrite.
|
||||
typedef struct _WAVEHDR {
|
||||
char* lpData;
|
||||
unsigned int dwBufferLength;
|
||||
unsigned int dwBytesRecorded;
|
||||
uintptr_t dwUser;
|
||||
unsigned int dwFlags;
|
||||
unsigned int dwLoops;
|
||||
struct _WAVEHDR* lpNext;
|
||||
uintptr_t reserved;
|
||||
} WAVEHDR;
|
||||
|
||||
// MMTIME - used by waveOutGetPosition.
|
||||
typedef struct {
|
||||
unsigned int wType;
|
||||
union {
|
||||
unsigned int ms;
|
||||
unsigned int sample;
|
||||
unsigned int cb;
|
||||
unsigned int ticks;
|
||||
} u;
|
||||
} MMTIME;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) unsigned long timeGetTime(void) {
|
||||
return (unsigned long)(SysGetTick() / 1000u);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Waveform audio (stubs - no audio hardware at this stage)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutOpen(HWAVEOUT* phwo, unsigned int deviceId,
|
||||
const WAVEFORMATEX* fmt, uintptr_t callback,
|
||||
uintptr_t callbackInstance, unsigned long flags) {
|
||||
(void)deviceId; (void)fmt; (void)callback; (void)callbackInstance; (void)flags;
|
||||
if (phwo) *phwo = (void*)0;
|
||||
return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutClose(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutPrepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutUnprepareHeader(HWAVEOUT hwo, WAVEHDR* hdr,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutWrite(HWAVEOUT hwo, WAVEHDR* hdr, unsigned int size) {
|
||||
(void)hwo; (void)hdr; (void)size; return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutPause(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutRestart(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutReset(HWAVEOUT hwo) {
|
||||
(void)hwo; return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutSetVolume(HWAVEOUT hwo, unsigned long volume) {
|
||||
(void)hwo; (void)volume; return MMSYSERR_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT waveOutGetPosition(HWAVEOUT hwo, MMTIME* mmt,
|
||||
unsigned int size) {
|
||||
(void)hwo; (void)size;
|
||||
if (mmt) { mmt->wType = 0; mmt->u.ms = 0; }
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Timer resolution control
|
||||
// No hardware timer reprogramming at this stage; pretend success so callers
|
||||
// that wrap their audio/game loop in timeBeginPeriod(1) don't bail out.
|
||||
// ---------------------------------------------------------------------------
|
||||
__declspec(dllexport) MMRESULT timeBeginPeriod(unsigned int uPeriod) {
|
||||
(void)uPeriod;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
|
||||
__declspec(dllexport) MMRESULT timeEndPeriod(unsigned int uPeriod) {
|
||||
(void)uPeriod;
|
||||
return MMSYSERR_NOERROR;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
; syscall_nr.inc
|
||||
|
||||
SYS_EXIT_PROCESS EQU 0
|
||||
SYS_EXIT_THREAD EQU 1
|
||||
SYS_CREATE_PROCESS EQU 2
|
||||
SYS_WAIT_FOR_PROCESS EQU 3
|
||||
SYS_GET_PROCESS_EXIT_CODE EQU 4
|
||||
SYS_KILL_PROCESS EQU 5
|
||||
SYS_GET_PROCESS_LIST EQU 6
|
||||
SYS_GET_PROCESS_PID EQU 7
|
||||
SYS_CREATE_THREAD EQU 8
|
||||
SYS_WAIT_THREAD EQU 9
|
||||
SYS_GET_CURRENT_TID EQU 10
|
||||
SYS_GET_CURRENT_PID EQU 11
|
||||
SYS_SET_THREAD_PRIORITY EQU 12
|
||||
SYS_YIELD EQU 13
|
||||
SYS_SLEEP EQU 14
|
||||
SYS_SET_USER_APC_DISPATCHER EQU 15
|
||||
SYS_APC_RETURN EQU 16
|
||||
SYS_ALLOC EQU 17
|
||||
SYS_FREE EQU 18
|
||||
SYS_REALLOC EQU 19
|
||||
SYS_CLOSE_HANDLE EQU 20
|
||||
SYS_MUTEX_CREATE EQU 21
|
||||
SYS_MUTEX_ACQUIRE EQU 22
|
||||
SYS_MUTEX_TRY_ACQUIRE EQU 23
|
||||
SYS_MUTEX_RELEASE EQU 24
|
||||
SYS_SEM_CREATE EQU 25
|
||||
SYS_SEM_WAIT EQU 26
|
||||
SYS_SEM_TRY_WAIT EQU 27
|
||||
SYS_SEM_SIGNAL EQU 28
|
||||
SYS_EVENT_CREATE EQU 29
|
||||
SYS_EVENT_SIGNAL EQU 30
|
||||
SYS_EVENT_CLEAR EQU 31
|
||||
SYS_EVENT_WAIT EQU 32
|
||||
SYS_QUEUE_CREATE EQU 33
|
||||
SYS_QUEUE_SEND EQU 34
|
||||
SYS_QUEUE_RECV EQU 35
|
||||
SYS_PUBLISH_QUEUE EQU 36
|
||||
SYS_OPEN_NAMED_QUEUE EQU 37
|
||||
SYS_QUEUE_DUPLICATE EQU 38
|
||||
SYS_NAMED_QUEUE_OWNER_PID EQU 39
|
||||
SYS_OPEN_FILE EQU 40
|
||||
SYS_CREATE_FILE EQU 41
|
||||
SYS_READ_FILE EQU 42
|
||||
SYS_WRITE_FILE EQU 43
|
||||
SYS_READ_FILE_ASYNC EQU 44
|
||||
SYS_WRITE_FILE_ASYNC EQU 45
|
||||
SYS_CANCEL_IO EQU 46
|
||||
SYS_SEEK_FILE EQU 47
|
||||
SYS_GET_FILE_POS EQU 48
|
||||
SYS_GET_FILE_SIZE EQU 49
|
||||
SYS_TRUNCATE_FILE EQU 50
|
||||
SYS_DELETE_FILE EQU 51
|
||||
SYS_RENAME_FILE EQU 52
|
||||
SYS_OPEN_DIR EQU 53
|
||||
SYS_READ_DIR EQU 54
|
||||
SYS_CREATE_DIR EQU 55
|
||||
SYS_REMOVE_DIR EQU 56
|
||||
SYS_SET_CWD EQU 57
|
||||
SYS_GET_CWD EQU 58
|
||||
SYS_WRITE_CONSOLE EQU 59
|
||||
SYS_SCREEN_BLIT EQU 60
|
||||
SYS_SCREEN_CLEAR EQU 61
|
||||
SYS_SCREEN_GET_SIZE EQU 62
|
||||
SYS_SCREEN_PUT_CHAR EQU 63
|
||||
SYS_KB_POLL EQU 64
|
||||
SYS_KB_IS_KEY_DOWN EQU 65
|
||||
SYS_KB_READ_CHAR EQU 66
|
||||
SYS_KB_READ_SCANCODE EQU 67
|
||||
SYS_KB_GET_MODIFIERS EQU 68
|
||||
SYS_MOUSE_POLL EQU 69
|
||||
SYS_MOUSE_READ EQU 70
|
||||
SYS_MOUSE_GET_POS EQU 71
|
||||
SYS_GET_TICK EQU 72
|
||||
SYS_DEBUG_PRINT EQU 73
|
||||
SYS_QUERY_TRAP_FRAME EQU 74
|
||||
SYS_SOCKET EQU 75
|
||||
SYS_BIND EQU 76
|
||||
SYS_LISTEN EQU 77
|
||||
SYS_ACCEPT EQU 78
|
||||
SYS_CONNECT EQU 79
|
||||
SYS_SEND EQU 80
|
||||
SYS_RECV EQU 81
|
||||
SYS_SHUTDOWN EQU 82
|
||||
SYS_SEND_TO EQU 83
|
||||
SYS_RECV_FROM EQU 84
|
||||
SYS_RESOLVE_HOST EQU 85
|
||||
SYS_SET_SOCKET_OPTION EQU 86
|
||||
SYS_GET_SOCKET_OPTION EQU 87
|
||||
SYS_POLL EQU 88
|
||||
SYS_GET_MEMORY_STATUS EQU 89
|
||||
|
||||
SYS_MAX EQU 90
|
||||
@@ -0,0 +1,220 @@
|
||||
; syscall_stubs.asm - user-mode INT 0x40 stubs exported by syscall.dll (MASM64)
|
||||
;
|
||||
; Calling convention (kernel side):
|
||||
; RAX = syscall number
|
||||
; RCX = arg 0
|
||||
; RDX = arg 1
|
||||
; R8 = arg 2
|
||||
; R9 = arg 3
|
||||
; R10 = arg 4 (our extension - loaded from the stack by SYSCALL_STUB6)
|
||||
; R11 = arg 5 (our extension - loaded from the stack by SYSCALL_STUB6)
|
||||
;
|
||||
; User-mode callers follow standard MS x64: args 1-4 in RCX/RDX/R8/R9,
|
||||
; args 5+ on the stack above the home area ([rsp+28h], [rsp+30h], ...).
|
||||
;
|
||||
; SYSCALL_STUB2 - 4 or fewer args: pure register passing, no stack read.
|
||||
; SYSCALL_STUB6 - 5 or 6 args: loads [rsp+28h] -> R10 and [rsp+30h] -> R11.
|
||||
;
|
||||
; Handle lifecycle:
|
||||
; SysCloseHandle is the single close stub for every object type.
|
||||
; There is no SysCloseFile - files are closed through the unified path,
|
||||
; exactly like mutexes, semaphores, events, and queues.
|
||||
|
||||
INCLUDE syscall_nr.inc
|
||||
|
||||
SYS_USER_APC_FRAME_Magic EQU 0
|
||||
SYS_USER_APC_FRAME_OriginalRip EQU 8
|
||||
SYS_USER_APC_FRAME_OriginalRsp EQU 16
|
||||
SYS_USER_APC_FRAME_OriginalRflags EQU 24
|
||||
SYS_USER_APC_FRAME_OriginalRax EQU 32
|
||||
SYS_USER_APC_FRAME_Routine EQU 40
|
||||
SYS_USER_APC_FRAME_NormalContext EQU 48
|
||||
SYS_USER_APC_FRAME_SystemArgument1 EQU 56
|
||||
SYS_USER_APC_FRAME_SystemArgument2 EQU 64
|
||||
|
||||
.CODE
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB6 MACRO name, nr
|
||||
PUBLIC Sys&name
|
||||
Sys&name PROC EXPORT
|
||||
mov r10, [rsp+28h] ; 5th argument
|
||||
mov r11, [rsp+30h] ; 6th argument
|
||||
mov rax, nr
|
||||
int 40h
|
||||
ret
|
||||
Sys&name ENDP
|
||||
ENDM
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Process & thread lifecycle
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 ExitProcess, SYS_EXIT_PROCESS
|
||||
SYSCALL_STUB2 ExitThread, SYS_EXIT_THREAD
|
||||
SYSCALL_STUB2 CreateProcess, SYS_CREATE_PROCESS
|
||||
SYSCALL_STUB2 WaitForProcess, SYS_WAIT_FOR_PROCESS
|
||||
SYSCALL_STUB2 GetProcessExitCode, SYS_GET_PROCESS_EXIT_CODE
|
||||
SYSCALL_STUB2 KillProcess, SYS_KILL_PROCESS
|
||||
SYSCALL_STUB2 GetProcessList, SYS_GET_PROCESS_LIST
|
||||
SYSCALL_STUB2 GetProcessPid, SYS_GET_PROCESS_PID
|
||||
|
||||
SYSCALL_STUB2 CreateThread, SYS_CREATE_THREAD
|
||||
SYSCALL_STUB2 WaitThread, SYS_WAIT_THREAD
|
||||
SYSCALL_STUB2 GetCurrentTid, SYS_GET_CURRENT_TID
|
||||
SYSCALL_STUB2 GetCurrentPid, SYS_GET_CURRENT_PID
|
||||
SYSCALL_STUB2 SetThreadPriority, SYS_SET_THREAD_PRIORITY
|
||||
SYSCALL_STUB2 Yield, SYS_YIELD
|
||||
SYSCALL_STUB2 Sleep, SYS_SLEEP
|
||||
SYSCALL_STUB2 SetUserApcDispatcher, SYS_SET_USER_APC_DISPATCHER
|
||||
SYSCALL_STUB2 ApcReturn, SYS_APC_RETURN
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; User APC dispatcher
|
||||
; ---------------------------------------------------------------------------
|
||||
PUBLIC SysUserApcDispatcher
|
||||
SysUserApcDispatcher PROC EXPORT
|
||||
; Kernel enters here with RSP pointing at SYS_USER_APC_FRAME. There is no
|
||||
; return address because this is an IRET redirection. Preserve RBX because
|
||||
; the original user context resumes after SysApcReturn.
|
||||
push rbx
|
||||
lea rbx, [rsp + 8] ; original injected RSP = frame pointer
|
||||
|
||||
sub rsp, 32 ; shadow space; RSP is already call-aligned
|
||||
mov rcx, [rbx + SYS_USER_APC_FRAME_NormalContext]
|
||||
mov rdx, [rbx + SYS_USER_APC_FRAME_SystemArgument1]
|
||||
mov r8, [rbx + SYS_USER_APC_FRAME_SystemArgument2]
|
||||
mov rax, [rbx + SYS_USER_APC_FRAME_Routine]
|
||||
call rax
|
||||
add rsp, 32
|
||||
|
||||
mov rcx, rbx ; SysApcReturn(frame)
|
||||
mov rbx, [rsp] ; restore original user RBX
|
||||
add rsp, 8
|
||||
mov rax, SYS_APC_RETURN
|
||||
int 40h
|
||||
|
||||
SysUserApcDispatcher_Failed:
|
||||
jmp SysUserApcDispatcher_Failed
|
||||
SysUserApcDispatcher ENDP
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Memory and handles
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 Alloc, SYS_ALLOC
|
||||
SYSCALL_STUB2 Free, SYS_FREE
|
||||
SYSCALL_STUB2 Realloc, SYS_REALLOC
|
||||
SYSCALL_STUB2 CloseHandle, SYS_CLOSE_HANDLE
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Synchronization objects
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 MutexCreate, SYS_MUTEX_CREATE
|
||||
SYSCALL_STUB2 MutexAcquire, SYS_MUTEX_ACQUIRE
|
||||
SYSCALL_STUB2 MutexTryAcquire, SYS_MUTEX_TRY_ACQUIRE
|
||||
SYSCALL_STUB2 MutexRelease, SYS_MUTEX_RELEASE
|
||||
|
||||
SYSCALL_STUB2 SemCreate, SYS_SEM_CREATE
|
||||
SYSCALL_STUB2 SemWait, SYS_SEM_WAIT
|
||||
SYSCALL_STUB2 SemTryWait, SYS_SEM_TRY_WAIT
|
||||
SYSCALL_STUB2 SemSignal, SYS_SEM_SIGNAL
|
||||
|
||||
SYSCALL_STUB2 EventCreate, SYS_EVENT_CREATE
|
||||
SYSCALL_STUB2 EventSignal, SYS_EVENT_SIGNAL
|
||||
SYSCALL_STUB2 EventClear, SYS_EVENT_CLEAR
|
||||
SYSCALL_STUB2 EventWait, SYS_EVENT_WAIT
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Queues and named queues
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 QueueCreate, SYS_QUEUE_CREATE
|
||||
SYSCALL_STUB2 QueueSend, SYS_QUEUE_SEND
|
||||
SYSCALL_STUB2 QueueRecv, SYS_QUEUE_RECV
|
||||
SYSCALL_STUB2 PublishQueue, SYS_PUBLISH_QUEUE
|
||||
SYSCALL_STUB2 OpenNamedQueue, SYS_OPEN_NAMED_QUEUE
|
||||
SYSCALL_STUB2 QueueDuplicate, SYS_QUEUE_DUPLICATE
|
||||
SYSCALL_STUB2 NamedQueueOwnerPid, SYS_NAMED_QUEUE_OWNER_PID
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Files, directories, and current directory
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 OpenFile, SYS_OPEN_FILE
|
||||
SYSCALL_STUB2 CreateFile, SYS_CREATE_FILE
|
||||
SYSCALL_STUB2 ReadFile, SYS_READ_FILE
|
||||
SYSCALL_STUB2 WriteFile, SYS_WRITE_FILE
|
||||
SYSCALL_STUB2 ReadFileAsync, SYS_READ_FILE_ASYNC
|
||||
SYSCALL_STUB2 WriteFileAsync, SYS_WRITE_FILE_ASYNC
|
||||
SYSCALL_STUB2 CancelIo, SYS_CANCEL_IO
|
||||
SYSCALL_STUB2 SeekFile, SYS_SEEK_FILE
|
||||
SYSCALL_STUB2 GetFilePos, SYS_GET_FILE_POS
|
||||
SYSCALL_STUB2 GetFileSize, SYS_GET_FILE_SIZE
|
||||
SYSCALL_STUB2 TruncateFile, SYS_TRUNCATE_FILE
|
||||
SYSCALL_STUB2 DeleteFile, SYS_DELETE_FILE
|
||||
SYSCALL_STUB2 RenameFile, SYS_RENAME_FILE
|
||||
|
||||
SYSCALL_STUB2 OpenDir, SYS_OPEN_DIR
|
||||
SYSCALL_STUB2 ReadDir, SYS_READ_DIR
|
||||
SYSCALL_STUB2 CreateDir, SYS_CREATE_DIR
|
||||
SYSCALL_STUB2 RemoveDir, SYS_REMOVE_DIR
|
||||
SYSCALL_STUB2 SetCwd, SYS_SET_CWD
|
||||
SYSCALL_STUB2 GetCwd, SYS_GET_CWD
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Console and graphics
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 WriteConsole, SYS_WRITE_CONSOLE
|
||||
SYSCALL_STUB6 ScreenBlit, SYS_SCREEN_BLIT
|
||||
SYSCALL_STUB2 ScreenClear, SYS_SCREEN_CLEAR
|
||||
SYSCALL_STUB2 ScreenGetSize, SYS_SCREEN_GET_SIZE
|
||||
SYSCALL_STUB6 ScreenPutChar, SYS_SCREEN_PUT_CHAR
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Input
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 KbPoll, SYS_KB_POLL
|
||||
SYSCALL_STUB2 KbIsKeyDown, SYS_KB_IS_KEY_DOWN
|
||||
SYSCALL_STUB2 KbReadChar, SYS_KB_READ_CHAR
|
||||
SYSCALL_STUB2 KbReadScancode, SYS_KB_READ_SCANCODE
|
||||
SYSCALL_STUB2 KbGetModifiers, SYS_KB_GET_MODIFIERS
|
||||
|
||||
SYSCALL_STUB2 MousePoll, SYS_MOUSE_POLL
|
||||
SYSCALL_STUB2 MouseRead, SYS_MOUSE_READ
|
||||
SYSCALL_STUB2 MouseGetPos, SYS_MOUSE_GET_POS
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Time and debug
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 GetTick, SYS_GET_TICK
|
||||
SYSCALL_STUB2 DebugPrint, SYS_DEBUG_PRINT
|
||||
SYSCALL_STUB2 QueryTrapFrame, SYS_QUERY_TRAP_FRAME
|
||||
|
||||
; ---------------------------------------------------------------------------
|
||||
; Native sockets
|
||||
; ---------------------------------------------------------------------------
|
||||
SYSCALL_STUB2 Socket, SYS_SOCKET
|
||||
SYSCALL_STUB2 Bind, SYS_BIND
|
||||
SYSCALL_STUB2 Listen, SYS_LISTEN
|
||||
SYSCALL_STUB2 Accept, SYS_ACCEPT
|
||||
SYSCALL_STUB2 Connect, SYS_CONNECT
|
||||
SYSCALL_STUB6 Send, SYS_SEND
|
||||
SYSCALL_STUB6 Recv, SYS_RECV
|
||||
SYSCALL_STUB2 Shutdown, SYS_SHUTDOWN
|
||||
SYSCALL_STUB2 SendTo, SYS_SEND_TO
|
||||
SYSCALL_STUB2 RecvFrom, SYS_RECV_FROM
|
||||
SYSCALL_STUB2 ResolveHost, SYS_RESOLVE_HOST
|
||||
SYSCALL_STUB2 SetSocketOption, SYS_SET_SOCKET_OPTION
|
||||
SYSCALL_STUB2 GetSocketOption, SYS_GET_SOCKET_OPTION
|
||||
SYSCALL_STUB2 Poll, SYS_POLL
|
||||
|
||||
SYSCALL_STUB2 GetMemoryStatus, SYS_GET_MEMORY_STATUS
|
||||
|
||||
END
|
||||
@@ -0,0 +1,96 @@
|
||||
// init.c - first user-mode process
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define STATUS_SUCCESS 0u
|
||||
|
||||
void itoa(int num, char* str) {
|
||||
int i = 0;
|
||||
int is_negative = 0;
|
||||
|
||||
// Handle 0 explicitly
|
||||
if (num == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle negative numbers
|
||||
if (num < 0) {
|
||||
is_negative = 1;
|
||||
num = -num;
|
||||
}
|
||||
|
||||
// Process individual digits (result is in reverse)
|
||||
while (num != 0) {
|
||||
int rem = num % 10;
|
||||
str[i++] = rem + '0';
|
||||
num = num / 10;
|
||||
}
|
||||
|
||||
if (is_negative) {
|
||||
str[i++] = '-';
|
||||
}
|
||||
|
||||
str[i] = '\0'; // Null terminate
|
||||
|
||||
// Reverse the string
|
||||
int start = 0;
|
||||
int end = i - 1;
|
||||
while (start < end) {
|
||||
char temp = str[start];
|
||||
str[start] = str[end];
|
||||
str[end] = temp;
|
||||
start++;
|
||||
end--;
|
||||
}
|
||||
}
|
||||
|
||||
static void debug_print(const char* s) {
|
||||
SYS_DEBUG_PRINT_REQUEST req = { SysSvFromCstr(s) };
|
||||
(void)SysDebugPrint(&req);
|
||||
}
|
||||
|
||||
void _start(void)
|
||||
{
|
||||
SYS_STRING_VIEW args = SysGetPeb()->CommandLine;
|
||||
uint32_t h = 0;
|
||||
uint32_t s = 0;
|
||||
|
||||
while (true) {
|
||||
debug_print("[init] starting\n");
|
||||
SYS_CREATE_PROCESS_REQUEST cp = { args, SysSvFromParts(0, 0), &h };
|
||||
s = SysCreateProcess(&cp);
|
||||
if (s != STATUS_SUCCESS) {
|
||||
debug_print("[init] FATAL: failed to start!\n");
|
||||
char buffer[12];
|
||||
itoa(s, buffer);
|
||||
debug_print(buffer);
|
||||
debug_print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
debug_print("[init] waiting\n");
|
||||
s = SysWaitForProcess(h);
|
||||
if (s != STATUS_SUCCESS)
|
||||
debug_print("[init] WARNING: SysWaitForProcess returned error\n");
|
||||
|
||||
int exitCode = 0;
|
||||
if (SysGetProcessExitCode(h, &exitCode) != STATUS_SUCCESS) {
|
||||
debug_print("[init] WARNING: failed to read exit code\n");
|
||||
exitCode = 1;
|
||||
}
|
||||
|
||||
SysCloseHandle(h);
|
||||
|
||||
if (exitCode == 0)
|
||||
break;
|
||||
|
||||
debug_print("[init] child crashed, restarting\n");
|
||||
}
|
||||
|
||||
debug_print("[init] done\n");
|
||||
SysExitProcess(0);
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <intrin.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define EVENT_AUTO_RESET 0u
|
||||
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||
return SysOpenFile(&req);
|
||||
}
|
||||
|
||||
static void print_hex8(uint8_t v) {
|
||||
static const char h[] = "0123456789ABCDEF";
|
||||
char b[3];
|
||||
b[0] = h[(v >> 4) & 0xF];
|
||||
b[1] = h[v & 0xF];
|
||||
b[2] = 0;
|
||||
print(b);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t n) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
if (!n) { print("0"); return; }
|
||||
while (n && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (n % 10));
|
||||
n /= 10;
|
||||
}
|
||||
while (i) {
|
||||
char c[2] = { buf[--i], 0 };
|
||||
print(c);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct APC_TEST_STATE {
|
||||
volatile uint32_t Delivered;
|
||||
volatile uint32_t Status;
|
||||
volatile uint64_t Information;
|
||||
} APC_TEST_STATE;
|
||||
|
||||
static void file_apc(void* context, SYS_IO_STATUS_BLOCK* iosb, uint64_t information) {
|
||||
APC_TEST_STATE* st = (APC_TEST_STATE*)context;
|
||||
st->Status = iosb ? iosb->Status : 0xFFFFFFFFu;
|
||||
st->Information = information;
|
||||
st->Delivered++;
|
||||
do { SYS_DEBUG_PRINT_REQUEST req = { SYS_SV_LITERAL("async_apc: user APC callback ran\n") }; SysDebugPrint(&req); } while (0);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
print("async_apc: start\n");
|
||||
|
||||
uint32_t s = SysSetUserApcDispatcher((void*)SysUserApcDispatcher);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
print("async_apc: dispatcher registration failed status=");
|
||||
print_u64(s);
|
||||
print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
uint32_t file = 0;
|
||||
s = open_file("system/init.exe", &file);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
print("async_apc: open failed status=");
|
||||
print_u64(s);
|
||||
print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
uint32_t event = 0;
|
||||
s = SysEventCreate(EVENT_AUTO_RESET, 0, &event);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
print("async_apc: event create failed status=");
|
||||
print_u64(s);
|
||||
print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
uint8_t* buffer = (uint8_t*)SysAlloc(512);
|
||||
if (!buffer) {
|
||||
print("async_apc: allocation failed\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
memset(buffer, 0, 512);
|
||||
|
||||
SYS_IO_STATUS_BLOCK iosb;
|
||||
iosb.Status = 0;
|
||||
iosb.Information = 0;
|
||||
|
||||
APC_TEST_STATE state;
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
SYS_ASYNC_FILE_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.FileHandle = file;
|
||||
req.EventHandle = event;
|
||||
req.Offset = 0;
|
||||
req.Buffer = buffer;
|
||||
req.Size = 512;
|
||||
req.Iosb = &iosb;
|
||||
req.ApcRoutine = file_apc;
|
||||
req.ApcContext = &state;
|
||||
|
||||
s = SysReadFileAsync(&req);
|
||||
if (s != SYS_STATUS_PENDING) {
|
||||
print("async_apc: submit failed status=");
|
||||
print_u64(s);
|
||||
print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
while (!state.Delivered) {
|
||||
s = SysEventWait(event, 1);
|
||||
print("async_apc: alertable wait returned ");
|
||||
print_u64(s);
|
||||
print("\n");
|
||||
if (s != SYS_STATUS_USER_APC && s != SYS_STATUS_SUCCESS && s != SYS_STATUS_PENDING)
|
||||
break;
|
||||
}
|
||||
|
||||
print("async_apc: delivered=");
|
||||
print_u64(state.Delivered);
|
||||
print(" status=");
|
||||
print_u64(state.Status);
|
||||
print(" info=");
|
||||
print_u64(state.Information);
|
||||
print(" bytes=");
|
||||
print_hex8(buffer[0]); print(" ");
|
||||
print_hex8(buffer[1]); print(" ");
|
||||
print_hex8(buffer[2]); print(" ");
|
||||
print_hex8(buffer[3]); print("\n");
|
||||
|
||||
uint32_t ok = (state.Delivered == 1 && state.Status == SYS_STATUS_SUCCESS &&
|
||||
state.Information == 512 && buffer[0] == 'M' && buffer[1] == 'Z');
|
||||
|
||||
SysCloseHandle(event);
|
||||
SysCloseHandle(file);
|
||||
SysFree(buffer);
|
||||
|
||||
if (!ok) {
|
||||
print("async_apc: FAIL\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
print("async_apc: PASS\n");
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ntlite/syscall.h>
|
||||
|
||||
#define EVENT_AUTO_RESET 0u
|
||||
|
||||
static void print(const char* s) {
|
||||
SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromCstr(s) };
|
||||
SysWriteConsole(&req);
|
||||
}
|
||||
|
||||
static uint32_t open_file(const char* path, uint32_t* handleOut) {
|
||||
SYS_PATH_HANDLE_REQUEST req = { SysSvFromCstr(path), handleOut };
|
||||
return SysOpenFile(&req);
|
||||
}
|
||||
|
||||
static void print_u64(uint64_t v) {
|
||||
char buf[32];
|
||||
int i = 0;
|
||||
if (v == 0) {
|
||||
print("0");
|
||||
return;
|
||||
}
|
||||
while (v && i < (int)sizeof(buf)) {
|
||||
buf[i++] = (char)('0' + (v % 10));
|
||||
v /= 10;
|
||||
}
|
||||
while (i--) {
|
||||
char c = buf[i];
|
||||
do { SYS_WRITE_CONSOLE_REQUEST req = { SysSvFromParts(&c, 1) }; SysWriteConsole(&req); } while (0);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
print("async_cancel: start\n");
|
||||
|
||||
uint32_t file = 0;
|
||||
uint32_t event = 0;
|
||||
uint32_t request = 0;
|
||||
|
||||
uint32_t s = open_file("system/init.exe", &file);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
print("async_cancel: open failed status="); print_u64(s); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
s = SysEventCreate(EVENT_AUTO_RESET, 0, &event);
|
||||
if (s != SYS_STATUS_SUCCESS) {
|
||||
print("async_cancel: event create failed status="); print_u64(s); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
uint8_t* buffer = (uint8_t*)SysAlloc(512);
|
||||
if (!buffer) {
|
||||
print("async_cancel: alloc failed\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
memset(buffer, 0, 512);
|
||||
|
||||
SYS_IO_STATUS_BLOCK iosb;
|
||||
iosb.Status = 0;
|
||||
iosb.Information = 0;
|
||||
|
||||
SYS_ASYNC_FILE_REQUEST req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.FileHandle = file;
|
||||
req.EventHandle = event;
|
||||
req.Offset = 0;
|
||||
req.Buffer = buffer;
|
||||
req.Size = 512;
|
||||
req.Iosb = &iosb;
|
||||
req.RequestHandleOut = &request;
|
||||
|
||||
s = SysReadFileAsync(&req);
|
||||
if (s != SYS_STATUS_PENDING || request == 0) {
|
||||
print("async_cancel: submit failed status="); print_u64(s);
|
||||
print(" request="); print_u64(request); print("\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
uint32_t cs = SysCancelIo(request);
|
||||
print("async_cancel: cancel returned "); print_u64(cs); print("\n");
|
||||
|
||||
while (iosb.Status == SYS_STATUS_PENDING) {
|
||||
s = SysEventWait(event, 1);
|
||||
print("async_cancel: wait returned "); print_u64(s); print("\n");
|
||||
if (s != SYS_STATUS_SUCCESS && s != SYS_STATUS_KERNEL_APC && s != SYS_STATUS_USER_APC && s != SYS_STATUS_ALERTED)
|
||||
break;
|
||||
}
|
||||
|
||||
print("async_cancel: iosb status="); print_u64(iosb.Status);
|
||||
print(" info="); print_u64(iosb.Information); print("\n");
|
||||
|
||||
// If the worker had already started, cancellation is best-effort and the
|
||||
// request may complete successfully. If it was still queued, it must report
|
||||
// SYS_STATUS_CANCELLED. Both outcomes are valid; SYS_STATUS_PENDING is not.
|
||||
uint32_t ok = (iosb.Status == SYS_STATUS_CANCELLED) ||
|
||||
(iosb.Status == SYS_STATUS_SUCCESS && iosb.Information == 512);
|
||||
|
||||
SysCloseHandle(request);
|
||||
SysCloseHandle(event);
|
||||
SysCloseHandle(file);
|
||||
SysFree(buffer);
|
||||
|
||||
if (!ok) {
|
||||
print("async_cancel: FAIL\n");
|
||||
SysExitProcess(1);
|
||||
}
|
||||
|
||||
print("async_cancel: PASS\n");
|
||||
SysExitProcess(0);
|
||||
return 0;
|
||||
}
|
||||
Reference in new issue
Block a user