363 lines
11 KiB
C
363 lines
11 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 <stdbool.h>
|
|
#include <string.h>
|
|
#include <intrin.h>
|
|
#include <syscall.h>
|
|
|
|
#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;
|
|
}
|