468 lines
12 KiB
C
468 lines
12 KiB
C
#include "cgdoom.h"
|
|
#include "cgdoom-kbd.h"
|
|
#include "platform.h"
|
|
|
|
#include "doomdef.h"
|
|
#include "i_system.h"
|
|
|
|
#include <SDL2/SDL.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <glob.h>
|
|
|
|
SDL_Window *window = NULL;
|
|
SDL_Surface *VRAM_RGB888 = NULL;
|
|
|
|
char _PRAM0[160*1024];
|
|
|
|
/* Rendering system emulation. */
|
|
|
|
uint16_t _VRAM[WIDTH * HEIGHT];
|
|
uint16_t _VRAM2[WIDTH * HEIGHT];
|
|
|
|
static void QuitSDL(void)
|
|
{
|
|
if(window)
|
|
SDL_DestroyWindow(window);
|
|
if(VRAM_RGB888)
|
|
SDL_FreeSurface(VRAM_RGB888);
|
|
SDL_Quit();
|
|
}
|
|
|
|
static void InitSDL(void)
|
|
{
|
|
if(SDL_Init(SDL_INIT_VIDEO) < 0) {
|
|
printf("InitSDL: %s\n", SDL_GetError());
|
|
return;
|
|
}
|
|
|
|
window = SDL_CreateWindow("CGDoom",
|
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
|
WIDTH, HEIGHT, 0);
|
|
|
|
VRAM_RGB888 = SDL_CreateRGBSurface(0, WIDTH, HEIGHT, 24, 0, 0, 0, 0);
|
|
if(!VRAM_RGB888) {
|
|
printf("InitSDL: Cannot create intermediate surface: %s\n",
|
|
SDL_GetError());
|
|
QuitSDL();
|
|
return;
|
|
}
|
|
|
|
atexit(QuitSDL);
|
|
}
|
|
|
|
uint16_t *GetVRAMAddress(void)
|
|
{
|
|
return _VRAM;
|
|
}
|
|
|
|
uint16_t *GetSecondaryVRAMAddress(void)
|
|
{
|
|
return _VRAM2;
|
|
}
|
|
|
|
void Bdisp_PutDisp_DD(void)
|
|
{
|
|
if(!window)
|
|
InitSDL();
|
|
if(!window || !VRAM_RGB888)
|
|
return;
|
|
|
|
/* Copy from VRAM to RGB888 and let SDL handle the conversion to the
|
|
window's native format */
|
|
for(int y = 0; y < HEIGHT; y++)
|
|
for(int x = 0; x < WIDTH; x++)
|
|
{
|
|
int rgb565 = VRAM[WIDTH * y + x];
|
|
int r = (rgb565 >> 8) & 0xf8;
|
|
int g = (rgb565 >> 3) & 0xfc;
|
|
int b = (rgb565 << 3) & 0xf8;
|
|
|
|
Uint8 *rgb888 = VRAM_RGB888->pixels + y * VRAM_RGB888->pitch + 3 * x;
|
|
rgb888[0] = b;
|
|
rgb888[1] = g;
|
|
rgb888[2] = r;
|
|
}
|
|
|
|
SDL_BlitSurface(VRAM_RGB888, NULL, SDL_GetWindowSurface(window), NULL);
|
|
SDL_UpdateWindowSurface(window);
|
|
}
|
|
|
|
int Bdisp_FrameAndColor(int p1, int p2)
|
|
{
|
|
(void)p1;
|
|
(void)p2;
|
|
return 0;
|
|
}
|
|
|
|
void Bdisp_AllClr_VRAM(void)
|
|
{
|
|
memset(_VRAM, 0xff, WIDTH * HEIGHT * 2);
|
|
}
|
|
|
|
/* Keyboard system emulation. */
|
|
|
|
static int MatrixCodeToPCKey(int key)
|
|
{
|
|
switch(key)
|
|
{
|
|
case KEYCODE_F1: return SDL_SCANCODE_F1;
|
|
case KEYCODE_F2: return SDL_SCANCODE_F2;
|
|
case KEYCODE_F3: return SDL_SCANCODE_F3;
|
|
case KEYCODE_F4: return SDL_SCANCODE_F4;
|
|
case KEYCODE_F5: return SDL_SCANCODE_F5;
|
|
case KEYCODE_F6: return SDL_SCANCODE_F6;
|
|
|
|
case KEYCODE_UP: return SDL_SCANCODE_UP;
|
|
case KEYCODE_RIGHT: return SDL_SCANCODE_RIGHT;
|
|
case KEYCODE_DOWN: return SDL_SCANCODE_DOWN;
|
|
case KEYCODE_LEFT: return SDL_SCANCODE_LEFT;
|
|
|
|
case KEYCODE_SHIFT: return SDL_SCANCODE_Q;
|
|
case KEYCODE_OPTN: return SDL_SCANCODE_W;
|
|
case KEYCODE_VARS: return SDL_SCANCODE_E;
|
|
case KEYCODE_MENU: return SDL_SCANCODE_R;
|
|
|
|
case KEYCODE_ALPHA: return SDL_SCANCODE_A;
|
|
case KEYCODE_SQUARE: return SDL_SCANCODE_S;
|
|
case KEYCODE_POWER: return SDL_SCANCODE_D;
|
|
case KEYCODE_EXIT: return SDL_SCANCODE_F;
|
|
|
|
case KEYCODE_XOT: return SDL_SCANCODE_T;
|
|
case KEYCODE_LOG: return SDL_SCANCODE_Y;
|
|
case KEYCODE_LN: return SDL_SCANCODE_U;
|
|
case KEYCODE_SIN: return SDL_SCANCODE_I;
|
|
case KEYCODE_COS: return SDL_SCANCODE_O;
|
|
case KEYCODE_TAN: return SDL_SCANCODE_P;
|
|
|
|
case KEYCODE_FRAC: return SDL_SCANCODE_G;
|
|
case KEYCODE_FD: return SDL_SCANCODE_H;
|
|
case KEYCODE_LEFTP: return SDL_SCANCODE_J;
|
|
case KEYCODE_RIGHTP: return SDL_SCANCODE_K;
|
|
case KEYCODE_COMMA: return SDL_SCANCODE_L;
|
|
case KEYCODE_ARROW: return SDL_SCANCODE_SEMICOLON;
|
|
|
|
case KEYCODE_1: return SDL_SCANCODE_1;
|
|
case KEYCODE_2: return SDL_SCANCODE_2;
|
|
case KEYCODE_3: return SDL_SCANCODE_3;
|
|
case KEYCODE_4: return SDL_SCANCODE_4;
|
|
case KEYCODE_5: return SDL_SCANCODE_5;
|
|
case KEYCODE_6: return SDL_SCANCODE_6;
|
|
case KEYCODE_7: return SDL_SCANCODE_7;
|
|
case KEYCODE_8: return SDL_SCANCODE_8;
|
|
case KEYCODE_9: return SDL_SCANCODE_9;
|
|
case KEYCODE_0: return SDL_SCANCODE_0;
|
|
|
|
case KEYCODE_DEL: return SDL_SCANCODE_BACKSPACE;
|
|
case KEYCODE_ACON: return SDL_SCANCODE_ESCAPE;
|
|
|
|
case KEYCODE_MUL: return SDL_SCANCODE_LEFTBRACKET;
|
|
case KEYCODE_DIV: return SDL_SCANCODE_RIGHTBRACKET;
|
|
|
|
case KEYCODE_PLUS: return SDL_SCANCODE_MINUS;
|
|
case KEYCODE_MINUS: return SDL_SCANCODE_EQUALS;
|
|
|
|
case KEYCODE_DOT: return -1;
|
|
case KEYCODE_EXP: return -1;
|
|
case KEYCODE_NEG: return -1;
|
|
|
|
case KEYCODE_EXE: return SDL_SCANCODE_RETURN;
|
|
|
|
default: return -1;
|
|
}
|
|
}
|
|
|
|
void CGD_ScanKeyboard(CGDoom_KeyboardState state)
|
|
{
|
|
SDL_Event e;
|
|
while(SDL_PollEvent(&e)) {}
|
|
|
|
int length;
|
|
const Uint8 *sdl_state = SDL_GetKeyboardState(&length);
|
|
|
|
for(int row = 0; row < 12; row++) {
|
|
for(int col = 0; col < 8; col++) {
|
|
int matrix_code = (row << 4) | col;
|
|
int index = row ^ 1;
|
|
int pattern = (0x80 >> col);
|
|
|
|
int scancode = MatrixCodeToPCKey(matrix_code);
|
|
state[index] &= ~pattern;
|
|
if(scancode >= 0 && scancode < length && sdl_state[scancode])
|
|
state[index] |= pattern;
|
|
}
|
|
}
|
|
}
|
|
|
|
int PRGM_GetKey(void)
|
|
{
|
|
// TODO: PRGM_GetKey()
|
|
// We don't really need it here, it's only used to delay error messages
|
|
// on-calc, and these are printed on terminal
|
|
return 0;
|
|
}
|
|
|
|
void GetKey(int *key)
|
|
{
|
|
SDL_Event e;
|
|
int valid=0, sym;
|
|
|
|
while(!valid)
|
|
{
|
|
SDL_WaitEvent(&e);
|
|
if(e.type == SDL_QUIT ||
|
|
(e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE))
|
|
exit(0);
|
|
|
|
if(e.type != SDL_KEYDOWN) continue;
|
|
|
|
valid = 1;
|
|
switch((sym = e.key.keysym.sym))
|
|
{
|
|
case SDLK_UP: *key = KEY_CTRL_UP; break;
|
|
case SDLK_RIGHT: *key = KEY_CTRL_RIGHT; break;
|
|
case SDLK_DOWN: *key = KEY_CTRL_DOWN; break;
|
|
case SDLK_LEFT: *key = KEY_CTRL_LEFT; break;
|
|
case SDLK_RETURN: *key = KEY_CTRL_EXE; break;
|
|
case SDLK_F1: *key = KEY_CTRL_F1; break;
|
|
case SDLK_F2: *key = KEY_CTRL_F2; break;
|
|
case SDLK_F3: *key = KEY_CTRL_F3; break;
|
|
case SDLK_F4: *key = KEY_CTRL_F4; break;
|
|
case SDLK_F5: *key = KEY_CTRL_F5; break;
|
|
case SDLK_F6: *key = KEY_CTRL_F6; break;
|
|
|
|
default:
|
|
if(sym >= '0' && sym <= '9') *key = sym;
|
|
else valid = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Returns the value of a 128 Hz counter */
|
|
int RTC_GetTicks(void)
|
|
{
|
|
struct timespec tp;
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
|
int msec = (tp.tv_sec * 1000) + (tp.tv_nsec / 1000000);
|
|
return (msec * 128) / 1000;
|
|
}
|
|
|
|
void assert(int iLine,const char *pszFilename,const char *pszAassert)
|
|
{
|
|
static int iDoAssert = 1;
|
|
printf("assert %s,%i: %s\n",pszFilename,iLine,pszAassert);
|
|
if(iDoAssert)
|
|
{
|
|
iDoAssert = 0;
|
|
}
|
|
}
|
|
|
|
//---
|
|
// Filesystem API emulation
|
|
//---
|
|
|
|
#define FILE_TABLE_MAX 16
|
|
static FILE *file_table[FILE_TABLE_MAX] = { NULL };
|
|
static struct {
|
|
int pos;
|
|
glob_t glob;
|
|
} search_table[FILE_TABLE_MAX];
|
|
|
|
void Bfile_NameToStr_ncpy(char *dest, const uint16_t *source, size_t n)
|
|
{
|
|
size_t i = 0;
|
|
|
|
if(!memcmp(source, u"\\\\fls0\\", 14))
|
|
source += 7;
|
|
|
|
while(i < n && source[i] != 0x0000 && source[i] != 0xffff) {
|
|
dest[i] = source[i];
|
|
i++;
|
|
}
|
|
for(size_t j = i; j < n; j++)
|
|
dest[j] = source[i];
|
|
}
|
|
|
|
void Bfile_StrToName_ncpy(uint16_t *dest, const char *source, size_t n)
|
|
{
|
|
if(!strncmp(source, "\\\\fls0\\", 7)) {
|
|
memcpy(dest, u"\\\\fls0\\", 14);
|
|
dest += 7;
|
|
}
|
|
|
|
size_t i = 0;
|
|
while(i < n && source[i]) {
|
|
dest[i] = source[i];
|
|
i++;
|
|
}
|
|
while(i < n)
|
|
dest[i++] = 0;
|
|
}
|
|
|
|
int Bfile_DeleteEntry(const uint16_t *filename_u16)
|
|
{
|
|
char filename_u8[1024];
|
|
Bfile_NameToStr_ncpy(filename_u8, filename_u16, 1024);
|
|
printf("Deleting %s (virtually)\n", filename_u8);
|
|
return 0;
|
|
}
|
|
|
|
int Bfile_CreateEntry_OS(const uint16_t *filename_u16, int mode, size_t *size)
|
|
{
|
|
char filename_u8[1024];
|
|
Bfile_NameToStr_ncpy(filename_u8, filename_u16, 1024);
|
|
|
|
if(mode == BFILE_CREATEMODE_FILE) {
|
|
printf("Creating %s with size %zu (virtually)\n", filename_u8, *size);
|
|
}
|
|
else if(mode == BFILE_CREATEMODE_FOLDER) {
|
|
printf("Cannot create folder %s: Not Implemented\n", filename_u8);
|
|
return -1;
|
|
}
|
|
else {
|
|
printf("Bfile_CreateEntry_OS(): Invalid mode %d\n", mode);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Bfile_OpenFile_OS(const uint16_t *filename_u16, int mode, int zero)
|
|
{
|
|
(void)zero;
|
|
char filename_u8[1024];
|
|
Bfile_NameToStr_ncpy(filename_u8, filename_u16, 1024);
|
|
|
|
char const *bits;
|
|
if(mode == BFILE_READ || mode == BFILE_READ_SHARE)
|
|
bits = "rb";
|
|
else if(mode == BFILE_WRITE)
|
|
bits = "wb";
|
|
else if(mode == BFILE_READWRITE || mode == BFILE_READWRITE_SHARE)
|
|
bits = "w+b";
|
|
else {
|
|
printf("Bfile_OpenFile_OS(): invalid mode %d for %s\n", mode, filename_u8);
|
|
return -1;
|
|
}
|
|
|
|
FILE *fp = fopen(filename_u8, bits);
|
|
if(!fp) {
|
|
printf("Bfile_OpenFile_OS(): cannot open %s: %m\n", filename_u8);
|
|
return -1;
|
|
}
|
|
|
|
/* Find open slot in file table */
|
|
int slot = 0;
|
|
while(slot < FILE_TABLE_MAX && file_table[slot] != NULL)
|
|
slot++;
|
|
if(slot >= FILE_TABLE_MAX) {
|
|
printf("Bfile_OpenFile_OS(): cannot open %s, table is full\n",filename_u8);
|
|
fclose(fp);
|
|
return -1;
|
|
}
|
|
|
|
printf("Bfile_OpenFile_OS(): opened %s\n", filename_u8);
|
|
file_table[slot] = fp;
|
|
return slot;
|
|
}
|
|
|
|
int Bfile_GetFileSize_OS(int fd)
|
|
{
|
|
FILE *fp = file_table[fd];
|
|
long pos = ftell(fp);
|
|
fseek(fp, 0, SEEK_END);
|
|
long size = ftell(fp);
|
|
fseek(fp, pos, SEEK_SET);
|
|
return size;
|
|
}
|
|
|
|
int Bfile_SeekFile_OS(int fd, int pos)
|
|
{
|
|
fseek(file_table[fd], pos, SEEK_SET);
|
|
return 0;
|
|
}
|
|
|
|
int Bfile_ReadFile_OS(int fd, void *buf, int size, int readpos)
|
|
{
|
|
if(readpos != -1)
|
|
Bfile_SeekFile_OS(fd, readpos);
|
|
|
|
return fread(buf, 1, size, file_table[fd]);
|
|
}
|
|
|
|
int Bfile_WriteFile_OS(int fd, const void *buf, int size)
|
|
{
|
|
return fwrite(buf, 1, size, file_table[fd]);
|
|
}
|
|
|
|
int Bfile_CloseFile_OS(int fd)
|
|
{
|
|
fclose(file_table[fd]);
|
|
file_table[fd] = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int Bfile_FindFirst(const uint16_t *pattern_u16, int *fd, uint16_t *found,
|
|
void *fileinfo)
|
|
{
|
|
char pattern_u8[1024];
|
|
Bfile_NameToStr_ncpy(pattern_u8, pattern_u16, 1024);
|
|
|
|
int slot = 0;
|
|
while(slot < FILE_TABLE_MAX && search_table[slot].pos != 0)
|
|
slot++;
|
|
if(slot >= FILE_TABLE_MAX) {
|
|
printf("Bfile_FindFirst(): cannot search %s, table is full\n", pattern_u8);
|
|
*fd = -1;
|
|
return -16;
|
|
}
|
|
|
|
int rc = glob(pattern_u8, 0, NULL, &search_table[slot].glob);
|
|
|
|
printf("Bfile_FindFirst(): Searching %s: %zu results\n", pattern_u8,
|
|
(rc == GLOB_NOMATCH) ? 0 : search_table[slot].glob.gl_pathc);
|
|
|
|
if(rc == GLOB_NOMATCH)
|
|
return -16;
|
|
|
|
*fd = slot;
|
|
return Bfile_FindNext(slot, found, fileinfo);
|
|
}
|
|
|
|
int Bfile_FindNext(int fd, uint16_t *found, void *fileinfo0)
|
|
{
|
|
int *pos = &search_table[fd].pos;
|
|
glob_t *glob = &search_table[fd].glob;
|
|
|
|
if(*pos >= glob->gl_pathc)
|
|
return -16;
|
|
|
|
const char *name = glob->gl_pathv[*pos];
|
|
Bfile_StrToName_ncpy(found, name, strlen(name)+1);
|
|
(*pos)++;
|
|
|
|
Bfile_FileInfo *fileinfo = fileinfo0;
|
|
// TODO: More resonsable Bfile_FileInfo entries?
|
|
memset(fileinfo, 0, sizeof *fileinfo);
|
|
|
|
FILE *fp = fopen(name, "rb");
|
|
if(fp) {
|
|
fseek(fp, 0, SEEK_END);
|
|
fileinfo->fsize = ftell(fp);
|
|
fclose(fp);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int Bfile_FindClose(int fd)
|
|
{
|
|
if(fd < 0 || fd >= FILE_TABLE_MAX)
|
|
return -1;
|
|
|
|
search_table[fd].pos = 0;
|
|
globfree(&search_table[fd].glob);
|
|
return 0;
|
|
}
|