// 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 #define DOOM_IMPLEMENTATION #include "PureDOOM.h" int _fltused; // --------------------------------------------------------------------------- // doom_print_fn // --------------------------------------------------------------------------- static void doom_print_wrapper(const char* msg) { SysWriteConsole(msg, strlen(msg)); } // --------------------------------------------------------------------------- // doom_malloc_fn / doom_free_fn // Prepend size tracking metadata to satisfy SysFree's size requirement. // --------------------------------------------------------------------------- static void* doom_malloc_wrapper(int size) { size_t total_size = size + sizeof(size_t); void* p = SysAlloc(total_size); if (!p) SysExitProcess(1); // Store total allocated size at the start of the block *(size_t*)p = total_size; // Return pointer past the size prefix return (void*)((size_t*)p + 1); } static void doom_free_wrapper(void* ptr) { if (!ptr) return; size_t* real_p = (size_t*)ptr - 1; size_t total_size = *real_p; SysFree(real_p, total_size); } // --------------------------------------------------------------------------- // File I/O wrappers // --------------------------------------------------------------------------- static void* doom_open_wrapper(const char* path, const char* mode) { uint32_t handle = 0; uint32_t result; if (mode[0] == 'w') { result = SysCreateFile(path, &handle); } else { result = SysOpenFile(path, &handle); } return (result == 0) ? (void*)(uintptr_t)handle : 0; } static void doom_close_wrapper(void* handle) { SysCloseHandle((uint32_t)(uintptr_t)handle); } static int doom_read_wrapper(void* handle, void* buf, int count) { size_t read = 0; SysReadFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &read); return (int)read; } static int doom_write_wrapper(void* handle, const void* buf, int count) { size_t written = 0; SysWriteFile((uint32_t)(uintptr_t)handle, buf, (size_t)count, &written); return (int)written; } static int doom_seek_wrapper(void* handle, int offset, doom_seek_t origin) { uint32_t h = (uint32_t)(uintptr_t)handle; uint64_t absolute_offset = 0; switch (origin) { case DOOM_SEEK_SET: absolute_offset = (uint64_t)offset; break; case DOOM_SEEK_CUR: absolute_offset = SysGetFilePos(h) + offset; break; case DOOM_SEEK_END: absolute_offset = SysGetFileSize(h) + offset; break; default: absolute_offset = (uint64_t)offset; break; } SysSeekFile(h, absolute_offset); return 0; } static int doom_tell_wrapper(void* handle) { return (int)SysGetFilePos((uint32_t)(uintptr_t)handle); } static int doom_eof_wrapper(void* handle) { uint32_t h = (uint32_t)(uintptr_t)handle; uint64_t size = SysGetFileSize(h); uint64_t pos = SysGetFilePos(h); return (pos >= 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 = SysGetCommandLine(); SysWriteConsole("doom: starting\n", 15); // 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); SysWriteConsole("doom: entering game loop\n", 25); // Core execution loop while (1) { poll_input(); doom_update(); render_frame((const uint8_t*)doom_get_framebuffer(4 /* RGBA */)); } SysExitProcess(0); return 0; }