#include "quakedef.h" #ifdef MULTITHREAD #include #endif #include #include #include "ftejslib.h" #ifndef isDedicated qboolean isDedicated; #endif quakeparms_t parms; void Sys_Error (const char *error, ...) { va_list argptr; char string[1024]; va_start (argptr,error); vsnprintf (string, sizeof (string), error, argptr); va_end (argptr); COM_WorkerAbort(string); Sys_Printf("Error: %s\n", string); Con_Print ("Quake Error: "); Con_Print (string); Con_Print ("\n"); Host_Shutdown (); emscriptenfte_alert(string); emscriptenfte_abortmainloop("Sys_Error", true); exit (1); } void Sys_RecentServer(char *command, char *target, char *title, char *desc) { } qboolean Sys_RandomBytes(qbyte *string, int len) { return false; } //print into stdout void Sys_Printf (char *fmt, ...) { va_list argptr; char text[2048]; conchar_t ctext[2048], *e, *c; unsigned int len = 0; unsigned int w, codeflags; va_start (argptr,fmt); vsnprintf (text, sizeof(text), fmt, argptr); va_end (argptr); //make sense of any markup e = COM_ParseFunString(CON_WHITEMASK, text, ctext, sizeof(ctext), false); //convert to utf-8 for the js to make sense of for (c = ctext; c < e; ) { c = Font_Decode(c, &codeflags, &w); if (codeflags & CON_HIDDEN) continue; //dequake it as required, so its only codepoints the browser will understand. should probably deal with linefeeds specially. if (w >= 0xe000 && w < 0xe100) { //quake-encoded mess if ((w & 0x7f) >= 0x20) w &= 0x7f; //regular (discoloured) ascii else if (w & 0x80) { //c1 glyphs static char tab[32] = "---#@.@@@@ # >.." "[]0123456789.---"; w = tab[w&31]; } else { //c0 glyphs static char tab[32] = ".####.#### # >.." "[]0123456789.---"; w = tab[w&31]; } } else if (w < ' ' && w != '\t' && w != '\r' && w != '\n') w = '?'; //c0 chars are awkward len += utf8_encode(text+len, w, sizeof(text)-1-len); } text[len] = 0; //now throw it at the browser's console.log. emscriptenfte_print(text); } #if 1 //use Performance.now() instead of Date.now() - its likely to both provide higher precision and no NTP/etc issues. double Sys_DoubleTime (void) { double t = emscriptenfte_uptime_ms()/1000; //we need it as seconds... static double old = -99999999; if (t < old) t = old; //don't let t step backwards, ever. this shouldn't happen, but some CPUs don't keep their high-precision timers synced properly. return old=t; } unsigned int Sys_Milliseconds(void) { return Sys_DoubleTime() * (uint64_t)1000; } #else unsigned int Sys_Milliseconds(void) { static int first = true; static unsigned long oldtime = 0, curtime = 0; unsigned long newtime; newtime = emscriptenfte_ticks_ms(); //return Date.now() if (first) { first = false; oldtime = newtime; } if (newtime < oldtime) Con_Printf("Sys_Milliseconds stepped backwards!\n"); else curtime += newtime - oldtime; oldtime = newtime; return curtime; } //return the current time, in the form of a double double Sys_DoubleTime (void) { return Sys_Milliseconds() / 1000.0; } #endif //create a directory void Sys_mkdir (const char *path) { } qboolean Sys_rmdir (const char *path) { return true; } //unlink a file qboolean Sys_remove (const char *path) { emscriptenfte_buf_delete(path); return true; } qboolean Sys_Rename (const char *oldfname, const char *newfname) { return emscriptenfte_buf_rename(oldfname, newfname); } qboolean Sys_GetFreeDiskSpace(const char *path, quint64_t *freespace) { //not implemented. we could try querying local storage quotas, but our filesystem is otherwise purely ram so doesn't have much of a limit in 64bit browsers. hurrah for swap space. return false; } //someone used the 'quit' command #include "glquake.h" void Sys_Quit (void) { if (host_initialized) { qglClearColor(0,0,0,1); qglClear(GL_COLOR_BUFFER_BIT); Draw_FunString (0, 0, "Reload the page to restart"); Host_Shutdown(); } exit (0); } struct enumctx_s { char name[MAX_OSPATH]; const char *gpath; size_t gpathlen; const char *match; int (*callback)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *); void *ctx; searchpathfuncs_t *spath; int ret; }; static void Sys_EnumeratedFile(void *vctx, size_t fsize) { //called for each enumerated file. //we don't need the whole EnumerateFiles2 thing as our filesystem is flat, so */* isn't an issue for us (we don't expect a lot of different 'files' if only because they're a pain to download). struct enumctx_s *ctx = vctx; if (!ctx->ret) return; //we're meant to stop when if it returns false... if (!strncmp(ctx->name, ctx->gpath, ctx->gpathlen)) //ignore any gamedir prefix if (wildcmp(ctx->match, ctx->name+ctx->gpathlen)) //match it within the searched gamedir... ctx->ret = ctx->callback(ctx->name+ctx->gpathlen, fsize, 0, ctx->ctx, ctx->spath); //call the callback } int Sys_EnumerateFiles (const char *gpath, const char *match, int (*func)(const char *, qofs_t, time_t mtime, void *, searchpathfuncs_t *), void *parm, searchpathfuncs_t *spath) { struct enumctx_s ctx; char tmp[MAX_OSPATH]; if (!gpath) gpath = ""; ctx.gpathlen = strlen(gpath); if (ctx.gpathlen && gpath[ctx.gpathlen-1] != '/') { //make sure gpath is /-terminated. if (ctx.gpathlen >= sizeof(tmp)-1) return false; //just no... Q_strncpyz(tmp, gpath, sizeof(tmp)); gpath = tmp; tmp[ctx.gpathlen++] = '/'; } ctx.gpath = gpath; ctx.match = match; ctx.callback = func; ctx.ctx = parm; ctx.spath = spath; ctx.ret = true; emscritenfte_buf_enumerate(Sys_EnumeratedFile, &ctx, sizeof(ctx.name)); return ctx.ret; } //blink window if possible (it's not) void Sys_ServerActivity(void) { } void Sys_CloseLibrary(dllhandle_t *lib) { } dllhandle_t *Sys_LoadLibrary(const char *name, dllfunction_t *funcs) { return NULL; } void *Sys_GetAddressForName(dllhandle_t *module, const char *exportname) { return NULL; } void Sys_BrowserRedirect_f(void) { emscriptenfte_window_location(Cmd_Argv(1)); } char *Sys_URIScheme_NeedsRegistering(void) { //just disables the prompts that we can't honour anyway. return NULL; } void Sys_Init(void) { extern cvar_t vid_width, vid_height, vid_fullscreen; //vid_fullscreen takes effect only on mouse clicks, any suggestion to do a vid_restart is pointless. vid_fullscreen.flags &= ~CVAR_VIDEOLATCH; //these are not really supported. so silence any spam that suggests we do something about something not even supported. vid_width.flags &= ~CVAR_VIDEOLATCH; vid_height.flags &= ~CVAR_VIDEOLATCH; Cmd_AddCommand("sys_browserredirect", Sys_BrowserRedirect_f); } void Sys_Shutdown(void) { } int VARGS Sys_DebugLog(char *file, char *fmt, ...) { return 0; }; qboolean Sys_InitTerminal(void) { return true; } char *Sys_ConsoleInput(void) { return NULL; } void Sys_CloseTerminal (void) { } int Sys_MainLoop(double newtime) { extern cvar_t vid_vsync; static double oldtime; double time; if (newtime) newtime /= 1000; //use RAF's timing for slightly greater precision. else newtime = Sys_DoubleTime (); //otherwise fall back on internally consistent timing... if (newtime < oldtime) newtime = oldtime; //don't let ourselves go backwards... if (!oldtime) oldtime = newtime; time = newtime - oldtime; if (!host_initialized) { Sys_Printf ("Host_Init\n"); Host_Init (&parms); return 1; } oldtime = newtime; Host_Frame (time); return vid_vsync.ival; } int QDECL main(int argc, char **argv) { memset(&parms, 0, sizeof(parms)); parms.basedir = ""; parms.argc = argc; parms.argv = (const char**)argv; #ifdef CONFIG_MANIFEST_TEXT parms.manifest = CONFIG_MANIFEST_TEXT; #endif COM_InitArgv (parms.argc, parms.argv); TL_InitLanguages(""); emscriptenfte_setupmainloop(Sys_MainLoop); return 0; } qboolean Sys_GetDesktopParameters(int *width, int *height, int *bpp, int *refreshrate) { return false; } #ifdef WEBCLIENT qboolean Sys_RunInstaller(void) { //not implemented return false; } #endif /*static char *clipboard_buffer; void Sys_Clipboard_PasteText(clipboardtype_t cbt, void (*callback)(void *cb, const char *utf8), void *ctx) { callback(ctx, clipboard_buffer); } void Sys_SaveClipboard(clipboardtype_t cbt, const char *text) { free(clipboard_buffer); clipboard_buffer = strdup(text); }*/ #ifdef MULTITHREAD /* Thread creation calls */ void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize) { // SDL threads do not support setting thread stack size return (void *)SDL_CreateThread(func, args); } void Sys_WaitOnThread(void *thread) { SDL_WaitThread((SDL_Thread *)thread, NULL); } /* Mutex calls */ // SDL mutexes don't have try-locks for mutexes in the spec so we stick with 1-value semaphores void *Sys_CreateMutex(void) { return (void *)SDL_CreateSemaphore(1); } qboolean Sys_TryLockMutex(void *mutex) { return !SDL_SemTryWait(mutex); } qboolean Sys_LockMutex(void *mutex) { return !SDL_SemWait(mutex); } qboolean Sys_UnlockMutex(void *mutex) { return !SDL_SemPost(mutex); } void Sys_DestroyMutex(void *mutex) { SDL_DestroySemaphore(mutex); } /* Conditional wait calls */ typedef struct condvar_s { SDL_mutex *mutex; SDL_cond *cond; } condvar_t; void *Sys_CreateConditional(void) { condvar_t *condv; SDL_mutex *mutex; SDL_cond *cond; condv = (condvar_t *)malloc(sizeof(condvar_t)); if (!condv) return NULL; mutex = SDL_CreateMutex(); cond = SDL_CreateCond(); if (mutex) { if (cond) { condv->cond = cond; condv->mutex = mutex; return (void *)condv; } else SDL_DestroyMutex(mutex); } free(condv); return NULL; } qboolean Sys_LockConditional(void *condv) { return !SDL_mutexP(((condvar_t *)condv)->mutex); } qboolean Sys_UnlockConditional(void *condv) { return !SDL_mutexV(((condvar_t *)condv)->mutex); } qboolean Sys_ConditionWait(void *condv) { return !SDL_CondWait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex); } qboolean Sys_ConditionSignal(void *condv) { return !SDL_CondSignal(((condvar_t *)condv)->cond); } qboolean Sys_ConditionBroadcast(void *condv) { return !SDL_CondBroadcast(((condvar_t *)condv)->cond); } void Sys_DestroyConditional(void *condv) { condvar_t *cv = (condvar_t *)condv; SDL_DestroyCond(cv->cond); SDL_DestroyMutex(cv->mutex); free(cv); } #endif void Sys_Sleep (double seconds) { //SDL_Delay(seconds * 1000); }