Files
nt-lite/code/user/apps/doom/doom.c
T

426 lines
13 KiB
C

// 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 <stdlib.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 = malloc(size);
if (!p) SysExitProcess(1);
return p;
}
static void doom_free_wrapper(void* ptr)
{
if (!ptr) return;
free(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*)malloc(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);
free(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;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(f->Handle, buf, (size_t)count, &read);
SysReadFile(&req);
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;
SYS_FILE_IO_REQUEST req = SysMakeFileIoRequest(f->Handle, buf, (size_t)count, &written);
SysWriteFile(&req);
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 canonical input keys to PureDOOM
// ---------------------------------------------------------------------------
typedef struct { INPUT_KEY inputKey; doom_key_t key; } KeyMap;
static const KeyMap g_keymap[] = {
// Letters
{ INPUT_KEY_W, DOOM_KEY_W },
{ INPUT_KEY_A, DOOM_KEY_A },
{ INPUT_KEY_S, DOOM_KEY_S },
{ INPUT_KEY_D, DOOM_KEY_D },
{ INPUT_KEY_E, DOOM_KEY_E },
{ INPUT_KEY_Q, DOOM_KEY_Q },
{ INPUT_KEY_Y, DOOM_KEY_Y },
{ INPUT_KEY_N, DOOM_KEY_N },
// Arrow keys
{ INPUT_KEY_UP, DOOM_KEY_UP_ARROW },
{ INPUT_KEY_DOWN, DOOM_KEY_DOWN_ARROW },
{ INPUT_KEY_LEFT, DOOM_KEY_LEFT_ARROW },
{ INPUT_KEY_RIGHT, DOOM_KEY_RIGHT_ARROW },
// Action keys
{ INPUT_KEY_LEFT_CTRL, DOOM_KEY_CTRL },
{ INPUT_KEY_SPACE, DOOM_KEY_SPACE },
{ INPUT_KEY_LEFT_SHIFT, DOOM_KEY_SHIFT },
{ INPUT_KEY_LEFT_ALT, DOOM_KEY_ALT },
// Navigation
{ INPUT_KEY_ESCAPE, DOOM_KEY_ESCAPE },
{ INPUT_KEY_ENTER, DOOM_KEY_ENTER },
{ INPUT_KEY_TAB, DOOM_KEY_TAB },
{ INPUT_KEY_BACKSPACE, DOOM_KEY_BACKSPACE },
// Weapon slots
{ INPUT_KEY_1, DOOM_KEY_1 },
{ INPUT_KEY_2, DOOM_KEY_2 },
{ INPUT_KEY_3, DOOM_KEY_3 },
{ INPUT_KEY_4, DOOM_KEY_4 },
{ INPUT_KEY_5, DOOM_KEY_5 },
{ INPUT_KEY_6, DOOM_KEY_6 },
{ INPUT_KEY_7, DOOM_KEY_7 },
// Misc
{ INPUT_KEY_MINUS, DOOM_KEY_MINUS },
{ INPUT_KEY_EQUAL, DOOM_KEY_EQUALS },
{ INPUT_KEY_COMMA, DOOM_KEY_COMMA },
{ INPUT_KEY_PERIOD, DOOM_KEY_PERIOD },
// Function keys
{ INPUT_KEY_F1, DOOM_KEY_F1 },
{ INPUT_KEY_F2, DOOM_KEY_F2 },
{ INPUT_KEY_F3, DOOM_KEY_F3 },
{ INPUT_KEY_F4, DOOM_KEY_F4 },
{ INPUT_KEY_F5, DOOM_KEY_F5 },
{ INPUT_KEY_F6, DOOM_KEY_F6 },
{ INPUT_KEY_F7, DOOM_KEY_F7 },
{ INPUT_KEY_F8, DOOM_KEY_F8 },
{ INPUT_KEY_F9, DOOM_KEY_F9 },
{ INPUT_KEY_F10, DOOM_KEY_F10 },
{ INPUT_KEY_F11, DOOM_KEY_F11 },
{ INPUT_KEY_F12, 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 = (SysIsKeyDown(g_keymap[i].inputKey) != 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 — writes directly into a mapped kernel surface and presents it.
// ---------------------------------------------------------------------------
#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_surface_handle;
static uint32_t* g_surface_pixels;
static uint32_t g_surface_pitch_pixels;
static void init_surface(void)
{
uint32_t s = SysCreateSurface(DISP_W, DISP_H, SYS_SURFACE_FORMAT_XRGB8888,
SYS_SURFACE_FLAG_NONE, &g_surface_handle);
if (s != SYS_STATUS_SUCCESS || !g_surface_handle) {
doom_print_wrapper("doom: SysCreateSurface failed\n");
SysExitProcess(1);
}
void* base = 0;
uint32_t pitchBytes = 0;
s = SysMapSurface(g_surface_handle, &base, &pitchBytes);
if (s != SYS_STATUS_SUCCESS || !base || pitchBytes < DISP_W * sizeof(uint32_t)) {
doom_print_wrapper("doom: SysMapSurface failed\n");
SysExitProcess(1);
}
g_surface_pixels = (uint32_t*)base;
g_surface_pitch_pixels = pitchBytes / sizeof(uint32_t);
}
static void render_frame(const uint8_t* rgba)
{
const uint32_t* src = (const uint32_t*)rgba;
uint32_t* dstBase = g_surface_pixels;
uint32_t dstPitch = g_surface_pitch_pixels;
if (!dstBase || dstPitch < DISP_W) {
doom_print_wrapper("doom: surface not initialized\n");
SysExitProcess(1);
}
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/XRGB8888 conversion for the current display path.
uint32_t bgra = (p & 0xFF00FF00u)
| ((p & 0x00FF0000u) >> 16)
| ((p & 0x000000FFu) << 16);
uint32_t* block = dstBase + (size_t)(y * SCALE) * dstPitch + (size_t)(x * SCALE);
for (int sy = 0; sy < SCALE; sy++) {
for (int sx = 0; sx < SCALE; sx++) {
block[(size_t)sy * dstPitch + sx] = bgra;
}
}
}
}
DISPLAY_RECT rect = { 0, 0, DISP_W, DISP_H };
uint32_t code = SysPresentSurface(g_surface_handle, &rect, 1);
if (code != SYS_STATUS_SUCCESS) {
SysExitProcess(code);
}
}
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);
init_surface();
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;
}