// 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 #include #include #include #include #include #include #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; 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 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 = (SysKbIsKeyDown(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 — 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; }