#include "quakedef.h" #include #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; } static qboolean sys_supportsansi; static char ansiremap[8] = {'0', '4', '2', '6', '1', '5', '3', '7'}; static size_t ApplyColour(char *out, size_t outsize, unsigned int chrflags) { char *s, *e; static int oldflags = CON_WHITEMASK; int bg, fg; if (!sys_supportsansi) return 0; if (oldflags == chrflags) return 0; s = out; e = out+outsize; oldflags = chrflags; *out++='\x1b'; *out++='['; *out++='0'; *out++=';'; if (chrflags & CON_BLINKTEXT) { //set blink flag *out++='5'; *out++=';'; } bg = (chrflags & CON_BGMASK) >> CON_BGSHIFT; fg = (chrflags & CON_FGMASK) >> CON_FGSHIFT; // don't handle intensive bit for background // as terminals differ too much in displaying \e[1;7;3?m bg &= 0x7; if (chrflags & CON_NONCLEARBG) { if (fg & 0x8) // intensive bit set for foreground { // set bold/intensity ansi flag *out++='1'; *out++=';'; fg &= 0x7; // strip intensive bit } // set foreground and background colors *out++='3'; *out++=ansiremap[fg]; *out++=';'; *out++='4'; *out++=ansiremap[bg]; *out++='m'; } else { switch(fg) { //to get around wierd defaults (like a white background) we have these special hacks for colours 0 and 7 case COLOR_BLACK: // set inverse *out++='7'; *out++='m'; break; case COLOR_GREY: *out++='1'; *out++=';'; *out++='3'; *out++='0'; *out++='m'; break; case COLOR_WHITE: // set nothing else *out++='m'; break; default: if (fg & 0x8) // intensive bit set for foreground { // set bold/intensity ansi flag *out++='1'; *out++=';'; fg &= 0x7; // strip intensive bit } *out++='3'; *out++=ansiremap[fg]; *out++='m'; break; } } return out-s; } //print into stdout void Sys_Printf (char *fmt, ...) { va_list argptr; char text[65536]; conchar_t ctext[countof(text)], *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; if ((codeflags&CON_RICHFORECOLOUR) || (w == '\n' && (codeflags&CON_NONCLEARBG))) codeflags = CON_WHITEMASK; //make sure we don't get annoying backgrounds on other lines. len+=ApplyColour(text+len,sizeof(text)-1-len, codeflags); //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); } len+=ApplyColour(text+len,sizeof(text)-1-len, CON_WHITEMASK); //force it back to white at the end of the print... just in case 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. we don't do dirs. 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. *freespace = 0; //just in case. 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)); } void Sys_OpenFile_f(void) { emscriptenfte_openfile(); } static void Sys_Register_File_Associations_f(void) { //we should be able to register 'web+foo://' schemes here. we can't skip the web+ part though, which is a shame. const char *s; char scheme[MAX_OSPATH]; const char *schemes = fs_manifest->schemes; for (s = schemes; (s=COM_ParseOut(s,scheme,sizeof(scheme)));) { EM_ASM({ navigator.registerProtocolHandler( UTF8ToString($0), document.location.origin+document.location.pathname+"?%s"+document.location.hash, UTF8ToString($1)); }, va("%s%s", strncmp(scheme,"web+",4)?"web+":"", scheme), fs_manifest->formalname?fs_manifest->formalname:fs_manifest->installation); } } char *Sys_URIScheme_NeedsRegistering(void) { //we have no way to query if we're registered or not. cl_main will default to bypassing this. 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_AddCommandD("sys_browserredirect", Sys_BrowserRedirect_f, "Navigates the browser to a different url. For sites using quake maps as a more interesting sitemap."); if (EM_ASM_INT(return window.showOpenFilePicker!=undefined;)) //doesn't work in firefox. Cmd_AddCommandD("sys_openfile", Sys_OpenFile_f, "Opens a file picker"); //opens file picker if (EM_ASM_INT(return Module['mayregisterscemes'] != false;)) //needs to be able to pass args via the url. don't bother adding the command if it'll fail. hurrah for checkcmd Cmd_AddCommandD("sys_register_file_associations", Sys_Register_File_Associations_f, "Register this page as the default handler for web+scheme handlers.\n"); //can't really do feature detection for this... either we spit out unreadable text or we don't... sys_supportsansi = EM_ASM_INT(return navigator.userAgent.indexOf("FireFox")!=-1;); } void Sys_Shutdown(void) { emscriptenfte_setupmainloop(NULL); } 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 ("Starting "FULLENGINENAME"\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 #include //FIXME: swap this out for sys_linux_threads.c (our pthreads code) /* 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); }